效果
小程序自定義sheet-action.gif
小程序自定義底部彈出modal框組件序矩,仿照小程序sheet-action的效果,封裝成組件直接在其他業(yè)務(wù)頁面公用跋破。
- 底部彈出動(dòng)畫使用的是小程序的animation簸淀,彈出動(dòng)畫可以根據(jù)自行需求去替換。
- modal頂部有一條短的橫線毒返,向下滑動(dòng)可以觸發(fā)隱藏modal
- 點(diǎn)擊陰影部分可以觸發(fā)隱藏modal
使用方法
- 在業(yè)務(wù)頁面引入組件
// 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
})
},
)}
- show參數(shù)是boolean值
// pages/index/wswl
<view>
<button bindtap="showModal">點(diǎn)擊</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
}
},
/**
* 組件的初始數(shù)據(jù)
*/
data: {
list: [{
name: '快遞',
selected: 1,
},
{
name: '自提',
selected: 0
}
],
animate: {},
hideModal: false, //模態(tài)框的狀態(tài) false-隱藏 true-顯示
},
/**
* 數(shù)據(jù)監(jiān)聽
*/
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',
})
// 先顯示背景再執(zhí)行動(dòng)畫租幕,translateY(0)偏移量為0代表顯示默認(rèn)高度
setTimeout(() => {
animation.translateY(0).step()
this.setData({
animate: animation.export()
})
}, 50)
},
// 隱藏遮罩層
hideModal() {
const animation = wx.createAnimation({
duration: 500,
timingFunction: 'ease',
})
// 設(shè)置為100vh可以確保滾動(dòng)到底部,可以按照自己的內(nèi)容高度設(shè)置拧簸,能夠滑到底部即可
animation.translateY('100vh').step()
this.setData({
animate: animation.export(),
})
// 先執(zhí)行動(dòng)畫劲绪,再隱藏組件
setTimeout(() => {
this.setData({
hideModal: false
})
}, 300)
},
// 移動(dòng)
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
})
},
// 確認(rèn)
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>確認(rèn)</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;
}
/* 內(nèi)容視圖 */
.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;
}