前言
在使用钉钉自己的mini-ddui
时,发现swipe-action组件有个坑,swipe-action
左右滑动时无法禁止组件本身被点击,导致滑动时如果组件内元素可以点击容易误操作用户体验很差.
在查看了官方文档后,发现并没有给出什么解决办法,so…只能把官方库的组件改了一下
思路
1.给swipe-action
组件增加一个onSwipeEnd
的钩子
2.当手放开时判断当前swipe状态,如果在swipe,则点击触发无效
swipe-action 修改版 完整组件代码
东西不是特别多不想放到git上了
swipe-action.acss
.am-swipe {
position: relative;
overflow: hidden;
}
.am-swipe-content {
position: relative;
transition: all 250ms;
}
.am-swipe-right {
position: absolute;
top: 0;
bottom: 0;
display: flex;
right: 0;
}
.am-swipe-btn {
padding: 0 14px;
justify-content: center;
align-items: center;
display: flex;
color: #fff;
}
.am-swipe-btn-delete {
background-color: rgb(244, 51, 60);
}
.am-swipe-btn-edit {
background-color: #108ee9;
} //=== v2 ===
.am-swipe-movable {}
.am-swipe-area {
position: absolute;
}
.am-swipe-area .am-swipe-content {
transition: none;
}
.am-swiping .am-swipe-content {
transition: none;
}
swipe-action.axml
<view a:if={{!useV2}} class="am-swipe {{swiping ? 'am-swiping' : ''}} {{className}}">
<block a:if="{{right.length > 0}}">
<view class="am-swipe-right am-swipe-right-{{$id}}">
<block a:for="{{right}}">
<view
class="am-swipe-btn am-swipe-btn-{{item.type}}"
onTap="onItemClick"
data-index="{{index}}"
key="swipe-item-{{index}}"
>
{{item.text}}
</view>
</block>
</view>
</block>
<view
class="am-swipe-content"
onTap="onSwipeTap"
onTouchStart="onSwipeStart"
onTouchEnd="onSwipeEnd"
catchTouchMove="onSwipeMove"
data-type="content"
style="-webkit-transform: translateX({{leftPos}}px); transform: translateX({{leftPos}}px);"
>
<slot />
</view>
</view>
<view
class="am-swipe {{className}}"
style="width: {{viewWidth}}px; height: {{height}}px;"
key="{{key}}"
a:else
>
<movable-area
class="am-swipe-movable-area"
style="width: {{ viewWidth + actionWidth}}px;height: {{height}}px;left: -{{actionWidth}}px;"
>
<block a:if="{{right.length > 0}}">
<view class="am-swipe-right am-swipe-right-{{$id}}" style="right: 0;">
<block a:for="{{right}}">
<view
class="am-swipe-btn am-swipe-btn-{{item.type}}"
onTap="onItemClick"
data-index="{{index}}"
key="swipe-item-{{index}}"
>
{{item.text}}
</view>
</block>
</view>
</block>
<movable-view
class="am-swipe-content"
onTap="onSwipeTap"
onTouchStart="onSwipeStart"
direction="horizontal"
data-type="content"
style="width: {{viewWidth}}px;height:{{height}}px; transition: {{transitionVal}};"
x="{{x}}"
onChange="onChange"
onChangeEnd="onChangeEnd"
>
<slot />
</movable-view>
</movable-area>
</view>
swipe-action.js
const { windowWidth } = dd.getSystemInfoSync();
const isV2 = dd.canIUse('movable-view.onTouchStart');
Component({
data: {
leftPos: 0,
swiping: false,
holdSwipe: true,
viewWidth: windowWidth,
x: 0,
actionWidth: 0,
transitionVal: 'none',
},
props: {
className: '',
right: [],
restore: false,
index: null,
height: 52,
enableNew: false,
},
didMount() {
const { enableNew } = this.props;
const useV2 = isV2 && enableNew;
this.btnWidth = 0;
this.setData({
useV2,
});
this.setBtnWidth();
if (useV2) {
setTimeout(() => {
this.setData({
transitionVal: 'transform 100ms',
});
}, 500);
}
},
didUpdate(_prevProps, prevData) {
const { restore } = this.props;
const { holdSwipe, useV2 } = this.data;
if ((restore === true && _prevProps.restore !== restore) ||
(prevData.holdSwipe === true && holdSwipe === false)) {
this.setData({
leftPos: 0,
swiping: false,
x: this.btnWidth, // V2
});
}
if (!useV2) {
this.setBtnWidth();
}
},
methods: {
setBtnWidth() {
my.createSelectorQuery()
.select(`.am-swipe-right-${this.$id}`)
.boundingClientRect()
.exec((ret) => {
this.btnWidth = (ret && ret[0] && ret[0].width) || 0;
if (isV2 && this.props.enableNew) {
this.setData({
actionWidth: this.btnWidth,
x: this.btnWidth,
});
}
});
},
onSwipeTap() {
if (!this.data.swiping) {
this.setData({
leftPos: 0,
swiping: false,
x: 0,
});
}
},
onSwipeStart(e) {
this.touchObject = {
startX: e.touches[0].pageX,
startY: e.touches[0].pageY,
};
const { index, onSwipeStart } = this.props;
if (onSwipeStart) {
onSwipeStart({ index });
}
},
onSwipeMove(e) {
const { touchObject } = this;
const touchePoint = e.touches[0];
const { leftPos } = this.data;
touchObject.endX = touchePoint.pageX;
// 首次触发时,计算滑动角度
if (touchObject.direction === undefined) {
let direction = 0;
const xDist = touchObject.startX - touchePoint.pageX || 0;
const yDist = touchObject.startY - touchePoint.pageY || 0;
const r = Math.atan2(yDist, xDist);
let swipeAngle = Math.round((r * 180) / Math.PI);
if (swipeAngle < 0) {
swipeAngle = 360 - Math.abs(swipeAngle);
}
if (swipeAngle <= 45 && swipeAngle >= 0) {
direction = 1;
}
if (swipeAngle <= 360 && swipeAngle >= 315) {
direction = 1;
}
if (swipeAngle >= 135 && swipeAngle <= 225) {
direction = -1;
}
touchObject.direction = direction;
}
// 通过角度判断是左右方向
if (touchObject.direction !== 0) {
let newLeftPos = leftPos;
// 滑动距离
const distance = touchObject.endX - touchObject.startX;
// 左划
if (distance < 0) {
newLeftPos = Math.max(distance, -this.btnWidth);
// 右划
} else {
newLeftPos = 0;
}
if (Math.abs(distance) > 10) {
this.setData({
leftPos: newLeftPos,
swiping: distance < 0,
});
}
}
},
onSwipeEnd(e) {
const { touchObject } = this;
if (touchObject.direction !== 0) {
const touchePoint = e.changedTouches[0];
touchObject.endX = touchePoint.pageX;
const { leftPos } = this.data;
const distance = touchObject.endX - touchObject.startX;
let newLeftPos = leftPos;
if (distance < 0) {
if (Math.abs(distance + leftPos) > this.btnWidth * 0.7) {
newLeftPos = (-this.btnWidth);
} else {
newLeftPos = 0;
}
}
this.setData({
leftPos: newLeftPos,
swiping: false,
});
const { index, onSwipeEnd } = this.props;
if (onSwipeEnd) {
onSwipeEnd({index, newLeftPos });
}
}
},
onChange() {
if (!this.data.swiping) {
this.setData({
swiping: true,
});
}
},
onChangeEnd(e) {
const { actionWidth } = this.data;
const { x } = e.detail;
this.setData({
x: x < actionWidth / 2 ? -1 : actionWidth - 1,
swiping: false,
}, () => {
this.setData({
x: this.data.x === -1 ? 0 : actionWidth,
});
});
},
done() {
this.setData({
holdSwipe: true,
}, () => {
this.setData({
holdSwipe: false,
});
});
},
onItemClick(e) {
const { onRightItemClick } = this.props;
const { holdSwipe } = this.data;
if (onRightItemClick) {
const { index } = e.target.dataset;
onRightItemClick({
index,
extra: this.props.extra,
detail: this.props.right[index],
done: this.done.bind(this),
});
}
if (!this.data.swiping && holdSwipe === false) {
setTimeout(() => {
this.setData({
leftPos: 0,
swiping: false,
x: 0,
});
}, 300);
}
},
},
});
组件与scroll-view配合的使用
<!--这里是伪代码,非相关代码请自行根据官方文档补充,这里只展示相关代码-->
<swipe-action className="card-swipe-item" onSwipeStart="onSwipeStart" onSwipeEnd="onSwipeEnd" extra="{{item}}">
<view onTap="goInfo"></view>
</swipe-action>
...
Page({
data:{
isSwiping:false
},
onSwipeStart(e) {
console.log('swipe start', e)
},
onSwipeEnd(e) {
this.setData({
isSwiping: e.newLeftPos === 0 ? false : true
});
console.log('swipe end', e.index, e.newLeftPos)
},
goInfo(e) {
if (this.data.isSwiping) {
return false;
}
//do something....
},
})
后记
onSwipeEnd
这个钩子还是很有用的,可以结合scroll-view控制侧滑开后不能滚动等等…