前言
传统的小程序左滑做法是利用touchmove来实时的更新滑动的距离,这让小程序在性能上大打折扣,而且还时常出现卡顿不流畅的现象,小程序新出的movable-area + movable-view 可以完美的解决这个问题。 话不多说,直接上地址:源码地址
动图效果
利用setData的实现
wxml 文件
<view class='item'>
<view bind:touchstart="b_touchS" bind:touchmove="b_touchM">
<view class='carGoods' catch:touchmove="{{c_touchM}}" bind:touchend="c_touchE" data-index="{{good.id}}" style="{{good.txtStyle}}">
<view class='left'>
<view class='greenCheck {{good.checked?"active":""}}'><image src='/images/select.png' mode='aspectFit'></image></view>
<view class="goodsBg">
<image src='{{good.sku.image}}' mode='widthFix'></image>
</view>
<view class='product'>
<view class='title'>{{good.sku.name}}</view>
<view class='format'>{{good.sku.tag}}</view>
<view class='down'>
<view class='price'>¥{{good.sku.price}}</view>
<view class="num_counter">
<image class="reduce" data-id="{{good.id}}" data-num="{{good.nums}}" src="/images/reduce.png" mode="widthFix"></image>
<view class="num">{{good.nums}}</view>
<image class="add" data-id="{{good.id}}" data-num="{{good.nums}}" src="/images/add.png" mode="widthFix"></image>
</view>
</view>
</view>
</view>
</view>
</view>
<view class="inner">删除</view>
</view>
js 文件
// components/good/good.js
var INITX = 30;//触发条件的X移动距离
var MAXY = 30;//触发条件
Component({
/**
* 组件的属性列表
*/
properties: {
good: Object
},
created: function () {
this.initEleWidth();
},
/**
* 组件的初始数据
*/
data: {
delBtnWidth: 160,
firstX: -1,
c_touchM: ''
},
/**
* 组件的方法列表
*/
methods: {
b_touchS: function (e) {
if (e.touches.length == 1) {
this.setData({
//设置触摸起始点水平方向位置
startX: e.touches[0].clientX,
// 设置触摸起始点垂直位置
startY: e.touches[0].clientY,
//初始化x计算起点
firstX: -1,
c_touchM: ''
});
}
},
b_touchM: function (e) {
if (e.touches.length == 1) {
// 如果当商品没有滑动删除按钮时,向右滑动失效。
const { txtStyle } = this.data.good
//手指移动时水平方向位置
var moveX = e.touches[0].clientX;
// 初始化firstX已经被拉开时
if (this.data.firstX < 0) {
var moveY = e.touches[0].clientY
// 计算开始和结束的两个点的X和Y的差值
var triggerX = moveX - this.data.startX
var triggerY = moveY - this.data.startY
console.log(triggerX)
if (Math.abs(triggerX) >= INITX && Math.abs(triggerY) < MAXY) {
if (txtStyle == '' || txtStyle == 'left:0px') {
if (triggerX > 0) {
return false
}
}
this.setData({
firstX: moveX,
c_touchM: 'c_touchM'
})
} else {
return
}
}
//手指起始点位置与移动期间的差值
}
},
c_touchM: function (e) {
if (e.touches.length == 1) {
//手指移动时水平方向位置
var moveX = e.touches[0].clientX;
//手指触发点位置与移动期间的差值
var disX = this.data.firstX - moveX
var delBtnWidth = this.data.delBtnWidth;
var txtStyle = "";
if (disX > 0) {
txtStyle = `left: ${-disX}px`;
} else {
txtStyle = `left: ${-delBtnWidth - disX}px`;
}
if (disX < 0 && disX <= -delBtnWidth) {//如果移动距离小于等于0,文本层位置不变
txtStyle = "left:0px";
} else if (disX > 0 && disX >= delBtnWidth) {//移动距离大于0,文本层left值等于手指移动距离
//控制手指移动距离最大值为删除按钮的宽度
txtStyle = "left:-" + delBtnWidth + "px";
}
//获取手指触摸的是哪一项
var index = e.currentTarget.dataset.index;
var good = this.data.good;
if (index) {
good.txtStyle = txtStyle;
//更新列表的状态
this.setData({
good: good
});
}
}
},
c_touchE: function (e) {
if (e.changedTouches.length == 1) {
//手指移动结束后水平位置
var endX = e.changedTouches[0].clientX;
//触摸开始与结束,手指移动的距离
var disX = this.data.startX - endX;
var delBtnWidth = this.data.delBtnWidth;
// console.log(disX, delBtnWidth)
//如果距离小于删除按钮的1/2,不显示删除按钮
var txtStyle = disX / 2 > delBtnWidth / 2 ? "left:-" + delBtnWidth + "px" : "left:0px";
//获取手指触摸的是哪一项
var index = e.target.dataset.index;
var good = this.data.good;
if (index) {
good.txtStyle = txtStyle;
//更新列表的状态
this.setData({
good: good
});
}
}
},
//获取元素自适应后的实际宽度
getEleWidth: function (w) {
var real = 0;
try {
var res = wx.getSystemInfoSync().windowWidth;
//以宽度750px设计稿做宽度的自适应
var scale = (750 / 2) / (w / 2);
real = Math.floor(res / scale);
return real;
} catch (e) {
return false;
// Do something when catch error
}
},
initEleWidth: function () {
var delBtnWidth = this.getEleWidth(this.data.delBtnWidth);
this.setData({
delBtnWidth: delBtnWidth
});
},
}
})
利用movable-area + movable-view的实现
wxml 文件
<view class='item'>
<movable-area>
<movable-view class="carGoods" out-of-bounds="true" direction="horizontal" x="{{good.xmove}}"
data-index="{{good.id}}"
inertia="true"
bindtouchstart="handleTouchStart"
bindtouchend="handleTouchEnd"
>
<view class='left'>
<view class='greenCheck {{good.checked?"active":""}}'><image src='/images/select.png' mode='aspectFit'></image></view>
<view class="goodsBg">
<image src='{{good.sku.image}}' mode='widthFix'></image>
</view>
<view class='product'>
<view class='title'>{{good.sku.name}}</view>
<view class='format'>{{good.sku.tag}}</view>
<view class='down'>
<view class='price'>¥{{good.sku.price}}</view>
<view class="num_counter">
<image class="reduce" data-id="{{good.id}}" data-num="{{good.nums}}" src="/images/reduce.png" mode="widthFix"></image>
<view class="num">{{good.nums}}</view>
<image class="add" data-id="{{good.id}}" data-num="{{good.nums}}" src="/images/add.png" mode="widthFix"></image>
</view>
</view>
</view>
</view>
</movable-view>
</movable-area>
<view class="inner">删除</view>
</view>
js 文件
// components/good2/good2.js
var INITX = 30
var MAXY = 30
Component({
/**
* 组件的属性列表
*/
properties: {
good: Object
},
/**
* 组件的初始数据
*/
data: {
},
/**
* 组件的方法列表
*/
methods: {
/**
* 显示删除按钮
*/
showDeleteButton: function (e) {
console.log(e)
let index = e.currentTarget.dataset.index
this.setXmove(index, -80)
},
/**
* 隐藏删除按钮
*/
hideDeleteButton: function (e) {
let index = e.currentTarget.dataset.index
this.setXmove(index, 0)
},
/**
* 设置movable-view位移
*/
setXmove: function (index, xmove) {
let good = this.data.good
console.log(index)
if (index) {
good.xmove = xmove;
//更新列表的状态
this.setData({
good: good
});
}
},
/**
* 设置movavle-view的开始位置
*/
handleTouchStart(e) {
this.startX = e.touches[0].pageX
this.startY = e.touches[0].pageY
},
/**
* 设置movavle-view的结束位置
*/
handleTouchEnd(e) {
// console.log(e)
// console.log(e.changedTouches[0].pageX)
const endX = e.changedTouches[0].pageX
const endY = e.changedTouches[0].pageY
const triggerX = endX - this.startX
const triggerY = endY - this.startY
// console.log(this.startX)
if (Math.abs(triggerX) > INITX && Math.abs(triggerY) < MAXY) {
console.log(triggerY)
if (endX < this.startX && endX - this.startX <= -40) {
this.showDeleteButton(e)
} else if (endX > this.startX && endX - this.startX < 40) {
this.showDeleteButton(e)
} else {
this.hideDeleteButton(e)
}
}
},
}
})
结尾
传统的左滑删除的用户体验确实不好,小程序官方出的这movable-view确实是好用,不仅不卡,还有动画效果,如果有需要的可以试试这个方案。