一起来封装一个小程序弹窗组件吧,(带缩放动画)

927 阅读3分钟

起源:一直觉得很有必要自己封装一个弹窗组件,一边实现各种不同产品的需求,但又要注重交互体验,加个缩放动画,一直都没写个好看的,终于,在前几天写了一个我自认为丝滑的,有动画的,弹窗的组件,哈哈哈哈哈哈哈哈哈哈哈哈哈哈

先说下动画

思路的话,我觉得,可能不一样的打就是加动画,这里我用的是css3的animation,遮罩层从rgba(0, 0, 0, 0)到rgba(0, 0, 0, 0.7),弹窗主体采用transform: translate(-50%, -50%) scale(0);到transform: translate(-50%, -50%) scale(1),要注意的是因为弹窗始终要居中,所以我用translate(-50%, -50%)配合了position:absolute;top:50%;left:50%,

关键在动画的消失

因为动画要缩回去,所以要动画的时间,如果世上外面传一个flag来控制的话,显然是不能实现中国需求的,所以我做的是在弹窗里面再写一个flag,当外面传一个flag为true时,里面dialogFlag也为true,显示的时候是外面的触发里面的flag,消失的时候是里面触发外面的即dialogFlag先设置为false,延迟0.3s再将外面的flag设置为false就好了(要是小伙伴有更好的办法解决可以私聊呀),这里需要用到微信的observers(监听事件),它可以监听到外面的flag并设置里面的flag

小程序组件的事件触发

triggerEvent(触发事件),它接收三个参数,triggerEvent(事件名称,参数,事件选项--控制冒泡,捕获)

到这里,差不多就讲完了,下面就是完整代码啦:

wxml:因为有些场景是要盖住地图,或者图片,所以这里我主体采用了cover-view
<!--Component/dialog/dialog.wxml-->
<cover-view wx:if="{{show}}" class="{{dialogFlag?'mask':'mask hideMask'}}" catchtap="close">
	<cover-view class="dialog" style="width:{{dialogWidth}}">
		<cover-view class="dialog_title" wx:if="{{title}}">{{title}}</cover-view>
		<cover-view class="dialog_body" wx:if="{{content}}">{{content}}</cover-view>
    <slot></slot>
		<cover-view class="dialog_footer">
			<cover-view class="dialog_btn" catchtap="close">取消</cover-view>
			<cover-view class="dialog_btn dialog_primary" catchtap="confirm">确定</cover-view>
		</cover-view>
	</cover-view>
</cover-view>
<!-- <dialog title="提示" show="{{show}}" content="这是一个带有动画缩放效果的弹窗" bind:close="onClose"></dialog> -->
wxss:
/* Component/dialog/dialog.wxss */
.mask {
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  position: fixed;
  animation: enterMask 0.3s;
  background: rgba(0, 0, 0, 0.7);
  display: block;
}

.dialog {
  padding: 40rpx;
  box-sizing: border-box;
  background: #ffffff;
  border-radius: 20rpx;
  position: absolute;
  left: 50%;
  top: 50%;
  animation: enterDialog 0.3s;
  transform: translate(-50%, -50%);
}

.hideMask {
  animation: leaveMask 0.3s;
  display: block;
}

.hideMask .dialog {
  transform: translate(-50%, -50%) scale(0);
  display: block;
  animation: leaveDialog 0.3s;
}

@keyframes enterMask {
  from {
    background: rgba(0, 0, 0, 0);
    display: none;
  }

  to {
    background: rgba(0, 0, 0, 0.7);
    display: block;
  }
}

@keyframes leaveMask {
  from {
    background: rgba(0, 0, 0, 0.7);
    display: block;
  }

  to {
    background: rgba(0, 0, 0, 0);
    display: none;
  }
}

@keyframes enterDialog {
  from {
    /* transform: ; */
    transform: translate(-50%, -50%) scale(0);
  }

  to {
    /* transform: ; */
    transform: translate(-50%, -50%) scale(1);
  }
}

@keyframes leaveDialog {
  from {
    /* transform: ; */
    transform: translate(-50%, -50%) scale(1);
    display: block;
  }

  to {
    /* transform: ; */
    transform: translate(-50%, -50%) scale(0);
    display: none;
  }
}

.dialog_title {
  font-size: 32rpx;
  font-weight: 500;
  text-align: center;
  color: #333333;
  padding: 20rpx 0 40rpx;
}

.dialog_body {
  font-size: 24rpx;
  font-weight: 400;
  color: #666666;
  line-height: 33rpx;
  margin: 0 auto;
  width: 340rpx;
  white-space: normal;
  text-align: center;
}

.dialog_footer {
  display: flex;
  padding: 60rpx 50rpx 10rpx;
  justify-content: space-between;
}

.dialog_btn {
  width: 200rpx;
  height: 70rpx;
  background: #ffffff;
  border: 2rpx solid #009aff;
  border-radius: 35rpx;
  font-size: 28rpx;
  font-weight: 400;
  text-align: center;
  color: #009aff;
  line-height: 70rpx;
}

.dialog_primary {
  background: #009AFF;
  color: #fff;
}

json:组件的常规配置
{
  "component": true,
  "usingComponents": {}
}
js:重点,哈哈哈
Component({
  /**
   * 组件的属性列表 props
   */
  properties: {
    // 标题
    title: {
      type: String,
      value: ''
    },
    // 内容
    content: {
      type: String,
      value: ''
    },
    // 显示-隐藏flag
    show:{
      type:Boolean,
      value:false
    },
    // 是否显示底部按钮
    footer:{
      type:Boolean,
      value:true
    },
    // 弹窗的宽度
    dialogWidth:{
      type: String,
      value: '650rpx'
    }
  },

  /**
   * 组件的初始数据
   */
  data: {
    dialogFlag:false//收起、展开mask动画
  },

  /**
   * 组件的方法列表
   */
  methods: {
    close(){
      // 
      /*
        triggerEvent 自定义组件触发事件,triggerEvent('myevent', myEventDetail, myEventOption)
        myevent:事件的触发名称
        myEventDetail:传递出去的参数
        myEventOption:事件选项
        {
          bubbles	  Boolean	否	false	事件是否冒泡
          composed	Boolean	否	false	事件是否可以穿越组件边界,为false时,事件将只能在引用组件的节点树上触发,不进入其他任何组件内部
          capturePhase	Boolean	否	false	事件是否拥有捕获阶段
        }
      */
      this.setData({
        dialogFlag:false
      })
      setTimeout(()=>{
        this.triggerEvent('close')
      },300)
    },
    confirm(){
      setTimeout(()=>{
        this.triggerEvent('confirm')
      },300)
    }
  },
  observers: {
    // 监听props,设置dialogFlag
    'show': function() {
      // console.log(this.properties.show)
      if (this.properties.show) {
        this.setData({
          dialogFlag:true
        })
      }
    }
  }
})

到这里就非常nice的结束了,要是有小伙伴有更好的解决方法,可以评论呀