基于taro和react封装一个兼容pc,移动端和小程序的触摸钩子

我正在参加「掘金·启航计划」

使用场景

使用 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 中读取到 xy 的各种偏移值,具体就不一一叙述了,可以看下方的介绍,或者到 useTouch 钩子中查看类型的定义。

钩子的封装

简单来说其实就是区分是否是移动端,然后对触摸或鼠标事件进行区分,再将这些函数分别解构到对应的dom上,作为事件的绑定。

一开始我也想学习 ahooksuseDrap 钩子那样通过传递一个 ref 来绑定事件

ref.current.addEventListener('mousemove', onTouchMove)

但是后来发现这招在小程序行不通,而且小程序也没有 document 那样的东西,小程序绑定事件好像只能通过在 dom 中通过事件名来进行绑定。所以就这样封装了。

钩子的最原始结构

定义好 onTouchStart, onTouchMove, onTouchEnd 三个函数,作为触摸开始,移动和结束的事件。

再根据是否是pc端,再为 document 添加或移除 mousemovemouseup 事件。

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;
};

代码

由于代码较多,就不占篇幅,放码上掘金了。

原文链接:https://juejin.cn/post/7244808577209073725 作者:滑动变滚动的蜗牛

(0)
上一篇 2023年6月16日 上午10:00
下一篇 2023年6月16日 上午10:10

相关推荐

发表回复

登录后才能评论