在小程序中实现左滑删除(侧滑删除)并不是一个很轻松的事情,因为有几个问题(坑)是需要我们考虑的:
- 小程序的事件系统是存在缺陷的,其中不能在事件处理过程中调用
preventDefault
来动态决定事件是否允许冒泡,假设用户斜着滑动一个条目时,我们可能需要根据用户滑动的斜率等等来判断这次滑动是用来触发侧滑还是页面滚动,在这方面的处理可能比较复杂。
这里感谢有专自媒体
的踩坑文章: 微信小程序实现左滑删除-一切没有那么简单
- 一般的,我们可能比较习惯这么去写左滑删除
html
<view wx:for='{{list}}' style="left:-{{item.offset}}rpx" class="scroll-view-item">
</view>
js
`touchMove`(e) {
if (e.touches.length == 1) {
let pointBefore = this.data.pointBefore;
let thisPoint = e.touches[0];
let slope = Math.abs(pointBefore.clientY - thisPoint.clientY) / Math.abs(pointBefore.clientX - thisPoint.clientX);
console.log(slope)
if (slope > 0.57) {
return
}
let index = e.currentTarget.dataset.index;
let offsetResult = 0;
//手指移动时水平方向位置
let moveX = e.touches[0].clientX;
//手指起始点位置与移动期间的差值
let disX = this.data.startX - moveX;
let resultX = this.data.startOffset + disX;
if (resultX <= 0) {//如果移动距离小于等于0,说明向右滑动,文本层位置不变
offsetResult = resultX;
} else if (resultX >= this.data.maxOffset) {
//控制手指移动距离最大值为删除按钮的宽度
offsetResult = this.data.maxOffset;
} else if (resultX > 0) {//移动距离大于0,文本层left值等于手指移动距离
offsetResult = resultX;
}
//更新列表的状态
this.setData({
offset: offsetResult
});
}
}
但是在真机测试的时候总是卡卡的,一种可能是:
小程序不推荐在js里头监听touchMove
等ui事件并且每次都去修改ui内容,原因:
WXS相应事件
背景
有频繁用户交互的效果在小程序上表现是比较卡顿的,例如页面有 2 个元素 A 和 B,用户在 A 上做
touchmove
手势,要求 B 也跟随移动,<movable-view
> 就是一个典型的例子。一次touchmove
事件的响应过程为:a、
touchmove
事件从视图层(Webview)抛到逻辑层(App Service)b、逻辑层(App Service)处理
touchmove
事件,再通过 setData 来改变 B 的位置一次
touchmove
的响应需要经过 2 次的逻辑层和渲染层的通信以及一次渲染,通信的耗时比较大。此外 setData 渲染也会阻塞其它脚本执行,导致了整个用户交互的动画过程会有延迟。
另外,从CRP(标准渲染路径)来说,我们这里频繁地调用 setData来修改元素的 left
样式,会导致网页不断地进行 layout,style,composite操作,导致卡顿。而使用 transform
样式仅影响composite操作,同样可以做到元素的左移,这也是movable-view
的做法。
使用movable-view
的另一个好处
使用movable-view
不需要我们手动处理touchmove
事件来修改左滑元素的位置,也因此避免了多次setData
可能带来的卡顿。
这里给出使用 movable-view
的代码
js
Page({
data: {
x: wx.getSystemInfoSync().windowWidth / 750 * 200
},
})
wxml
<movable-area style="height: 300rpx; margin-bottom: 24rpx;width:{{showLike?'700rpx':'900rpx'}};position:relative;left: {{showLike?'25rpx':'-175rpx'}};" >
<movable-view
x="{{x}}"
class="scroll-view-item" style="width: 700rpx; height: 300rpx;"
direction="horizontal" out-of-bounds="true">
<view class="card-content" bind:tap="bindGroupTapInner">
<text class="group-title">侧滑删除</text>
</view>
<view class="button-edit button-slide" bindtap="bindGroupEditTap">
<i class="iconfont icon-editor"
style='font-size:58rpx; color:white; position:absolute; top:14rpx; left:16rpx;'/>
</view>
</movable-view>
</movable-area>
稍微要注意一点的是控制好movable-view和movable-area的宽度和位置,这里由于不同的需求要求不同,建议手动画图计算。
另外,movable-view内部帮我们解决了事件传递的问题,能够左滑的touchmove就不会传递给外层的元素,估计是使用了wxs函数吧,好像wxs中处理事件是可以控制事件冒泡的,但是笔者还未尝试,同时官方文档有些不足,可以看这里。