resize-observer源码解读

news2024/11/27 4:37:20

resize-observer

github 地址:https://github.com/devrelm/resize-observer

本地启动

npm install

npm start

node 18.16.0 (npm 9.5.1) 启动失败报错

node:internal/crypto/hash:71
  this[kHandle] = new _Hash(algorithm, xofLen);
                  ^

Error: error:0308010C:digital envelope routines::unsupported

解决:更改 node 版本

node 16.16.0 (npm 8.11.0) 启动成功

在这里插入图片描述

使用示例

const onResize: ResizeObserverProps["onResize"] = ({
  width,
  height,
  offsetHeight,
  offsetWidth,
}) => {
  setTimes((prevTimes) => prevTimes + 1);
  console.log(
    "Resize:",
    "\n",
    "BoundingBox",
    width,
    height,
    "\n",
    "Offset",
    offsetWidth,
    offsetHeight
  );
};

<ResizeObserver onResize={onResize} disabled={disabled}>
  <Wrapper>
    <textarea ref={textareaRef} placeholder="I'm a textarea!" />
  </Wrapper>
</ResizeObserver>;
export type OnResize = (size: SizeInfo, element: HTMLElement) => void;

export interface ResizeObserverProps {
  /** Pass to ResizeObserver.Collection with additional data */
  data?: any;
  children:
    | React.ReactNode
    | ((ref: React.RefObject<any>) => React.ReactElement);
  disabled?: boolean;
  /** Trigger if element resized. Will always trigger when first time render. */
  onResize?: OnResize;
}

ResizeObserve 組件

真正组件在ResizeObserver组件

const RefResizeObserver = React.forwardRef(ResizeObserver) as React.ForwardRefExoticComponent<
  React.PropsWithoutRef<ResizeObserverProps> & React.RefAttributes<any>
> & {
  Collection: typeof Collection;
};

RefResizeObserver.Collection = Collection;

export default RefResizeObserver;

ResizeObserver

里面还有一层组件SingleObserver

//src\index.tsx
function ResizeObserver(props: ResizeObserverProps, ref: React.Ref<HTMLElement>) {

  return childNodes.map((child, index) => {
    const key = child?.key || `${INTERNAL_PREFIX_KEY}-${index}`;
    return (
      <SingleObserver {...props} key={key} ref={index === 0 ? ref : undefined}>
        {child}
      </SingleObserver>
    );
  }) as any as React.ReactElement;
}


SingleObserver 組件

真正实现观察的方法在这个组件

const RefSingleObserver = React.forwardRef(SingleObserver);
//src\SingleObserver\index.tsx
function SingleObserver(
  props: SingleObserverProps,
  ref: React.Ref<HTMLElement>
) {
  return (
    <DomWrapper ref={wrapperRef}>
      {canRef
        ? React.cloneElement(mergedChildren as any, {
            ref: mergedRef,
          })
        : mergedChildren}
    </DomWrapper>
  );
}

实现元素变化逻辑

监听 elementRef.current 的變化
在 SingleObserver 组件

import { observe, unobserve } from "../utils/observerUtil";

// Dynamic observe
React.useEffect(() => {
  // getDom获取要被侦听的element
  const currentElement: HTMLElement = getDom();

  if (currentElement && !disabled) {
    // 执行侦听
    observe(currentElement, onInternalResize);
  }
  // 清除侦听
  return () => unobserve(currentElement, onInternalResize);
}, [elementRef.current, disabled]);

创建侦听器实例

// src\utils\observerUtil.ts
const elementListeners = new Map<Element, Set<ResizeListener>>();
import ResizeObserver from 'resize-observer-polyfill';

// interface ResizeObserverEntry {
//     readonly target: Element;
//     readonly contentRect: DOMRectReadOnly;
// }

// onResize 创建侦听器传入的callback
function onResize(entities: ResizeObserverEntry[]) {
  entities.forEach((entity) => {
    const { target } = entity;
    // elementListeners.get(target)是set集合 ,listener是回调函数onInternalResize
    elementListeners.get(target)?.forEach((listener) => listener(target));
  });
}

// Note: ResizeObserver polyfill not support option to measure border-box resize
const resizeObserver = new ResizeObserver(onResize);
// resize-observer-polyfill中ResizeObserverSPI类
const observer = new ResizeObserverSPI(callback, controller, this);

ResizeObserverSPI类的broadcastActive方法
callback返回的信息,entries是一个数组,返回所有正在活跃的目标element列表

// Create ResizeObserverEntry instance for every active observation.
const entries = this.activeObservations_.map((observation) => {
  // 返回被觀察element最新的大小
  return new ResizeObserverEntry(
    observation.target,
    // 執行observation.broadcastRect函數獲取最新的大小
    observation.broadcastRect()
  );
});

// 改变回调函数的this指向ctx
this.callback_.call(ctx, entries, ctx);

observe 函数

const elementListeners = new Map<Element, Set<ResizeListener>>();


function observe(element: Element, callback: ResizeListener) {
  if (!elementListeners.has(element)) {
    // 给elementListeners添加一个键值对
    elementListeners.set(element, new Set());
    //
    resizeObserver.observe(element);
  }
// elementListeners.get(element) 是set结构,给set插入一个新元素callback回调函数即onInternalResize
  elementListeners.get(element).add(callback);
}

unobserve 函数

const elementListeners = new Map<Element, Set<ResizeListener>>();

// 取消侦听
function unobserve(element: Element, callback: ResizeListener) {
  if (elementListeners.has(element)) {
    //set集合移除callback回调函数
    elementListeners.get(element).delete(callback);

    if (!elementListeners.get(element).size) {
      // 取消侦听
      resizeObserver.unobserve(element);
      // 移除目标element
      elementListeners.delete(element);
    }
  }
}

onInternalResize 函数

CollectionContext = React.createContext<onCollectionResize>(null);
const onCollectionResize = React.useContext(CollectionContext);


const propsRef = React.useRef < SingleObserverProps > props;
propsRef.current = props;

// Handler
const onInternalResize = React.useCallback((target: HTMLElement) => {
  const { onResize, data } = propsRef.current;

  // getBoundingClientRect侦听器内部实现的一个方法,获取元素尺寸大小
  const { width, height } = target.getBoundingClientRect();
  const { offsetWidth, offsetHeight } = target;

  /**
   * Resize observer trigger when content size changed.
   * In most case we just care about element size,
   * let's use `boundary` instead of `contentRect` here to avoid shaking.
   */
  const fixedWidth = Math.floor(width);
  const fixedHeight = Math.floor(height);

  if (
    sizeRef.current.width !== fixedWidth ||
    sizeRef.current.height !== fixedHeight ||
    sizeRef.current.offsetWidth !== offsetWidth ||
    sizeRef.current.offsetHeight !== offsetHeight
  ) {
    const size = {
      width: fixedWidth,
      height: fixedHeight,
      offsetWidth,
      offsetHeight,
    };
    sizeRef.current = size;

    // IE is strange, right?
    const mergedOffsetWidth =
      offsetWidth === Math.round(width) ? width : offsetWidth;
    const mergedOffsetHeight =
      offsetHeight === Math.round(height) ? height : offsetHeight;

    const sizeInfo = {
      ...size,
      offsetWidth: mergedOffsetWidth,
      offsetHeight: mergedOffsetHeight,
    };

    // Let collection know what happened
    onCollectionResize?.(sizeInfo, target, data);

    if (onResize) {
      // defer the callback but not defer to next frame
      Promise.resolve().then(() => {
        // 给父组件传递信息
        onResize(sizeInfo, target);
      });
    }
  }
}, []);



getDom 函數

const getDom = () =>
  findDOMNode<HTMLElement>(elementRef.current) ||
  // Support `nativeElement` format
  (elementRef.current && typeof elementRef.current === 'object'
    ? findDOMNode<HTMLElement>((elementRef.current as any)?.nativeElement)
    : null) ||
  findDOMNode<HTMLElement>(wrapperRef.current);

findDOMNode函數

github:https://github.com/react-component/util/blob/master/src/Dom/findDOMNode.ts

/**
 * Return if a node is a DOM node. Else will return by `findDOMNode`
 */
function findDOMNode<T = Element | Text>(
  node: React.ReactInstance | HTMLElement | SVGElement,
): T {
  if (isDOM(node)) {
    return (node as unknown) as T;
  }

  if (node instanceof React.Component) {
    return (ReactDOM.findDOMNode(node) as unknown) as T;
  }

  return null;
}
function isDOM(node: any): node is HTMLElement | SVGElement {
  // https://developer.mozilla.org/en-US/docs/Web/API/Element
  // Since XULElement is also subclass of Element, we only need HTMLElement and SVGElement
  return node instanceof HTMLElement || node instanceof SVGElement;
}

Collection组件

function Collection({ children, onBatchResize }: CollectionProps) {
  const resizeIdRef = React.useRef(0);
  const resizeInfosRef = React.useRef<ResizeInfo[]>([]);

  const onCollectionResize = React.useContext(CollectionContext);

  const onResize = React.useCallback<onCollectionResize>(
    (size, element, data) => {
      resizeIdRef.current += 1;
      const currentId = resizeIdRef.current;

      resizeInfosRef.current.push({
        size,
        element,
        data,
      });

      Promise.resolve().then(() => {
        if (currentId === resizeIdRef.current) {
          onBatchResize?.(resizeInfosRef.current);
          resizeInfosRef.current = [];
        }
      });

      // Continue bubbling if parent exist
      onCollectionResize?.(size, element, data);
    },
    [onBatchResize, onCollectionResize],
  );

  return <CollectionContext.Provider value={onResize}>{children}</CollectionContext.Provider>;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1530678.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

spark RDD 创建及相关算子

RDD编程入口 RDD编程入口对象是SparkContext对象&#xff0c;想要调用相关的计算api都需要通过构造出的sparkcontext对象调用 RDD的创建 通过并行化集合创建RDD&#xff08;本地集合转为分布式&#xff09;&#xff0c;api如下 rdd sc.parrallize(param1, param2)参数1是本…

第112讲:Mycat实践指南:字符串Hash算法分片下的水平分表详解

文章目录 1.字符串Hash算法分片的概念1.1.字符串Hash算法的概念1.2.字符串Hash算法是如何将数据路由到分片节点的 2.使用字符串Hash算法分片对某张表进行水平拆分2.1.在所有的分片节点中创建表结构2.2.配置Mycat实现字符串Hash算法分片的水平分表2.2.1.配置Schema配置文件2.2.2…

边缘检测-Tiny and Efficient Model for the Edge Detection Generalization

源代码: https://github.com/xavysp/TEED 论文地址&#xff1a;https://arxiv.org/pdf/2308.06468.pdf 大多数高级计算机视觉任务依赖于低级图像操作作为其初始过程。边缘检测、图像增强和超分辨率等操作为更高级的图像分析提供了基础。在这项工作中&#xff0c;我们考虑三个…

公众号获取token失败,当日access_token超过1万次处理

问题&#xff1a;如果你当日的 access_token 获取次数已经超过了 1 万次&#xff0c;那么很有可能是由于频繁获取 access_token 而被微信限制了。在这种情况下&#xff0c;你需要等待到下一个自然日或者等待一段时间后再尝试获取 access_token。或者直接去公众号去刷新掉用量。…

JAVA EE (计算机是如何工作的)

学前注意事项 出去面试的时候java岗位不需要懂前端&#xff08;会少量讲解&#xff09; 但是我们做项目的时候多少回用到一些前端的东西 1.什么是计算机 1.1前情提要 不仅仅只有电脑是计算机 计算机还不仅仅是电脑手机和平板 路由器 智能洗衣机 刷脸打卡机都可以说是计算…

MySQL中数据库表的监控

MySQL中数据库表的监控 &#xff08;1&#xff09;查看数据库中当前打开了哪些表&#xff1a;show OPEN TABLES &#xff0c;如图6-1-5所示。另外&#xff0c;还可以通过show OPEN TABLES where In_use > 0过滤出当前已经被锁定的表。 查看数据库中表的状态&#xff1a;SHO…

月销12万,卖出6万件,1688跨境热销榜第一是它!

店雷达1688热销商品榜&#xff0c;食品类目下&#xff0c;月度排名第一的热销商品是这款【三只松鼠_多味鹌鹑蛋混合口味 休闲零食小吃卤蛋夜宵熟食】月销售额达到12万&#xff0c;一个月内卖出5万笔&#xff0c;复购率保持在51%。看来我们的这款中国小吃&#xff0c;也收到很多…

linux -- I2C设备驱动 -- MS32006(低压5V多通道电机驱动器)

产品简述 MS32006 是一款多通道电机驱动芯片, 其中包含两路步进电机驱动, 一路直流电机驱动; 每个通道的电流最高电流1.0A; 支持两相四线与四相五线步进电机。芯片采用 I2C 的通信接口控制模式, 兼容 3.3V/5V 的标准工业接口。 MS32006 总共集成了两路步进电机驱动器与一…

数据分析能力模型分析与展示

具体内容&#xff1a; 专业素质 专业素质-01 数据处理 能力定义•能通过各种数据处理工具及数据处理方法&#xff0c;对内外部海量数据进行清洗和运用&#xff0c;提供统一数据标准&#xff0c;为业务分析做好数据支持工作。 L1•掌握一…

【数据结构】数据结构和算法的重要性复杂度详解

主页&#xff1a;醋溜马桶圈-CSDN博客 专栏&#xff1a;数据结构_醋溜马桶圈的博客-CSDN博客 gitee&#xff1a;mnxcc (mnxcc) - Gitee.com 目录 1.数据结构和算法 1.1什么是数据结构&#xff1f; 1.2 什么是算法&#xff1f; 1.3 数据结构和算法的重要性 1.4 如何学好数据…

线程和进程的区别和联系

一、什么是进程 进程(Process), 是一个具有独立功能的程序关于某个数据集合的一次运行活动&#xff0c;是系统进行 【资源分配和调度】 的一个独立单位。 进程是【程序】的【一次执行】(是计算机中程序的执行过程&#xff0c;而不是计算机中的程序)进程是系统进行【资源分配和…

【MySQL】知识点 + 1

# &#xff08;1&#xff09;查询当前日期、当前时间以及到2022年1月1日还有多少天&#xff0c;然后通过mysql命令执行命令。 select curdate() AS 当前日期,curtime() AS 当前时间,datediff(2022-01-01, curdate()) AS 距离2022年1月1日还有天数;# &#xff08;2&#xff09;利…

2024年 信息系统管理工程师(中级)

2024年信息系统管理工程师全套视频、历年真题及解析、历年真题视频解析、教材、模拟题、重点笔记等资料 1、2023、2022、2021、2020年全套教程精讲视频。 2、信息系统管理工程师历年真题及解析&#xff08;综合知识、案例分析&#xff09;、历年真题视频解析。 3、官方最新信…

【linux线程(三)】生产者消费者模型详解(多版本)

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:Linux从入门到精通⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学更多操作系统知识   &#x1f51d;&#x1f51d; Linux线程 1. 前言2. 初识生产…

【数据结构】哈希表与哈希桶

&#x1f440;樊梓慕&#xff1a;个人主页 &#x1f3a5;个人专栏&#xff1a;《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》《C》《Linux》《算法》 &#x1f31d;每一个不曾起舞的日子&#xff0c;都是对生命的辜负 目录 前言 1.概念 2.哈希冲突…

SylixOS工程如何生成map文件

生成.map文件通常是在编译链接阶段由编译器或链接器自动完成的。如果你需要手动生成.map文件&#xff0c;可以通过配置链接器选项来实现。 以bsp工程为例&#xff0c;在内核工程/libsylixos/SylixOS/mktemp/bsp.mk文件中添加-Wl,-Map,output.map选项来生成.map文件。

Java学习笔记:异常处理

Java学习笔记&#xff1a;异常处理 什么是异常异常体系结构&#xff1a;Error、Exception自定义异常 ​ **2024/3/19** 什么是异常 异常体系结构&#xff1a;Error、Exception 自定义异常

Orange3数据预处理(分类器组件)

创建类属性 从字符串属性创建类属性。 输入 数据&#xff1a;输入数据集 输出 数据&#xff1a;具有新类变量的数据集 功能 创建类属性功能从一个已存在的离散或字符串属性中创建一个新的类属性。该组件匹配所选属性的字符串值&#xff0c;并为匹配的实例构造一个新…

Spring Boot:筑基

Spring Boot 前言概述使用 Intellij idea 快速创建 Spring Boot 项目注意事项 前言 在学习 Spring 、SpringMVC 、MyBatis 和 JPA 框架的过程中&#xff0c;了解到 SSM 框架为 Java Web 开发提供了强大的后端支持&#xff0c;JPA 框架则简化了数据库的操作。然而&#xff0c;S…

【智能算法应用】智能算法优化BP神经网络思路

目录 1.思路2.设计 1.思路 在BP神经网络结构中&#xff0c;权值和阈值被视为模型的参数&#xff0c;它们在训练过程中需要通过反向传播算法进行学习&#xff0c;以使得网络的输出尽可能地接近真实标签。这意味着网络的目标是通过最小化均方误差&#xff08;MSE&#xff09;来调…