Resize Observer

Resize Observer — это веб-API, которое позволяет отслеживать изменения размеров DOM-элементов. В контексте библиотеки Atomico оно интегрируется через реактивные хуки, обеспечивая эффективное управление компонентами при изменении их размеров.

Основы работы с Resize Observer

В стандартном JavaScript ResizeObserver создаётся как объект, которому передаётся функция обратного вызова:

const observer = new ResizeObserver(entries => {
    for (let entry of entries) {
        console.log(entry.contentRect.width, entry.contentRect.height);
    }
});

Метод observe привязывает наблюдатель к конкретному элементу:

observer.observe(element);

Метод unobserve отключает наблюдение, а disconnect полностью прекращает работу наблюдателя.

Использование Resize Observer в Atomico

Atomico предлагает удобные хуки для интеграции ResizeObserver в реактивные компоненты. Основной подход — использование useResizeObserver, который возвращает текущие размеры элемента и автоматически обновляет их при изменении.

import { c, useResizeObserver } from "atomico";

function myComponent() {
    const [ref, rect] = useResizeObserver();

    return (
        <host shadowDom>
            <div ref={ref} style={{ width: "100%", height: "200px", background: "lightblue" }}>
                Ширина: {rect.width}, Высота: {rect.height}
            </div>
        </host>
    );
}

customElements.define("my-component", c(myComponent));

Здесь ключевые моменты:

  • ref — ссылка на DOM-элемент, который нужно наблюдать.
  • rect — объект с актуальными размерами { width, height, top, left }.

Настройка наблюдения

ResizeObserver отслеживает контентные размеры элемента. В Atomico это можно расширять через опции:

const [ref, rect] = useResizeObserver({
    box: "border-box" // отслеживать размер с учётом границ и паддингов
});

Доступны следующие значения свойства box:

  • "content-box" — учитывает только контент без паддингов и границ.
  • "border-box" — учитывает границы и паддинги, соответствует CSS box-sizing: border-box.
  • "device-pixel-content-box" — учитывает физические пиксели на устройстве, полезно для canvas и high-DPI.

Оптимизация производительности

Resize Observer может вызывать большое количество обновлений при динамических изменениях DOM. В Atomico можно оптимизировать реактивные обновления:

  • Дебаунс или троттлинг через кастомный хук:
import { useResizeObserver } from "atomico";
import { useState, useEffect } from "atomico";

function useThrottledResize(ref, delay = 100) {
    const [rect, setRect] = useState({ width: 0, height: 0 });

    useResizeObserver(ref, entry => {
        clearTimeout(ref._timeout);
        ref._timeout = setTimeout(() => {
            setRect(entry.contentRect);
        }, delay);
    });

    return rect;
}
  • Использование requestAnimationFrame для плавного обновления размеров без лишних ререндеров.

Практические сценарии

  1. Адаптивные компоненты: изменение ширины и высоты контейнера для динамического контента.
  2. Встраиваемые графики и canvas: автоматическое подстраивание под размеры родителя.
  3. Анимации и эффекты: запуск анимаций при изменении размеров блоков.
  4. Отслеживание текста и динамического контента: высота блока может меняться в зависимости от текста или загружаемых элементов.

Пример с адаптивной сеткой:

function gridItem() {
    const [ref, rect] = useResizeObserver();

    const columns = Math.floor(rect.width / 150);

    return (
        <host>
            <div ref={ref} style={{ display: "grid", gridTemplateColumns: `repeat(${columns}, 1fr)` }}>
                <div>Элемент 1</div>
                <div>Элемент 2</div>
                <div>Элемент 3</div>
            </div>
        </host>
    );
}

Особенности интеграции с Atomico

  • useResizeObserver синхронизирован с жизненным циклом компонента: при удалении элемента наблюдение прекращается автоматически.
  • Реактивное обновление размеров позволяет создавать чистые и декларативные компоненты, без прямого обращения к DOM через querySelector.
  • Можно комбинировать с другими хуками Atomico (useState, useEffect) для построения сложной логики на основе изменения размеров.

Отслеживание нескольких элементов

Atomico позволяет наблюдать сразу несколько элементов, используя массив refs:

const refs = [useResizeObserver(), useResizeObserver()];

return (
    <host>
        <div ref={refs[0][0]}>Элемент A: {refs[0][1].width}</div>
        <div ref={refs[1][0]}>Элемент B: {refs[1][1].width}</div>
    </host>
);

Каждый ref сохраняет свой собственный объект rect, полностью независимый и реактивный.

Сравнение с классическим ResizeObserver

Atomico упрощает следующие аспекты:

  • Автоматическое управление жизненным циклом наблюдателя.
  • Реактивная интеграция с компонентами, без необходимости вручную хранить состояние размеров.
  • Оптимизация ререндеров благодаря реактивной системе хуков Atomico.

Atomico превращает работу с ResizeObserver из низкоуровневого API в чистую реактивную декларацию, сохраняя контроль над производительностью и упрощая интеграцию в сложные компоненты.