vue实现自定义drawer功能

51 阅读2分钟

具体功能

drawer有个把手,点击有右侧滑动出来,再点击右侧滑入进去。

图片展示

image.png

功能理解

主要使用css实现,transform和transition。

感觉很牛掰的功能

实现点击其他地方(不是这个drawer)要折叠进去,我原来是使用的大遮罩层实现该功能,外面套一个大的div。

想法
  • 想用其他的方法实现,精简代码。
  • outline实现遮罩功能,点击outline缩回去。但是outlie和border不一样,点击事件不能触发。
  • image.png
  • 使用其他方法,全局监听click,点击的dom节点不是drawer的元素,就关闭
  • 遇到问题,点击的dom节点不是drawer元素,但是点击的是drawer中的子元素,应该不关闭
  • 最终使用ai解决!
代码
<style lang="scss" scoped>
.knowledge-drawer {
    position: fixed;
    top: 90px;
    right: 0;
    display: flex;
    align-items: center;
    transition: all 0.3s ease-in-out;
    transform: translateX(526px);

    &.knowledge-drawer-show {
        transform: translateX(0);
        .knowledge-drawer-handle > .knowledge-drawer-icon {
            transform: rotate(180deg);
        }
    }
    .knowledge-drawer-content {
        width: 526px;
        border-radius: 40px 0 0 40px;
        background: linear-gradient(180deg, #fff 0%, #fff 16.96%);
        box-shadow: 0 0 20px 0 #016cff33;
        height: calc(100vh - 150px);
        padding: 30px 31px;
        box-sizing: border-box;
        .eng-txt {
            margin-left: 6px;
            color: #d6dee9;
            font-size: 20px;
            font-weight: 700;
            text-transform: uppercase;
        }
        .knowledge-item-box {
            border-radius: 20px;
            background: #fff;
            border: 1px solid #daedff;
            margin-top: 20px;
            border-top-width: 6px;
            padding: 16px 24px;
        }

        .knowledge-drawer-content-scroll {
            height: calc(100% - 50px);
            width: 100%;
            padding-top: 5px;
            overflow-y: auto;
            &::-webkit-scrollbar {
                width: 0;
            }
        }
    }
    .knowledge-drawer-handle {
        line-height: 52px;
        padding: 18px 0;
        background-color: #f7954e;
        border-radius: 20px 0 0 20px;
        color: #ffffff;
        font-size: 20px;
        font-weight: 500;
        writing-mode: vertical-lr;
        text-align: center;
        letter-spacing: 5px;
        cursor: pointer;
    }
}
</style>

<template>
    <div class="knowledge-drawer" :class="{ 'knowledge-drawer-show': is_show_content }">
        <div class="knowledge-drawer-handle" @click="is_show_content = !is_show_content">
            查看知识点
            <el-icon class="knowledge-drawer-icon"><CaretLeft /></el-icon>
        </div>
        <div class="knowledge-drawer-content">
            <div style="display: flex">
                <div class="cost-accounting-top">
                    <div class="cost-accounting-name">教学资源</div>
                </div>
                <div class="eng-txt">Teaching resources</div>
            </div>
            <div class="knowledge-drawer-content-scroll">
                <div class="knowledge-item-box"></div>
                <div class="knowledge-item-box"></div>
            </div>
        </div>
    </div>
</template>
<script lang="ts" setup>
import { CaretLeft } from '@element-plus/icons-vue';

const is_show_content = ref(true);

// LK:我真牛逼!这个写法记住!
document.addEventListener(
    'click',
    (e) => {
        if (!is_show_content.value) {
            return;
        }
        const handleDom = e.target as HTMLElement;
        const drawerDom = document.querySelector('.knowledge-drawer');

        if (!drawerDom || !drawerDom.contains(handleDom)) {
            e.stopPropagation();//这一个也很关键,取消掉可以测试一下。
            is_show_content.value = false;
        }
    },
    true
);
</script>

还有其他方法监听的。

方法一:使用 e.target 和 .closest()(推荐)

方法二:遍历 e.composedPath()

方法三:直接判断祖先关系