首先,需求是在手机端实现一个左右滑动的tab的功能。大概的思路是:
- 1.判断手指滑动方向
- 2.然后通过 transform 滚动div。
这里使用的是Vant-ui的swipe组件来实现功能。
<van-swipe :autoplay="false" indicator-color="white">
<van-swipe-item v-for="cardItem in cardList" :key="cardItem.id">
<div>
卡片内容1
</div>
</van-swipe-item>
</van-swipe>
Vant3 源码位置packages/vant/src/swipe/Swipe.tsx. 用的是jsx语法。注意,这里用了render 方式,就自动忽视模板方式
return () => (
<div ref={root} class={bem()}>
<div
style={trackStyle.value}
class={bem('track', { vertical: props.vertical })}
onTouchstart={onTouchStart}
onTouchmove={onTouchMove}
onTouchend={onTouchEnd}
onTouchcancel={onTouchEnd}
>
{slots.default?.()}
</div>
{renderIndicator()}
</div>
);
其中 bem() 来创建类命( van-swipe__track van-swipe-item 等这样的格式)
- Bem 是块(block)、元素(element)、修饰符(modifier)的简写
-中划线 :仅作为连字符使用,表示某个块或者某个子元素的多单词之间的连接记号。
__ 双下划线:双下划线用来连接块和块的子元素
_ 单下划线:单下划线用来描述一个块或者块的子元素的一种状态
trackStyle 用来实现div的滚动,通过transform滚动div
const trackStyle = computed(() => {
const style: CSSProperties = {
transitionDuration: `${state.swiping ? 0 : props.duration}ms`,
transform: `translate${props.vertical ? 'Y' : 'X'}(${state.offset}px)`,
};
if (size.value) {
const mainAxis = props.vertical ? 'height' : 'width';
const crossAxis = props.vertical ? 'width' : 'height';
style[mainAxis] = `${trackSize.value}px`;
style[crossAxis] = props[crossAxis] ? `${props[crossAxis]}px` : '';
}
return style;
});
slots.default 用来以编程方式访问通过插槽分发的内容
下面是滑动相关的
touch / touchEvent 相关(参考mdn)
Touch
Touch 对象表示在触控设备上的触摸点。通常是指手指或者触控笔在触屏设备或者触摸板上的操作。
-
触点相对于可见视区(visual viewport)左边缘的X坐标。不包括任何滚动偏移。
-
触点相对于可见视区(visual viewport)上边缘的Y坐标。不包括任何滚动偏移。
TouchEvent
touchstart(详情点我)
大白话来说,记录手指开始滑动时,反馈坐标等。
touchend (详情点我)
大白话来说,记录手指停止滑动时,反馈坐标等。
touchmove (详情点我)
大白话来说,记录手指滑动时,反馈坐标等。
``
import { useTouch } from '../composables/use-touch';
...
// hook的方式引用 touch相关
const touch = useTouch();
...
代码有删减
const onTouchStart = (event: TouchEvent) => {
...
touch.start(event);
touchStartTime = Date.now();
console.log(touchStartTime)
...
};
const onTouchMove = (event: TouchEvent) => {
// console.log('onTouchMove-->',props)
if (props.touchable && state.swiping) {
touch.move(event);
if (isCorrectDirection.value) {
preventDefault(event, props.stopPropagation);
move({ offset: delta.value });
}
}
};
const onTouchEnd = () => {
...
const duration = Date.now() - touchStartTime;
const speed = delta.value / duration;
// 判断是否需要滑动
const shouldSwipe =
Math.abs(speed) > 0.25 || Math.abs(delta.value) > size.value / 2;
if (shouldSwipe && isCorrectDirection.value) {
const offset = props.vertical
? touch.offsetY.value
: touch.offsetX.value;
let pace = 0;
if (props.loop) {
pace = offset > 0 ? (delta.value > 0 ? -1 : 1) : 0;
} else {
pace = -Math[delta.value > 0 ? 'ceil' : 'floor'](
delta.value / size.value
);
}
move({
pace,
emitChange: true,
});
} else if (delta.value) {
move({ pace: 0 });
}
state.swiping = false;
autoplay();
};
总结一下
记录手指滑动的坐标,判断滑动的方向,通过transform滚动div。