我正在参加「掘金·启航计划」
使用场景
使用 Taro
开发下的小程序和h5页面
useTouchEvent
简单使用
import { View } from '@tarojs/components';
import React, { useState } from 'react';
import useTouchEvent from '..';
import './index.less';
const Demo: React.FC = () => {
const [dom, setDom] = useState({
x: 0,
y: 0,
});
const { info, onTouchFn } = useTouchEvent({
onTouchStart() {},
onTouchMove() {
setDom({ x: info.x, y: info.y });
},
onTouchEnd() {},
});
return (
<View className="demo-use-touches">
<View
className="ball"
style={{
transform: `translate(${dom.x}px, ${dom.y}px)`,
}}
{...onTouchFn}
>
小球
</View>
</View>
);
};
export default Demo;
根据上面的使用方式可以看到,根据需求将触摸开始,移动和结束三个事件分别传进去。
const { info, onTouchFn } = useTouchEvent({
onTouchStart() {},
onTouchMove() {},
onTouchEnd() {},
});
再将传递出来的 onTouchFn
解构到 dom
元素中即可。
<View {...onTouchFn}></View>
可以从 info
中读取到 x
和 y
的各种偏移值,具体就不一一叙述了,可以看下方的介绍,或者到 useTouch
钩子中查看类型的定义。
钩子的封装
简单来说其实就是区分是否是移动端,然后对触摸或鼠标事件进行区分,再将这些函数分别解构到对应的dom上,作为事件的绑定。
一开始我也想学习 ahooks
的 useDrap
钩子那样通过传递一个 ref
来绑定事件
ref.current.addEventListener('mousemove', onTouchMove)
但是后来发现这招在小程序行不通,而且小程序也没有 document
那样的东西,小程序绑定事件好像只能通过在 dom
中通过事件名来进行绑定。所以就这样封装了。
钩子的最原始结构
定义好 onTouchStart
, onTouchMove
, onTouchEnd
三个函数,作为触摸开始,移动和结束的事件。
再根据是否是pc端,再为 document
添加或移除 mousemove
和 mouseup
事件。
export default function useTouchEvent(options: UseTouchEventParams = {}) {
const touch = useTouch();
const optionsRef = useLatest(options);
const onTouchStart = (e: MouseTouchEvent) => {
touch.start(e);
if (!isMobile()) {
document.addEventListener('mousemove', onTouchMove, true);
document.addEventListener('mouseup', onTouchEnd, true);
}
optionsRef.current.onTouchStart?.(e);
};
const onTouchMove = (e: MouseTouchEvent) => {
touch.move(e);
optionsRef.current.onTouchMove?.(e, touch.info);
};
const onTouchEnd = (e: MouseTouchEvent) => {
touch.move(e);
if (!isMobile()) {
document.removeEventListener('mousemove', onTouchMove, true);
document.removeEventListener('mouseup', onTouchEnd, true);
}
optionsRef.current.onTouchEnd?.(e);
};
return {
...touch,
onTouchFn: onTouchMouse({
onTouchStart,
onTouchMove,
onTouchEnd,
}),
};
}
再通过 onTouchMouse
区分移动端和pc下不同的事件监听处理。通过 onTouchFn
将区分好的函数解构到对应的 dom
节点上即可。
/** 处理鼠标或手指触摸事件 */
export const onTouchMouse = ({
onTouchStart,
onTouchMove,
onTouchEnd,
}: UseTouchesOptions & IsTouchEvent) => {
if (!isMobile()) {
return {
onMouseDown: onTouchStart,
};
} else {
return {
onTouchStart: onTouchStart,
onTouchMove: onTouchMove,
onTouchEnd: onTouchEnd,
};
}
};
info 中包含的信息
export type TouchState = {
/** x的起始的位置 */
startX: number;
/** y的起始的位置 */
startY: number;
/** x的偏移量 */
deltaX: number;
/** y的偏移量 */
deltaY: number;
/** x的位移 正数 */
offsetX: number;
/** y的位移 正数 */
offsetY: number;
/** 当前移动的方向 */
direction: TouchDirection;
/** 触摸开始到结束的时间 */
time: number;
/** 起始的由始至终的x值 */
preX: number;
/** 起始的由始至终的y值 */
preY: number;
/** 由始至终的x值 */
x: number;
/** 由始至终的y值 */
y: number;
};
代码
由于代码较多,就不占篇幅,放码上掘金了。