自定义微信小程序 picker 组件
微信小程序中的 picker 是一个从底部弹起的滚动选择器,虽然能够满足大多数应用场景, 但如果我们需要让内容不局限于选择器,而是要在这个 抽屉 里面放各式各样的内容, 那官方 api
就不太能够满足这个需求了,我们需要自定义一个 picker
组件。这个效果实现起来并不难,但是涉及到一些基础知识,因此稍加记录。
组件结构
picker
译为 选择器,但我们想要完成的这个组件的内容可能是一些表单、一些文档等,它可以像抽屉一样弹出,因此我们将它 命名为 drawer
。
为了模仿原本 picker
的效果,可以将 drawer
写为 两个 view
,一个为 黑色蒙版,一个为 抽屉内容,其中内容需要留一个 slot
。在弹出抽屉 时,黑色蒙版缓慢渐显,抽屉内容缓慢从底部弹出,所以需要先设定两者动画时间。
/* drawer.wxss */
/* 黑色蒙版 */
.mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
transition: opacity 0.3s ease; /* 透明度时间 */
z-index: 999;
}
/* 内容 */
.content {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 40%;
background: white;
padding: 20rpx;
border-radius: 20rpx 20rpx 0 0;
transition: transform 0.2s ease; /* 底部弹出时间 */
z-index: 1000;
}
需要点击黑色蒙版时,关闭抽屉,使用 bindtap
绑定父视图的点击事件,同时使用 catchtap
绑定子视图的点击事件(空事件),阻止事件冒泡到父视图。
<!-- drawer.wxml -->
<!--黑色蒙版-->
<view class="mask" style="opacity: {{maskOpacity}};" wx:if="{{isShow}}" bindtap="closeDrawerHandler">
<!-- 内容区域 -->
<view class="content" style="transform: translateY({{transformValue}});" catchtap="handleChildTap">
<slot></slot>
</view>
</view>
在 js 中定义 isShow
用老控制 drawer
是否渲染,maskOpacity
用来控制蒙版透明度,transformValue
用来控制抽屉内容移动的距离。
小程序中的 setData
是异步的,因此可以用到 setData
的第二个参数,回调函数( setData
成功之后执行的函数)来执行组件被渲染后的 DOM
操作。或者使用 wx.nextTick()
确保在数据变更引起的视图渲染完成后,再执行 DOM
操作。同时,因为在 wxss
中定义的动画时间为 300 毫秒,因此在关闭抽屉时,需要在动画执行结束后 300 毫秒后, 再设置 isShow
为 false
。
// drawer.js
Component({
data: {
isShow: false, // 控制显示
maskOpacity: 0,
transformValue: '100%'
},
methods: {
// 显示 picker
showDrawerHandler() {
this.setData({
isShow: true
})
wx.nextTick(() => {
this.showAnimation();
});
},
closeDrawerHandler() {
this.hideAnimation();
setTimeout(() => {
this.setData({
isShow: false
})
}, 300)
},
// 显示动画
showAnimation() {
this.setData({
maskOpacity: 0,
transformValue: '100%'
}, () => {
setTimeout(() => {
this.setData({
maskOpacity: 1,
transformValue: '0'
})
}, 50)
})
},
// 隐藏动画
hideAnimation() {
this.setData({
maskOpacity: 0,
transformValue: '100%'
})
},
handleChildTap(){}
},
})
外部引用
在父视图的 .json
中定义 usingComponents
,将 drawer
组件引入:
"usingComponents": {
"drawer": "/components/drawer/drawer"
}
在 wxml
中使用 drawer
组件,并给组件一个 ID:
<drawer id="custom-drawer">
<view>
抽屉内容
</view>
</drawer>
<button type="primary" bindtap="showDrawer"></button>
在 js
中使用 selectComponent
获取组件实例,并调用组件的 showDrawerHandler
方法:
showDrawer(){
let drawer = this.selectComponent("#custom-drawer")
picker.showDrawerHandler()
}