简单实现 弹窗组件popup

996 阅读2分钟

简单实现 弹窗组件Popup

最近在开发需求上面发现了一个弹窗需求,平常赶需求进度的话一般都是直接使用vant里面直接使用组件,比较方便上手。最近刚好有机会摸鱼,遇到这个需求,干脆自己封装一下这个组件,以后肯定也会用上。顺便把在封装里面的一些小坑记录一下

实现的功能类似以下这张图:

11.png

这种组件其实最核心的就是弹窗,遮罩的动画和样式,其他的带有逻辑性的api都可以用父子组件的通信处理。

popup(组件复制可以直接使用,vue2.0的兼容性比较强,所以用的是vue2.6.1版本):

<template>
    <div :style="{ '--theme-color': themeColor }">
        <section v-if="isMask" class="mask" @click="closePopup()" />
        <footer :class="['footer', getOpenType()]">
            <p class="time-item" v-for="item in times" :key="item.key" :class="item.key === 1 ? 'border' : ''" @click="chooseTime(item)">
                {{ item.time }}
            </p>
            <section class="cancel">取消</section>
        </footer>
    </div>
</template>
<script>
export default {
    name: "popup",
    props: {
        value: {
            type: Boolean,
            default: false,
        },
        themeColor: {
            type: String,
            default: "#fff",
        },
    },
    data() {
        return {
            isMask: false,
            times: [
                { time: "最近一年", key: 1, days: 365 },
                { time: "最近三个月", key: 2, days: 90 },
                { time: "最近一个月", key: 3, days: 30 },
                { time: "最近七天", key: 4, days: 7 },
            ],
            openType: 0, // 0 没有变化 1 打开 2 关闭
        };
    },
    watch: {
        value(oldValue, newValue) {
            if (oldValue) {
                this.isMask = true;
                this.openType = 1;
            }
            if (newValue) {
                this.openType = 2;
                setTimeout(() => {
                    this.isMask = false;
                }, 200);
            }
        },
    },
    methods: {
        closePopup() {
            this.$emit("input", false);
        },
        getOpenType() {
            let type = this.openType === 1 ? "open" : this.openType === 2 ? "close" : "init";
            return type;
        },
        chooseTime(item){
            this.$emit("chooseTime",{...item});
        }
    },
};
</script><style scoped>
.popup {
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    font-size: 17px;
    transition: all 0.5s;
}
.mask {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-color: #000000;
    opacity: 0.5;
}
.footer {
    position: fixed;
    bottom: 0;
    left: 0;
    width: 100%;
    border-radius: 12px;
    background-color: var(--theme-color);
}
.border {
    border-radius: 12px;
}
.init {
    height: 0px;
}
.open {
    height: 321px;
    animation: slideContentUp 0.2s linear both;
}
​
.close {
    height: 321px;
    animation: slideContentDown 0.2s linear both;
}
.time-item {
    height: 56px;
    background-color: #ffffff;
    line-height: 56px;
    text-align: center;
    border-bottom: 1px solid #f5f5f5;
}
​
.cancel {
    margin-top: 12px;
    height: 84px;
    width: 100%;
    line-height: 84px;
    background-color: #ffffff;
    text-align: center;
}
@keyframes slideContentUp {
    from {
        height: 0;
    }
​
    to {
        height: 320px;
    }
}
​
@keyframes slideContentDown {
    from {
        height: 320px;
    }
​
    to {
        height: 0;
    }
}
</style>

需要注意的地方做一下记录:

为什么一开始把这个弹窗的高度设置为0?

这边使用的是 animation 做一个动效处理。如果一开始没有将弹窗的高度设置为0的话,会出现这种情况:一进入页面,默认接收的是否接受弹窗的参数是 false,会导致一进入页面就会有弹窗向底部滑动的样式。所以这边做了一个watch监听,用 openType参数 来做弹窗展示的显示(0:不设置高度,1:打开弹窗的时候,给上高度 2:关闭弹窗,同理 )。

遮罩优化

在watch监听里面关闭弹窗的时候会用 setTimeOut 延时动画的时间后在关闭,提升用户的体验感

出现遮罩以后滚动鼠标,底部页面滚动的问题

1、在打开遮罩层的事件中添加以下代码来禁止滚动

 document.documentElement.style.overflow = 'hidden';

2、在关闭遮罩层的事件中添加以下代码来允许滚动

document.documentElement.style.overflow = 'scroll';

在父组件使用

正常引入后

<Popup v-model="isShow" themeColor="#f5f5f5" @chooseTime="chooseTime" />
    // isShow :控制弹窗
    // themeColor : 主题颜色
    // chooseTime :点击时间时触发

我这边项目比较简单,使用到的api就保留了三个。只要实现了基本的样式功能,理润上各位大哥在copy使用的时候,可以加上一些自己需求上的一些功能进行扩展。确实自己封装一个小组件在打包以后的大小会比使用vant的小很多。 好了,换个姿势摸摸鱼

22.png