微信小程序实现底部向上弹框

本文转自

作者:smallzip
链接:https://www.jianshu.com/p/bd9a4f4b8e6a
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

效果

小程序自定义底部弹出modal框组件,仿照小程序sheet-action的效果,封装成组件直接在其他业务页面公用。

  1. 底部弹出动画使用的是小程序的animation,弹出动画可以根据自行需求去替换。
  2. modal顶部有一条短的横线,向下滑动可以触发隐藏modal
  3. 点击阴影部分可以触发隐藏modal

使用方法

  1. 在业务页面引入组件
// pages/index/index.json
{
  "usingComponents": {
    "product-cart": "../../components/product-cart/index"
  }
}
// pages/index/index.js
Page({
  data: {
    show: false // true:显示  false:隐藏
  },
showModal(){
  this.setData({
       show:true
    })
  },
)}
  1. show参数是boolean值
// pages/index/wswl
<view>
  <button bindtap="showModal">点击</button>
 <product-cart show="{{show}}"></product-cart>
</view>

组件源码

// pages/index/components/buy/index.js
let pageY = 0;
Component({
  options: {
    styleIsolation: 'isolated'
  },
  /**
   * 组件的属性列表
   */
  properties: {
    show: {
      type: Boolean,
      value: false
    }
  },
  /**
   * 组件的初始数据
   */
  data: {
    list: [{
        name: '快递',
        selected: 1,
      },
      {
        name: '自提',
        selected: 0
      }
    ],
    animate: {},
    hideModal: false, //模态框的状态  false-隐藏  true-显示
  },
  /**
   * 数据监听
   */
  observers: {
    'show': function(val) {
      if (val) {
        this.showModal()
      } else {
        this.hideModal()
      }
    }
  },
  /**
   * 组件的方法列表
   */
  methods: {
    // 显示遮罩层
    showModal() {
      this.setData({
        hideModal: true
      })
      const animation = wx.createAnimation({
        duration: 500,
        timingFunction: 'ease',
      })
      // 先显示背景再执行动画,translateY(0)偏移量为0代表显示默认高度
      setTimeout(() => {
        animation.translateY(0).step()
        this.setData({
          animate: animation.export()
        })
      }, 50)
    },
    // 隐藏遮罩层
    hideModal() {
      const animation = wx.createAnimation({
        duration: 500,
        timingFunction: 'ease',
      })
      // 设置为100vh可以确保滚动到底部,可以按照自己的内容高度设置,能够滑到底部即可
      animation.translateY('100vh').step()
      this.setData({
        animate: animation.export(),
      })
      // 先执行动画,再隐藏组件
      setTimeout(() => {
        this.setData({
          hideModal: false
        })
      }, 300)
    },
    // 移动
    touchMove(e) {
      const clientY = e.changedTouches[0].clientY
      if (clientY - pageY > 0 && clientY - pageY > 50) {
        this.hideModal()
      }
    },
    // 触摸开始
    touchStart(e) {
      pageY = e.changedTouches[0].clientY;
    },
    // 选择类型
    changeItem(e) {
      const {
        index
      } = e.currentTarget.dataset
      this.data.list.forEach((e, i) => {
        if (i == index) {
          e.selected = 1
        } else {
          e.selected = 0
        }
      })
      this.setData({
        list: this.data.list
      })
    },
    // 确认
    confirm() {
      this.hideModal()
    },
  }
})

<!--pages/index/components/buy/index.wxml-->
<view class="box" hidden="{{!hideModal}}">
  <view class="empty-box" bindtap="hideModal" id="empty-box"></view>
  <scroll-view scroll-y style="max-height:80vh;">
    <view class="content" style="transform:translateY({{translateY}}px);" animation="{{animate}}">
      <!-- boll -->
      <view class="header" bindtouchstart="touchStart" bindtouchmove="touchMove">
        <view></view>
      </view>

      <!-- 快递类型 -->
      <view>
        <view class="item" wx:for="{{list}}" wx:key="index" bindtap="changeItem" data-index="{{index}}">
          <view class="item-name">{{item.name}}</view>
          <view>
            <view class="item-no-selected" wx:if="{{item.selected==0}}"></view>
            <image class="item-selected" wx:if="{{item.selected==1}}" src="/assets/images/choose.png"></image>
          </view>
        </view>
      </view>

      <!-- 按钮 -->
      <view class="button" bindtap="confirm">
        <view>确认</view>
      </view>

    </view>
  </scroll-view>
</view>

/* pages/index/components/buy/index.wxss */

.flex {
  display: flex;
  align-items: center;
}

.box {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 1000;
  width: 100vw;
  height: 100vh;
  background: rgba(0, 0, 0, 0.5);
  display: flex;
  flex-direction: column;
}

.empty-box {
  flex: 1;
  background-color: transparent;
}

/* 内容视图 */

.content {
  width: 100vw;
  background: rgba(255, 255, 255, 1);
  opacity: 1;
  border-radius: 20px 20px 0px 0px;
  z-index: 1001;
}

/* 头部 */

.header {
  position: relative;
  height: 80rpx;
  width: 100vw;
}

.header > view {
  position: absolute;
  top: 26rpx;
  left: calc(50vw - 30rpx);
  width: 60rpx;
  height: 10rpx;
  background: rgba(161, 166, 175, 1);
  opacity: 0.6;
  border-radius: 6rpx;
}

/* 快递 */

.item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: calc(100vw - 80rpx);
  padding: 0 40rpx;
  height: 100rpx;
  background: rgba(255, 255, 255, 1);
  opacity: 1;
}

.item-no-selected {
  width: 36rpx;
  height: 36rpx;
  background: rgba(255, 255, 255, 1);
  border: 2rpx solid rgba(112, 112, 112, 1);
  border-radius: 50%;
  opacity: 0.5;
}

.item-selected {
  width: 40rpx;
  height: 40rpx;
}

/* 按钮 */

.button {
  width: 100vw;
  padding: 80rpx 40rpx 20rpx 40rpx;
}

.button >view {
  width: calc(100% - 80rpx);
  height: 98rpx;
  border-radius: 50rpx;
  line-height: 98rpx;
  text-align: center;
  font-size: 32rpx;
  font-family: PingFang SC;
  font-weight: bold;
  color: rgba(255, 255, 255, 1);
  background: rgba(237, 58, 74, 1);
  opacity: 1;
}



作者:smallzip
链接:https://www.jianshu.com/p/bd9a4f4b8e6a
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 5
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值