• 大家都能看得懂的源码 - 那些关于DOM的常见Hook封装(二)


    本文是深入浅出 ahooks 源码系列文章的第十五篇,该系列已整理成文档-地址。觉得还不错,给个 star 支持一下哈,Thanks。

    本篇接着针对关于 DOM 的各个 Hook 封装进行解读。

    useFullscreen

    管理 DOM 全屏的 Hook。

    该 hook 主要是依赖 screenfull 这个 npm 包进行实现的。

    选择它的原因,估计有两个:

    • 它的兼容性好,兼容各个浏览器的全屏 API。
    • 简单,包体积小。压缩后只要 1.1 k。

    大概介绍几个它的 API。

    • .request(element, options?)。使一个元素全屏显示。默认元素是
    • .exit()。退出全屏。
    • .toggle(element, options?)。假如目前是全屏,则退出,否则进入全屏。
    • .on(event, function)。添加一个监听器,用于当浏览器切换到全屏或切换出全屏或出现错误时。event 支持 'change' 或者 'error'。另外两种写法:.onchange(function).onerror(function)
    • .isFullscreen。判断是否是全屏。
    • .isEnabled。判断当前环境是否支持全屏。

    来看该 hook 的封装:

    首先是 onChange 事件中,判断是否是全屏,从而触发进入全屏的函数或者退出全屏的函数。
    当退出全屏的时候,卸载 change 事件。

    const { onExit, onEnter } = options || {};
    // 退出全屏触发
    const onExitRef = useLatest(onExit);
    // 全屏触发
    const onEnterRef = useLatest(onEnter);
    const [state, setState] = useState(false);
    
    const onChange = () => {
      if (screenfull.isEnabled) {
        const { isFullscreen } = screenfull;
        if (isFullscreen) {
          onEnterRef.current?.();
        } else {
          screenfull.off('change', onChange);
          onExitRef.current?.();
        }
        setState(isFullscreen);
      }
    };
    

    手动进入全屏函数,支持传入 ref 设置需要全屏的元素。并通过 screenfull.request 进行设置,并监听 change 事件。

    // 进入全屏
    const enterFullscreen = () => {
      const el = getTargetElement(target);
      if (!el) {
        return;
      }
    
      if (screenfull.isEnabled) {
        try {
          screenfull.request(el);
          screenfull.on('change', onChange);
        } catch (error) {
          console.error(error);
        }
      }
    };
    

    退出全屏方法,调用 screenfull.exit()

    // 退出全屏
    const exitFullscreen = () => {
      if (!state) {
        return;
      }
      if (screenfull.isEnabled) {
        screenfull.exit();
      }
    };
    

    最后通过 toggleFullscreen,根据当前状态,调用上面两个方法,达到切换全屏状态的效果。

    // 切换模式
    const toggleFullscreen = () => {
      if (state) {
        exitFullscreen();
      } else {
        enterFullscreen();
      }
    };
    

    useHover

    监听 DOM 元素是否有鼠标悬停。

    主要实现原理是监听 mouseenter 触发 onEnter 事件,切换状态为 true,监听 mouseleave 触发 onLeave 事件,切换状态为 false。代码简单,如下:

    export default (target: BasicTarget, options?: Options): boolean => {
      const { onEnter, onLeave } = options || {};
      const [state, { setTrue, setFalse }] = useBoolean(false);
      // 通过监听 mouseenter 判断有鼠标悬停
      useEventListener(
        'mouseenter',
        () => {
          onEnter?.();
          setTrue();
        },
        {
          target,
        },
      );
    
      // mouseleave 没有鼠标悬停
      useEventListener(
        'mouseleave',
        () => {
          onLeave?.();
          setFalse();
        },
        {
          target,
        },
      );
    
      return state;
    };
    

    useDocumentVisibility

    监听页面是否可见。

    这个 hook 主要使用了 Document.visibilityState 这个 API。先简单看下这个 API:

    Document.visibilityState (只读属性), 返回document的可见性, 即当前可见元素的上下文环境。由此可以知道当前文档 (即为页面) 是在背后, 或是不可见的隐藏的标签页,或者 (正在) 预渲染。可用的值如下:

    • 'visible' : 此时页面内容至少是部分可见. 即此页面在前景标签页中,并且窗口没有最小化。
    • 'hidden' : 此时页面对用户不可见。即文档处于背景标签页或者窗口处于最小化状态,或者操作系统正处于 '锁屏状态' 。
    • 'prerender' : 页面此时正在渲染中,因此是不可见的。文档只能从此状态开始,永远不能从其他值变为此状态。

    典型用法是防止当页面正在渲染时加载资源,或者当页面在背景中或窗口最小化时禁止某些活动。

    最后看这个 hook 的实现就很简单了:

    • 通过 document.visibilityState 判断是否可见。
    • 通过 visibilitychange 事件,更新结果。
    const getVisibility = () => {
      if (!isBrowser) {
        return 'visible';
      }
      //  Document.visibilityState (只读属性), 返回document的可见性, 即当前可见元素的上下文环境。
      return document.visibilityState;
    };
    
    function useDocumentVisibility(): VisibilityState {
      const [documentVisibility, setDocumentVisibility] = useState(() => getVisibility());
    
      useEventListener(
        // 监听该事件
        'visibilitychange',
        () => {
          setDocumentVisibility(getVisibility());
        },
        {
          target: () => document,
        },
      );
      return documentVisibility;
    }
    

    本文已收录到个人博客中,欢迎关注~

  • 相关阅读:
    Java笔记 实用类(二)
    Halcon 常用通道Scale灰度元操作整理
    C#/VB.NET:快速而简单的免费SVG到PDF转换技巧
    电话呼入呼出场景下是否需要做DDS切换
    ESP8266-Arduino编程实例-VEML7700光照度传感器驱动
    Opncv 实现拍照、颜色识别和阈值选取
    【计算机网络】网络编程接口 Socket API 解读(5)
    21天学Python --- 打卡7:Spider爬虫入门
    Zotero(3)---使用茉莉花插件提取中文文献信息
    Java 网络编程
  • 原文地址:https://www.cnblogs.com/gopal/p/16638912.html