小程序官方API其实已经通过wx.showToast,wx.showModal,wx.showLoading实现一些通用信息提示,loading的效果,方便又顺滑,那我们再弄一个弹框通用组件的目的就是为了解决不同元素内容的复用性,小程序官方提供了slot插槽,实现更高的自定义内容:
同样,小程序的弹框组件也是深度结合animate.css,只要传入对应的名称就可以有相应的进场和出场动画!
一、index.js
Component({ properties: { isShow: { type: Boolean, value: "" }, animIn: { type: String, value: 'zoomIn' }, animOut: { type: String, value: 'zoomOut' }, title: { type: String, value: '' }, duration: { type: Number, value: 350 }, shade: { type: Boolean, value: true }, shadeClose: { type: Boolean, value: true }, zIndex: { type: Number, optionalTypes: [String], value: 100 }, button: { type: Array, value: [{ title: '取消', className: '', type: 'cancel' }, { title: '确认', className: '', type: 'confirm' } ] } }, options: { multipleSlots: true // 在组件定义时的选项中启用多slot支持 }, data: { isRunning: false, //动画是否在进行 styleBodyString: "", animDir: "", //false表示动画属于退出状态,true表示动画显示状态 toggleAnim: '' }, observers: { 'isShow': function () { var that = this; if (this.data.isShow) { if (!this.data.styleBodyString) { const query = this.createSelectorQuery(); query.select('#J_coms-dialog').boundingClientRect(); if (this.data.button.length) { query.select('#J_dialog-btn').boundingClientRect(); } query.exec(function (res) { let footerH = 0; let mainH = 0; res.forEach(function (item, index, arr) { if (index == 0) { mainH = res[0].height; } else if(index == 1) { footerH = res[1].height; } if (index == arr.length - 1) { that.setData({ styleBodyString: "display:block;visibility:visible;left:50%;top:50%;margin-left:-" + res[0].width / 2 + "px;margin-top:-" + (mainH + footerH) / 2 + "px;width:" + res[0].width + "px;padding-bottom:" + footerH + "px" }) } }) that.setData({ animDir: true, //isShow=true,animDir 进入 toggleAnim: true //进入动画 animIn }) }) } else { that.setData({ animDir: true, //isShow=true,animDir 进入 toggleAnim: true //进入动画 animIn }) } } else { that.setData({ animDir: false //退出状态 }) } } }, lifetimes: { // 生命周期函数,可以为函数,或一个在methods段中定义的方法名 attached: function () { if (!this.data.duration || !this.data.animIn) { this.setData({ animIn: "", animOut: "", duration: 0 }) } } }, methods: { animationStart: function () { this.setData({ isRunning: true }) }, animationEnd: function () { var that = this; // animDir=false 表示退出状态 if (!this.data.animDir) { this.setData({ isShow: false }) } else { this.triggerEvent("onShow") } this.setData({ isRunning: false, animDir: !this.data.animDir }); }, onShadeClose: function () { if (!!this.data.shadeClose && !this.data.isRunning) { this.onClose(); } }, switchChange: function (e) { var data = e.currentTarget.dataset; if (data.type == 'cancel') { this.onClose(); } else { this.triggerEvent('onConfirm') } }, onClose: function () { //执行退出动画(触发animationend) if (this.data.animIn) { this.setData({ toggleAnim: false }); } else { this.setData({ isShow: false }); this.triggerEvent('onClose') } this.triggerEvent("onClose") }, touchMoveHandler: function () { return false; } } })
通过observers监听isShow,判断当前的弹框是属于显示还是隐藏状态。如果是显示状态,这个时候animDir表示可以“退出”,相反如果弹框是隐藏状态,animDir表示可以“进入”状态,然后监听动画animationEnd事件,通过回调函数里面判断animDir的方向,决定是要执行进场动画还是出场的动画!
二、index.wxml
<view wx:if="{{isShow}}" class="coms-dialog {{toggleAnim ? animIn : (isShow===true ? animOut :'')}}" style="z-index:{{zIndex+1}};animation-duration:{{duration}}ms;{{isShow ? styleBodyString : 'display:none;visibility: hidden;'}}" id="J_coms-dialog" catchtouchmove="touchMoveHandler" bindanimationstart="animationStart" bindanimationend="animationEnd"> <view class="dialog-body"> <slot name="content"></slot> </view> <view class="dialog-btn" id="J_dialog-btn" wx:if="{{button.length}}"> <view class="btn-handle {{item.className}} btn-{{item.type}}" wx:key="index" style="{{item.style}}" wx:for="{{button}}" data-type="{{item.type}}" bindtap="switchChange">{{item.title}}</view> </view> </view> <view catchtouchmove="touchMoveHandler" class="coms-dialog-mask {{(toggleAnim && isShow) ? 'active' :''}}" bindtap="onShadeClose" style="z-index:{{zIndex}};transition-duration:{{duration}}ms;"></view>
三、index.wxss
@import "../../commons/css/animate.wxss"; .coms-dialog { box-sizing: border-box; z-index: 12; position: fixed; visibility: hidden; animation-fill-mode: both; border-radius: 10rpx; overflow: hidden; } .coms-dialog-mask { position: fixed; top: 0; left: 0; right: 0; bottom: 0; animation-fill-mode: both; visibility: hidden; opacity: 0; background-color: rgba(0, 0, 0, 0.6); transition: all 0.25s ease-out; } .coms-dialog-mask.active { opacity: 1; visibility: visible; } .coms-dialog .dialog-btn { display: flex; position: absolute; left: 0; right: 0; text-align: center; bottom: 0; background: #fff; border-radius: 0 0 10rpx 10rpx; } .coms-dialog .dialog-btn .btn-handle { flex: 1; border-top: 1px solid #d2d3d5; border-right: 1px solid #d2d3d5; height: 95rpx; line-height: 95rpx; font-size: 32rpx; } .coms-dialog .dialog-btn .btn-cancel { color: #000000; } .coms-dialog .dialog-btn .btn-confirm { color: #fe2643; } .coms-dialog .dialog-btn .btn-handle:last-child { border-right: 0; }
调用的方式
<dialog isShow="{{isShow3}}" bind:onClose="onClose3" bind:onConfirm="onConfirm3" button="{{[{title:'我知道了',type:'cancel',style:'color:#fe2643'}]}}"> <view slot="content" class="dialog-dhm-useintro"> <view class="cap">使用说明</view> <scroll-view class="intro-txt" scroll-y="true" style="height:750rpx;"> <view class="txt-in"> <view wx:for="{{currIntro}}">{{item}}</view> </view> </scroll-view> </view> </dialog>
上一篇: 移动端弹框组件mDialog
下一篇: 小程序授权登录的踩坑