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: 2019;":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:2020":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
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
v-model | 当前组件是否显示 | boolean |
false
|
overlay | 是否显示遮罩层 | boolean |
true
|
position |
弹出位置,可选值为 top bottom right left
|
string |
center
|
overlay-style | 自定义遮罩层样式 | object | - |
round | 是否显示圆角 | boolean |
false
|
styles | 内容层样式 | boolean |
true
|
closeable | 是否显示关闭图标 | boolean |
false
|
close-icon | 关闭图标名称或图片链接 | string |
cross
|
虽然花了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就算没入门;
继续学习!
vue 自定义popup组件并支持scroll组件相关推荐
- 很遗憾,该服务器不支持 jmail 组件!,是否支持JMAIL组件,如何使用Jmail发送邮件?...
我司所有WINDOWS系列虚拟主机都支持JMAIL邮件发送. JMAIL邮件发送ASP示例代码如下: Set msg = Server.CreateObject("JMail.Message ...
- 基于 vue 编写的vue图片预览组件,支持单图和多图预览,仅传入一个图片地址,即可实现图片预览效果,可自定义背景、按钮颜色等
hevue-img-preview 简介 完整版下载地址:基于 vue 编写的vue图片预览组件 本组件是一个基于 vue 编写的 vue 图片预览组件,支持 pc 和手机端,支持单图和多图预览,仅传 ...
- vue 嵌套表格组件_使用form-create动态生成vue自定义组件和嵌套表单组件
使用form-create动态生成vue自定义组件和嵌套表单组件 maker.create 通过建立一个虚拟 DOM的方式生成自定义组件 生成 Maker let rule = [ formCreat ...
- vue自定义组件是.vue还是html,Vue自定义组件的四种方式示例详解
四种组件定义方式都存在以下共性(血泪史) 规则: 1.组件只能有一个根标签 2.记住两个词全局和局部 3.组件名称命名中'-小写字母'相当于大写英文字母(hello-com 相当于 helloCom) ...
- vue自动滚动组件 可以支持鼠标滚轮操作
vue自动滚动组件 可以支持鼠标滚轮操作 <!-- *@AutoScrollList *@author GYY *@date 2022/7/20 11:22 --> <templat ...
- Vue自定义组件npm上传私服,且从私服下载使用
Vue自定义组件npm上传私服,yarn私服下载使用 安装镜像源管理工具nrm 发布自定义组件到公司私服 若未下载nrm源管理工具直接使用npm设置私服地址也可以 以下步骤都是基于npm命令 从私服下 ...
- vue可视化拖拽组件模板,vue自定义下拉框组件
怎样利用Vue动态生成form表单 . $formCreate参数rules 表单生成规则[inputRule,selectRule,...]options 初始化配置参数(详细见底部createOp ...
- vue 自定义组件双向数据绑定
文章目录 序 属性&事件传值双向绑定 v-model组件双向绑定 .sync修饰符双向绑定 总结 !!!这边文章记录的是 vue2 的概念,vue3 对双向绑定进行了改动,不要把一下代码放到 ...
- vue 定义全局弹框_VUE路由拦截:Vue自定义全局弹窗组件
前言 在任何一个平台中,如果需要增加用户黏度,除了用户需要的基本内容外,用户登录注册提交信息也是非常重要的一环,可以了解用户基本信息,用户喜欢等. 抛出前后端混合开发外,vue可以轻松的实现路由拦截. ...
最新文章
- Java算法练习——正则表达式匹配
- linux_Nginx日志
- JavaWeb(八)——JSP(Java服务器端页面)
- 1079. 延迟的回文数 (20)
- 2017上海金马五校 购买装备 贪心+二分Check
- Git和SVN的区别,Git的使用方法大全
- 基于生成对抗网络的医学数据域适应研究
- 4章. 安装CentOS 5.x与多重引导小技巧
- 浏览器兼容性小记-DOM篇(二)
- 朴素贝叶斯详解及其python实现
- Minecraft Server 搭建手账
- 为什么在POS非常火爆的时候代还系统还能抢下市场呢?
- 椭圆是一个凸集的证明
- 二分法中为啥常用mid=beg+(end-beg)/2 而非mid=(beg+end)/2 ?
- 互联网企业盈利模式全分析
- 无线充电宝当无线充电器哪个品牌好?快充无线充电宝推荐
- 离散傅里叶变换DFT、离散余弦变换DCT、离散正弦变换DST,原理与公式推导
- aws WorkMail和SES实战
- HTTPS URL是否被加密?
- KPI是什么? 网站KPI指标怎么拟定?
热门文章
- 完成 新闻界面+一个注册界面(根据群内截图做出 相同(大体一致)的界面 ) 压缩包内有完成后的素材:样式截图3张+新闻文本txt+一个新闻页图片 注意1:在新闻界面 点击 注册用户 后跳转
- Linux修改资源限制详解
- 基于51单片机音乐盒仿真设计(音乐播放器)
- OBS 增加人脸特效插件
- 深入认识笔记本电脑的网卡
- nif 与 HiPE
- VS2015报错 error C2065:未声明的标识符错误 (__func__)
- 质量流量计的六种管道流体类型
- 如何仿真晶体管跨导gm的非线性
- 《能断金刚》46个商业问题解决之法