最近踩坑微信小程序skyline模式,新版手势系统,怎么说呢,还是很好用的,相比于之前在视图层通过wxs去拦截手势操作的垃圾API,手势系统可简单多了,做了一个下面的悬浮按钮的例子
- 支持随意拖拽
- 点击弹出子菜单
- 触发拖拽前回收子菜单
源码关注小程序 miaoui 获取
选择手势系统
有下列几种手势系统对应不同需求
- double-tap-gesture-handler 双击
- force-press-gesture-handler 强击
- horizontal-drag-gesture-handler 水平方向
- long-press-gesture-handler 长按
- pan-gesture-handler 任意方向
- scale-gesture-handler 多指操作
- tap-gesture-handler 点击
- vertical-drag-gesture-handler 垂直方向
没太搞懂tap-gesture-handler用来干嘛。基于这个悬浮按钮的需求,这里使用pan-gesture-handler手势就够了,tap响应事件的话view本身就有。
实现悬浮按钮
悬浮按钮的实现原理很简单,元素跟踪手势滑动轨迹的x, y值就好了。
第一步:设计元素并绑定shared变量
wxml
使用手势标签框定需要拖动的元素
<pan-gesture-handler worklet:ongesture="hanldePan">
<view class="box center" catch:tap="ballTap">
<view wx:if="{{menuData.length}}" class="menu-box">
<view wx:for="{{menuData}}" wx:key="index" class="ball-menu-item item-{{index}}" bind:tap="{{item.tap}}">
<view wx:if="{{item.icon}}" class="ball-item {{item.icon}}"></view>
<text wx:if="{{item.title}}">{{item.title}}</text>
</view>
</view>
<image src="/images/logo.png" mode="widthFix" style="width: 100%;"/>
</view>
</pan-gesture-handler>
绑定元素与shared变量
因为手势系统是基于skyline模式开发的,这里需要使用worklet的绑定拖动元素,使得它运行在UI线程中。如果不了解worklet, shared共享变量就需要补一补课了,去官网熟悉一下。
// 将shared变量与元素绑定
this.applyAnimatedStyle('.box', () => {
'worklet'
return {
transform: `translate(${this.x.value}px, ${this.y.value}px)`
}
})
第二步,动态设置shared(x, y)变量
通过worklet:ongesture的响应方法捕获手势轨迹,将轨迹数据赋值给x, y就完成了,是不是很简单
hanldePan(e) {
'worklet'
const dx = e.deltaX
const dy = e.deltaY
this.x.value += dx
this.y.value += y
const ballWidth = 66
const padding = 40
if (e.state === 1) { // 捕获手势起始动作,收回子菜单
wx.worklet.runOnJS(this.showMyMenu.bind(this))(null, false)
}
if (e.state === 3) { // 手势结束
const absX = Math.abs(this.x.value)
//吸附靠边
if (absX < (this.halfWinWidth - (ballWidth / 2) - (padding / 2))) {
// 靠右
this.x.value = timing(0, {duration: 400})
} else {
// 靠左
this.x.value = timing(-(windowWidth - ballWidth - 40), {duration: 400})
}
}
}
timing(0, {duration: 400})
timing是worklet中内置的位移动画算法
'worklet'声明,手势系统响应方法需要声明为worklet才能正确执行
射出子菜单
UI上让子菜单以扇形展开。菜单项比较简单,传入数组数据就好了。UI这里涉及到圆,三角函数啥的,哈哈哈,这些都忘光了,还好去百度一下很快捡回来了。根据旋转的角度,通过 sin 和 cos 计算得到x, y的位移然后补上动画就行了,此处使用了wAnimate动画库,可以去小程序搜一下它的示例小程序
for (let ii=0; ii<datas.length; ii++) {
wAnimate(`${animateMenuPrefix}${ii}`, (ani)=>{
const angle = 45 * ii
const restAngle = 90 - angle
let toX = dd
let toY = 0
if (angle !== 0) {
toY = dd * Math.sin(angle*(Math.PI / 180))
const tmp = (toY*toY + dd*dd) - 2*dd*toY*Math.cos(restAngle*(Math.PI / 180))
toX = Math.sqrt(tmp)
}
ani.css({x: -(toX), y: -(toY), opacity: 1}).animate()
})
}
结尾
新搞的公众号,偏小程序的,欢迎关注: 悟空不是程序猿