resize-observer-polyfill源码解读之ResizeObservation

github 地址:https://github.com/que-etc/resize-observer-polyfill

版本 v1.5.1(2018.12.9)

ResizeObservation 类定义

src/ResizeObservation.js

ResizeObservation 这个类是被观察的元素的封装, 它会保存元素上一次的宽高信息,提供了两个方法

  • isActive() 与元素之前的宽高进行比较,如果不同则返回 true,这样就指定这个元素更新了。
  • broadcastRect() 这个是用当前的值更新上一次的值。
class ResizeObservation {
  target; // @type {Element}
  broadcastWidth = 0; // element的content宽度
  broadcastHeight = 0; //element的content高度
   // element的padding-left padding-top width height
  contentRect_ = createRectInit(0, 0, 0, 0); 

  /**
   * 创建ResizeObservation实例
   */
  constructor(target) {
    this.target = target;
  }

  /**
   *目标元素 的宽度和高度是否存在更新
   */
  isActive() {
    // 获取元素的padding-left,padding-top, width 和height属性
    // 普通element执行getHTMLElementContentRect;svg执行getSVGContentRect
    const rect = getContentRect(this.target);
    this.contentRect_ = rect;
    return (
      rect.width !== this.broadcastWidth || rect.height !== this.broadcastHeight
    );
  }

  /**
   * 更新'broadcastWidth' and 'broadcastHeight' 属性,通过监听的内容元素this.contentRect_
   */
  broadcastRect() {
    const rect = this.contentRect_;
    this.broadcastWidth = rect.width;
    this.broadcastHeight = rect.height;
    return rect;
  }
}

getContentRect函数

获取目标元素的宽高属性方法

function getContentRect(target) {
  if (!isBrowser) {
    return emptyRect;
  }

  if (isSVGGraphicsElement(target)) {
    return getSVGContentRect(target);
  }

  return getHTMLElementContentRect(target);
}

getHTMLElementContentRect函数

getHTMLElementContentRect获取普通 element 目标元素的宽高方法

function getHTMLElementContentRect(target) {
  const { clientWidth, clientHeight } = target;
  if (!clientWidth && !clientHeight) {
    return emptyRect;
  }
  // getWindowOf返回当前 目标element对象所关联的 window 对象
  // getComputedStyle获取元素css属性集合,是一个对象
  const styles = getWindowOf(target).getComputedStyle(target);
  // 返回一个paddingd对象{padding-top,padding-right, padding-bottom,padding-left}
  const paddings = getPaddings(styles);
  // 水平边距
  const horizPad = paddings.left + paddings.right;
  // 垂直边距
  const vertPad = paddings.top + paddings.bottom;

  // width 属性用于设置元素的宽度。width 默认设置内容区域的宽度,但如果 box-sizing 属性被设置为 border-box,就转而设置边框区域的宽度。
  let width = toFloat(styles.width),
    height = toFloat(styles.height);

  // Width & height include paddings and borders when the 'border-box' box
  // model is applied (except for IE).
  if (styles.boxSizing === "border-box") {
    // clientWidth只包含padding;clientWidth该属性包括内边距(padding),但不包括边框(border)、外边距(margin)和垂直滚动条(如果存在)。
    if (Math.round(width + horizPad) !== clientWidth) {
      // getBordersSize返回border-left和borderWidth类加值
      width -= getBordersSize(styles, "left", "right") + horizPad;
      //即是 width = width - (getBordersSize(styles, "left", "right") + horizPad)
    }

    if (Math.round(height + vertPad) !== clientHeight) {
      // getBordersSize返回border-top和border-bottom类加值
      height -= getBordersSize(styles, "top", "bottom") + vertPad;
      // height =  height - (getBordersSize(styles, "top", "bottom") + vertPad;)
    }
  }
  // 此时 width 等于element的content宽度, height等于content的高度
  // css宽度和高度包含 滚动条的大小
  // 当target不是根元素
  if (!isDocumentElement(target)) {
    // 垂直方向滚动条宽度
    const vertScrollbar = Math.round(width + horizPad) - clientWidth;
    // 水平方向的滚动高度
    const horizScrollbar = Math.round(height + vertPad) - clientHeight;

    // 忽略绝对值为1时的滚动条
    /**
     * Chrome有一个相当奇怪的四舍五入“客户端”属性。
        例如,对于内容宽度为314.2px的元素,它有时会给出
        客户端宽度为315px,对于宽度为314.7px,它可能会给出
        314像素。而且这种情况并不总是发生。所以忽略这个delta
        作为不相关的。
     */
    if (Math.abs(vertScrollbar) !== 1) {
      width -= vertScrollbar;
      // 即 width = width - vertScrollbar
    }

    if (Math.abs(horizScrollbar) !== 1) {
      height -= horizScrollbar;
      //即 height = height - horizScrollbar;
    }
  }
  // 返回{paddingLeft, paddingTop, width, height}
  return createRectInit(paddings.left, paddings.top, width, height);
}

Element.clientWidth:只读属性 Element.clientWidth 对于内联元素以及没有 CSS 样式的元素为 0;否则,它是元素内部的宽度(以像素为单位)。该属性包括内边距(padding),但不包括边框(border)、外边距(margin)和垂直滚动条(如果存在)。

getComputedStyle():Window.getComputedStyle()方法返回一个对象,该对象在应用活动样式表并解析这些值可能包含的任何基本计算后报告元素的所有 CSS 属性的值。私有的 CSS 属性值可以通过对象提供的 API 或通过简单地使用 CSS 属性名称进行索引来访问。
返回的 style 是一个实时的 CSSStyleDeclaration 对象,当元素的样式更改时,它会自动更新本身。

CSSStyleDeclaration 接口表示一个对象,它是一个 CSS 声明块,CSS 属性键值对的集合。它暴露了样式信息和各种与样式相关的方法和属性。

CSSStyleDeclaration对象可被暴露于三种不同的 API 下:

  • HTMLElement.style,用于操作单个元素的样式()。
  • CSSStyleSheet API,举个例子,document.styleSheets[0].cssRules[0].style 会返回文档中第一个样式表中的第一条 CSS 规则。
  • Window.getComputedStyle(),将 CSSStyleDeclaration 对象作为一个只读的接口。

减法赋值 示例

let a = 2;
console.log((a -= 3 + 1));
// Expected output: -2

getWindowOf函数

Node.ownerDocument :Node.ownerDocument 只读属性会返回当前节点的顶层的 document 对象。
Document.defaultView: 在浏览器中,该属性返回当前 document 对象所关联的 window 对象,如果没有,会返回 null。

export default (target) => {
  // 目标element存在说明有ownerDocument属性,顶层对象所关联的window对象
  const ownerGlobal =
    target && target.ownerDocument && target.ownerDocument.defaultView;
  return ownerGlobal || global;
};

getPaddings函数

获取目标元素的padding信息

/**
 * Extracts paddings sizes from provided styles.
 *
 * @param {CSSStyleDeclaration} styles
 * @returns {Object} Paddings box.
 */
function getPaddings(styles) {
  const positions = ["top", "right", "bottom", "left"];
  const paddings = {};

  for (const position of positions) {
    const value = styles["padding-" + position];
    // parseFloat() 函数解析一个参数(必要时先转换为字符串)并返回一个浮点数。 number类型
    paddings[position] = toFloat(value);
  }

  return paddings;
}

getBordersSize函数

返回border累加的和

/**
 * Extracts borders size from provided styles.
 *
 * @param {CSSStyleDeclaration} styles
 * @param {...string} positions - Borders positions (top, right, ...)
 * @returns {number}
 */
function getBordersSize(styles, ...positions) {
  return positions.reduce((size, position) => {
    const value = styles["border-" + position + "-width"];

    return size + toFloat(value);
  }, 0);
}

createRectInit函数

根据传入的信息按规范格式返回

/**
 * Creates DOMRectInit object based on the provided dimensions and the x/y coordinates.
 * Spec: https://drafts.fxtf.org/geometry/#dictdef-domrectinit
 *
 * @param {number} x - X coordinate.
 * @param {number} y - Y coordinate.
 * @param {number} width - Rectangle's width.
 * @param {number} height - Rectangle's height.
 * @returns {DOMRectInit}
 */
export function createRectInit(x, y, width, height) {
  return { x, y, width, height };
}

isDocumentElement函数

判断是否根文档元素

/**
 * Checks whether provided element is a document element (<html>).
 *
 * @param {Element} target - Element to be checked.
 * @returns {boolean}
 */
function isDocumentElement(target) {
  // Document.documentElement 是一个会返回文档对象(document)的根元素的只读属性(如 HTML 文档的 <html> 元素)。
  return target === getWindowOf(target).document.documentElement;
}

getSVGContentRect函数

SVGGraphicsElement.getBBox()允许我们确定对象适合的最小矩形的坐标。返回的坐标是相对于当前 svg 空间的,即在将所有几何属性应用于目标元素中包含的所有元素之后。

/**
 * Calculates content rectangle of provided SVG element.
 *
 * @param {SVGGraphicsElement} target - Element content rectangle of which needs
 *      to be calculated.
 * @returns {DOMRectInit}
 */
function getSVGContentRect(target) {
  const bbox = target.getBBox();

  return createRectInit(0, 0, bbox.width, bbox.height);
}
  • 27
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值