小程序项目中遇到左滑删除的交互需求,体验了几个流行的小程序,各厂的解决方案有不同,也有类似。为一探究竟,自己动手实现了京东购物,印象笔记随时记的左滑交互。在文章的最后,给出了自己的方案,总结了文章中三种方案的优缺点。
方案一:使用scroll-view,代表:印象笔记随时记
原理
用scroll-view实现左滑删除交互的原理非常简单,设置一个支持横向滑动的scroll-view,同时将子组件的宽度设置为scroll-view的宽度加上删除按钮的宽度。由于子组件的宽度大于scroll-view的宽度,自然地,就可以进行横向滑动了。
代码
删除
.scroll-view-scroller {
width: 375px; //处于demo考虑 ,scroll-view宽度设为固定值,实际项目中请自行计算
}
.scroll-view-item {
width: 475px; // 300(scroller的宽度) + 100(delete按钮的宽度)
height: 100px;
background: blue;
position: relative;
}
.scroll-view-delete {
width: 100px; //按钮的宽度
position: absolute;
right: 0;
top: 0;
bottom: 0;
background: red;
color: #fff;
text-align: center;
line-height: 100px;
}
优点
- 实现成本低
- 兼容所有小程序版本
- 组件高度可设为自适应
缺点
- 当用户手指离开屏幕时,删除按钮不能自动隐藏或显示
- 有一个明显的横向滚动条
- 和原生的左滑体验差别较大
方案二: 检测左滑手势,代表:京东购物,每日优鲜
原理
通过touch事件,计算用户手指左向滑动的距离,当用户手指离开屏幕时,如果左向滑动的距离大于预设阀值,比如50像素,就打开左滑动画。
当我在体验京东购物和每日优鲜小程序上的左滑交互时,发现两者都存在一些细节上的缺失。比如没有将左滑限制为横扫,没有检测多点触控。这两点应该还是出于简化代码的考虑得吧。
代码
删除
.swipe-delete-wrapper {
height: 100px;
width: 100%;
position: relative;
}
.swipe-delte-btn {
position: absolute;
right: 0;
width: 100px;
top: 0;
text-align: center;
background: red;
bottom: 0;
line-height: 100px;
color: #fff;
}
.swipe-delete-content {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
background: blue;
z-index: 2;
transition: transform 200ms linear;
}
.swipe-delete-content.open {
transform: translateX(-100px);
}
onTouchStart(event) {
if (event.touches[0]) {
this.touchStartPosition = event.touches[0].clientX;
}
}
onTouchMove(event) {
if (event.touches[0]) {
this.touchEndPosition = event.touches[0].clientX;
}
}
closeSwipe() {
this.setData({
swiped: false
})
}
onTouchEnd() {
if (this.touchStartPosition && this.touchEndPosition) {
let delta = this.touchStartPosition - this.touchEndPosition;
//左滑距离大于阀值,显示删除按钮
if(delta > 50) {
this.setData({
swiped: true
})
}
//右滑距离小于阀值,隐藏删除按钮
if(delta < -50) {
this.setData({
swiped: false
})
}
}
this.touchStartPosition = null;
this.touchEndPosition = null;
}
优点
- 兼容所有小程序版本
- 体验好于scroll-view
- 组件高度可设为自适应
缺点
- 滑动不跟手
- 和原生的左滑体验差别较大
方案三:使用movable-view
原理
从1.2.0开始,小程序提供了movable-view,配合movable-area,可以实现非常流畅的滑动效果。
首先,我们需要确定组件的宽度和删除按钮的宽度,组件的宽度就是movable-view的宽度,而movable-area的宽度就是组件的宽度减去删除按钮的宽度。这样,我们就可以在限定的范围内滑动了,当超过最大滑动距离时,movable-view还会自动提供一个非常流畅的过界动画和回弹效果,媲美原生。
代码
删除
.moveable-item {
display: flex;
flex-direction: row;
position: relative;
align-items: center;
background: red;
}
.movable-wrapper {
min-height: 100px;
background: #ddd;
flex: 1;
position: relative;
}
.movable-item {
position: relative;
height: 100px;
width: 375px;
background: blue;
flex:1
}
.move-able-delete-section {
width: 100px;
height: 100%;
text-align: center;
color: #fff;
}
onTouchStart(event) {
if(event.touches[0]){
this.touchStartPosition = event.touches[0].clientX;
}
}
onTouchMove(event) {
if (event.touches[0]) {
this.touchEndPosition = event.touches[0].clientX;
}
}
onTouchEnd(event) {
if (this.touchStartPosition && this.touchEndPosition) {
console.log(this.touchStartPosition - this.touchEndPosition)
let delta = this.touchStartPosition - this.touchEndPosition;
//left
if(delta > 0) {
if(delta> 40) {
this.setData({
x: -100
})
}else {
this.setData({
x: 0
})
}
}else {
//right
if (Math.abs(delta) > 40) {
this.setData({
x: 0
})
}else {
this.setData({
x: -100
})
}
}
this.touchStartPosition = null;
this.touchEndPosition = null;
}
}
优点
- 动画流畅,操作跟手
- 媲美原生左滑交互
缺点
- 组件需要固定高度
总结
方案一作为小程序早期的解决方案,目前不再推荐。
方案二由于无需限制组件的高度,具有很大的普适性。如果你的小程序中组件高度需要自适应,那么推荐你使用方案二。
如果你的小程序中组件的高度是固定的,那么方案三无疑是最优的方案,你可以提供给用户媲美原生的左滑体验。