100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > vue 自定义popup组件并支持scroll组件

vue 自定义popup组件并支持scroll组件

时间:2022-08-13 10:42:18

相关推荐

vue  自定义popup组件并支持scroll组件

本来是使用第三方库 vant的vue 组件库 的popup,后来在popup中使用better-scroll插件的时候,出现并不兼容的情况,也就自己搭建一个popu插件,中间遇到很多问题,都会记录一下,给自己一些总结!

popup组件实现思路

首先是模仿vant的popup组件的功能:vant-popup

1.popup组件开发与样式编写(一个遮罩层,一个显示层)。

2.组件引用(目前是选择的组件导入的方法,没有选择使用插件this.$popup(),主要是用户可以通过组件自定义传参,插件的方式不好实现)。

代码

popup.vue

<template><div ><!-- 遮罩层--><transition name="gh-fade" ><div class="gh-overlay "v-if="isShow"ref="overlay"@click="changShowStat(false)"style="z-index: ;":style="overlayStyle"></div></transition><!-- 内容层--><transition :name="'gh-slide-'+position"><div class="gh-popup "v-if="isShow"ref="popup":class="[getPosition,{'gh-popup--round':round}]"style="z-index:":style="styles"><slot></slot><i v-if="closeable"@click="changShowStat(false)"tabindex="0":class="{'gh-icon-cross': closeable && !closeIcon ,}"class="gh-icon gh-popup__close-icon gh-popup__close-icon--top-right"><img v-if="closeIcon" class="gh-icon__image" :src="closeIcon" alt=""></i></div></transition></div></template><script>export default {name: "Popup",props:{value:Boolean,overlayStyle:{type:Object},height:{type:String,default: 'none'},width:{type:String,default: 'none'},styles:{type:Object},position:{type:String},closeable:{type:Boolean,default:false},closeIcon:{type:String},round:{type:Boolean,default:false},},data(){return{isShow:this.value}},mounted(){this.$nextTick(() => {const body = document.querySelector("body");if (body.append) {body.append(this.$el);} else {body.appendChild(this.$el);}});},beforeUpdate(){},computed:{getPosition(){return 'gh-popup--'+this.position},},watch: {// 监听value的值更新value(val) {this.isShow = val;}},methods:{changShowStat(val){this.$emit('input',false)},},}</script><style scoped>.gh-overflow-hidden {overflow: hidden !important;}.gh-overlay {position: fixed;top: 0;left: 0;z-index: 1;width: 100%;height: 100%;background-color: rgba(0,0,0,.7);}@keyframes fadeInUp {from {opacity: 0;-webkit-transform: translate3d(0, 100%, 0);transform: translate3d(0, 100%, 0);}to {opacity: 1;-webkit-transform: translate3d(0, 0, 0);transform: translate3d(0, 0, 0);}}.fadeInUp {-webkit-animation-name: fadeInUp;animation-name: fadeInUp;animation-duration: 0.5s;}.gh-popup {position: fixed;max-height: 100%;overflow-y: auto;background-color: #fff;}.gh-popup--center {top: 50%;left: 50%;-webkit-transform: translate3d(-50%, -50%, 0);transform: translate3d(-50%, -50%, 0);}.gh-popup--top {top: 0;left: 0;width: 100%;}.gh-popup--bottom {bottom: 0;left: 0;width: 100%;}.gh-popup--left {top: 50%;left: 0;-webkit-transform: translate3d(0, -50%, 0);transform: translate3d(0, -50%, 0);}.gh-popup--right {top: 50%;right: 0;-webkit-transform: translate3d(0, -50%, 0);transform: translate3d(0, -50%, 0);}.gh-icon {position: relative;display: inline-block;font: normal normal normal 14px/1 ght-icon;font-size: inherit;text-rendering: auto;-webkit-font-smoothing: antialiased;}.gh-popup__close-icon {position: absolute;z-index: 1;color: #c8c9cc;font-size: 22px;cursor: pointer;}.gh-popup__close-icon--top-right {top: 16px;right: 16px;}.gh-icon-cross::before {content: "\2716";}.gh-icon__image {width: 1em;height: 1em;object-fit: contain;}.gh-icon::before {display: inline-block;}.gh-popup--round {border-radius: 20px 20px 0 0;}/* 内容css start*/@-webkit-keyframes gh-slide-bottom-enter {from {-webkit-transform: translate3d(0, 100%, 0);transform: translate3d(0, 100%, 0);}}@keyframes gh-slide-bottom-enter {from {-webkit-transform: translate3d(0, 100%, 0);transform: translate3d(0, 100%, 0);}}@-webkit-keyframes gh-slide-bottom-leave {to {-webkit-transform: translate3d(0, 100%, 0);transform: translate3d(0, 100%, 0);}}@keyframes gh-slide-bottom-leave {to {-webkit-transform: translate3d(0, 100%, 0);transform: translate3d(0, 100%, 0);}}@-webkit-keyframes gh-slide-top-enter {from {-webkit-transform: translate3d(0, -100%, 0);transform: translate3d(0, -100%, 0);}}@keyframes gh-slide-top-enter {from {-webkit-transform: translate3d(0, -100%, 0);transform: translate3d(0, -100%, 0);}}@-webkit-keyframes gh-slide-top-leave {to {-webkit-transform: translate3d(0, -100%, 0);transform: translate3d(0, -100%, 0);}}@keyframes gh-slide-top-leave {to {-webkit-transform: translate3d(0, -100%, 0);transform: translate3d(0, -100%, 0);}}@-webkit-keyframes gh-slide-left-enter {from {-webkit-transform: translate3d(-100%, 0, 0);transform: translate3d(-100%, 0, 0);}}@keyframes gh-slide-left-enter {from {-webkit-transform: translate3d(-100%, -50%, 0);transform: translate3d(-100%, -50%, 0);}}@-webkit-keyframes gh-slide-left-leave {to {-webkit-transform: translate3d(-100%, -50%, 0);transform: translate3d(-100%, -50%, 0);}}@keyframes gh-slide-left-leave {to {-webkit-transform: translate3d(-100%, -50%, 0);transform: translate3d(-100%, -50%, 0);}}@-webkit-keyframes gh-slide-right-enter {from {-webkit-transform: translate3d(100%, -50%, 0);transform: translate3d(100%, -50%, 0);}}@keyframes gh-slide-right-enter {from {-webkit-transform: translate3d(100%, -50%, 0);transform: translate3d(100%, -50%, 0);}}@-webkit-keyframes gh-slide-right-leave {to {-webkit-transform: translate3d(100%, -50%, 0);transform: translate3d(100%, -50%, 0);}}@keyframes gh-slide-right-leave {to {-webkit-transform: translate3d(100%, -50%, 0);transform: translate3d(100%, -50%, 0);}}@-webkit-keyframes gh-fade-in {from {opacity: 0;}to {opacity: 1;}}@keyframes gh-fade-in {from {opacity: 0;}to {opacity: 1;}}@-webkit-keyframes gh-fade-out {from {opacity: 1;}to {opacity: 0;}}@keyframes gh-fade-out {from {opacity: 1;}to {opacity: 0;}}@-webkit-keyframes gh-rotate {from {-webkit-transform: rotate(0deg);transform: rotate(0deg);}to {-webkit-transform: rotate(360deg);transform: rotate(360deg);}}@keyframes gh-rotate {from {-webkit-transform: rotate(0deg);transform: rotate(0deg);}to {-webkit-transform: rotate(360deg);transform: rotate(360deg);}}.gh-fade-enter-active {-webkit-animation: 0.5s gh-fade-in;animation: 0.5s gh-fade-in;}.gh-fade-leave-active {-webkit-animation: 0.5s gh-fade-out;animation: 0.5s gh-fade-out;}.gh-slide-top-enter-active {-webkit-animation: gh-slide-top-enter 0.5s both ease;animation: gh-slide-top-enter 0.5s both ease;}.gh-slide-top-leave-active {-webkit-animation: gh-slide-top-leave 0.5s both ease;animation: gh-slide-top-leave 0.5s both ease;}.gh-slide-bottom-enter-active {-webkit-animation: gh-slide-bottom-enter 0.5s both ease;animation: gh-slide-bottom-enter 0.5s both ease;}.gh-slide-bottom-leave-active {-webkit-animation: gh-slide-bottom-leave 1s both ease;animation: gh-slide-bottom-leave 1s both ease;}.gh-slide-left-enter-active {-webkit-animation: gh-slide-left-enter 0.5s both ease;animation: gh-slide-left-enter 0.5s both ease;}.gh-slide-left-leave-active {-webkit-animation: gh-slide-left-leave 0.5s both ease;animation: gh-slide-left-leave 0.5s both ease;}.gh-slide-right-enter-active {-webkit-animation: gh-slide-right-enter 0.5s both ease;animation: gh-slide-right-enter 0.5s both ease;}.gh-slide-right-leave-active {-webkit-animation: gh-slide-right-leave 0.5s both ease;animation: gh-slide-right-leave 0.5s both ease;}</style>

组件导入

//导入popup组件import Popup from "Popup";

//在components中注册组件components:{Popup,},

//组件的使用<popup v-model="isShow" //用于控制组件是否显示position="bottom" //用于控制组件显示位置:styles="{height:'50%'}" //组件样式></popup>

组件设计到的功能点:

1.v-model 组件之间参数双向绑定

watch: {// 监听value的值更新value(val) {this.isShow = val;}},

methods:{//使用$emit对父组件的v-model,通过input进行数据绑定changShowStat(val){this.$emit('input',false)},},

2.组件自定义挂载(我这里是挂载到了body下)

使用$nextTick函数,在组件更新时对组件进行自定义挂载,我看iview你们也有全局挂载,但实现思路不一样,还在研究中。。

mounted(){this.$nextTick(() => {const body = document.querySelector("body");if (body.append) {body.append(this.$el);} else {body.appendChild(this.$el);}});},

3.vue组件过渡动画,因为开始学vue没多久,刚开始使用的js动态挂载css动画,来实现,逻辑代码写了一大堆,中间遇到很多问题,比如在watch钩子函数中,dom元素获取不到问题,还为此仔细学习了vue生命周期,最后实现了,还是出现点击频率过快,出现组件加载问题,说了很多废话,最后学习了vue的过渡动画 transition组件,来实现的。

样式写在vue组件的样式里面

参数props

虽然花了3天时间学了不少东西:

1.v-model 组件间的参数双向绑定

2.vue生命周期

3.vue $$nextTick函数

4.css3动画

5.vue <transition>过渡组件

6.vue插件开发

7.第三方ui库组件的引入

遇到的bug

1.props中数据只能单向传输,无法双向绑定,不要在子组件中改变props参数

2.css加载顺序问题,导致页面效果出不来,加载顺序是自上而下,渲染优先级是自下而上(类似java栈)

3.vantUI组件的popup不兼容better-scroll,所以才自己做了一个

4.页面按钮点击无效,因为使用了定位布局,父组件没有设置高度,导致无法点击

5.当自定义动画,使用js动态加载动画时,因为需要退出效果,往往在修改是否显示的参数(value)的时候,需要延时0.5秒,等动画显示完成后,再修改value的值;这时候点击过快会出现组件加载问题,考虑过使用防抖来优化,但是感觉治标不治本,后面采用了vue <transition>过渡组件。

methods:{changShowStat(val){if(val){this.$emit('input',val)}else{setTimeout(() => {this.$emit('input',val)}, 500)}},

6.dom获取不到问题,你会发现能打印this.$refs 能够找到dom,但是就是去获取this.$refs.xxx的时候,显示undefined ,主要是DOM结构已经出来了,但是如果在DOM结构中的某个DOM节点使用了v-if、v-show或者v-for(即根据获得的后台数据来动态操作DOM,即响应式),那么这些DOM是不会再mounted阶段找到的。参考资料,主要是dom没用挂载 最后自己也详细回顾了一下生命周期,觉得这个搞不懂,vue就算没入门;

继续学习!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。