跟着 ahooks 写 Hook 之 useMouse

333 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第18天,点击查看活动详情

💻文档地址:ahooks.js.org/zh-CN/

👨‍💻github地址:github.com/alibaba/hoo…


来看一张图

20201218023647530.png

图片来源:blog.csdn.net/Janewenhui/…

utils 函数

useEventListener

对 eventListener 做一个封装。

入参有三个

  • eventName: string : 事件名称
  • handler: noop : 执行函数
  • options: listener 属性的可选参数对象
    • capture: 表示 listener 会在该类型的事件捕获阶段传播到该 EventTarget 时触发。
    • once: 表示 listener 在添加之后最多只调用一次。如果是 true, listener 会在其被调用之后自动移除。
    • passive: 设置为 true 时,表示 listener 永远不会调用 preventDefault()

useLatest(handler) 获取当前函数最新的 ref。判断当前 element 是否存在 addEventListener。监听 eventName,执行对应的函数。卸载时,移除 Listener

function useEventListener(eventName: string, handler: noop, options: Options = {}) {
  const handlerRef = useLatest(handler);

  useEffectWithTarget(
    () => {
      const targetElement = getTargetElement(options.target, window);
      if (!targetElement?.addEventListener) {
        return;
      }

      const eventListener = (event: Event) => {
        return handlerRef.current(event);
      };

      targetElement.addEventListener(eventName, eventListener, {
        capture: options.capture,
        once: options.once,
        passive: options.passive,
      });

      return () => {
        targetElement.removeEventListener(eventName, eventListener, {
          capture: options.capture,
        });
      };
    },
    [eventName, options.capture, options.once, options.passive],
    options.target,
  );
}

useMouse

监听 mousemove 事件。

初始化数据。

const initState: CursorState = {
  // 鼠标点击位置距离当前电脑屏幕X轴(左侧)的位置
  screenX: NaN,
  // 鼠标点击位置距离当前电脑屏幕Y轴(顶部)的位置
  screenY: NaN,
  // 鼠标相对于浏览器X轴(左侧)的位置
  clientX: NaN,
  // 鼠标相对于浏览器Y轴(顶部)的位置
  clientY: NaN,
  // 鼠标相对于浏览器X轴(左侧)的位置,包含页面横向滚动距离(被卷去的body部分的长度)
  pageX: NaN,
  // 鼠标相对于浏览器Y轴(顶部)的位置,包含页面横向滚动距离(被卷去的body部分的长度)
  pageY: NaN,
  elementX: NaN,
  elementY: NaN,
  elementH: NaN,
  elementW: NaN,
  elementPosX: NaN,
  elementPosY: NaN,
};

useMouse 无传入 target 的时候,监听整个页面的滚动。

export default (target?: BasicTarget) => {
  const [state, setState] = useRafState(initState);

  useEventListener(
    'mousemove',
    (event: MouseEvent) => {
      const { screenX, screenY, clientX, clientY, pageX, pageY } = event;
      const newState = {
        screenX,
        screenY,
        clientX,
        clientY,
        pageX,
        pageY,
        elementX: NaN,
        elementY: NaN,
        elementH: NaN,
        elementW: NaN,
        elementPosX: NaN,
        elementPosY: NaN,
      };
      setState(newState);
    },
    {
      target: () => document,
    },
  );

  return state;
};

当页面传入 target。使用 Element.getBoundingClientRect()  方法返回一个 DOMRect 对象,其提供了元素的大小及其相对于视口的位置。

图片.png

if (targetElement) {
  const { left, top, width, height } = targetElement.getBoundingClientRect();
  newState.elementPosX = left + window.pageXOffset;
  newState.elementPosY = top + window.pageYOffset;
  newState.elementX = pageX - newState.elementPosX;
  newState.elementY = pageY - newState.elementPosY;
  newState.elementW = width;
  newState.elementH = height;
}

Example

import React, { useRef } from 'react';
import { useMouse } from 'ahooks';

export default () => {
  const ref = useRef(null);
  const { elementX, elementY, elementPosX, elementPosY, elementW, elementH } = useMouse(ref.current);

  return (
    <>
      <div ref={ref}> element </div>
    </>
  );
};