Переопределение методов платформы

В основе работы библиотеки лежит абстракция платформы — набора методов, через которые происходит взаимодействие с окружением (DOM, браузер, виртуальная среда). Эти методы используются функцией позиционирования для получения геометрии элементов, вычисления границ и обработки прокрутки.

Ключевая идея — отделение алгоритма позиционирования от конкретной среды выполнения. Это позволяет:

  • использовать библиотеку вне браузера (например, в Canvas или React Native),
  • тестировать логику без DOM,
  • гибко адаптировать поведение под нестандартные условия.

Платформа представлена объектом с набором функций, которые можно переопределять.


Стандартный интерфейс платформы

Базовый набор методов платформы включает:

  • getElementRects — получение размеров и координат reference и floating элементов
  • getClippingRect — вычисление области отсечения (viewport или родительские контейнеры)
  • getOffsetParent — определение offset-контекста
  • getDimensions — получение размеров элемента
  • getClientRects — список клиентских прямоугольников
  • isElement — проверка, является ли объект DOM-элементом
  • getScale — масштабирование (например, при CSS transform)
  • getDocumentElement — корневой элемент документа

Каждый из этих методов может быть заменён пользовательской реализацией.


Причины переопределения методов

1. Работа вне DOM

В средах без стандартного DOM (например, Canvas или WebGL) отсутствуют привычные методы вроде getBoundingClientRect. Переопределение позволяет подставить собственные вычисления координат.

2. Виртуальные элементы

Floating UI поддерживает виртуальные reference-элементы — объекты, которые ведут себя как DOM-элементы, но не существуют в дереве документа. Например, курсор мыши или координаты выделения текста.

3. Кастомные системы координат

При работе с трансформациями, масштабированием или вложенными контекстами может потребоваться собственная логика расчёта позиций.

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

Можно упростить вычисления, если известны ограничения среды — например, убрать поддержку сложных сценариев прокрутки.


Переопределение через параметр platform

Функция computePosition принимает объект platform, который переопределяет стандартную реализацию:

import {computePosition} from '@floating-ui/dom';

computePosition(reference, floating, {
  platform: {
    getElementRects: async ({reference, floating}) => {
      return {
        reference: {
          x: 100,
          y: 100,
          width: 50,
          height: 20,
        },
        floating: {
          x: 0,
          y: 0,
          width: 80,
          height: 40,
        },
      };
    }
  }
});

Если метод не указан, используется реализация по умолчанию.


Глубокая настройка getElementRects

Этот метод — один из ключевых, поскольку определяет исходные данные для позиционирования.

Стандартная версия использует getBoundingClientRect, но при переопределении можно:

  • учитывать дополнительные смещения,
  • игнорировать трансформации,
  • подставлять заранее вычисленные значения.

Пример с учётом пользовательского смещения:

const platform = {
  async getElementRects({reference, floating}) {
    const refRect = reference.getBoundingClientRect();
    const floatRect = floating.getBoundingClientRect();

    return {
      reference: {
        ...refRect,
        x: refRect.x + 10,
        y: refRect.y + 20,
      },
      floating: floatRect,
    };
  }
};

Переопределение getClippingRect

Метод отвечает за вычисление области, внутри которой должен находиться floating-элемент.

В стандартной реализации учитываются:

  • viewport,
  • scroll-контейнеры,
  • overflow-ограничения.

Переопределение может потребоваться, если:

  • используется фиксированная область отображения,
  • необходимо игнорировать прокрутку,
  • реализуется кастомный layout-движок.

Пример фиксированной области:

const platform = {
  async getClippingRect() {
    return {
      width: 800,
      height: 600,
      x: 0,
      y: 0,
    };
  }
};

Работа с виртуальными элементами

Виртуальный элемент — объект с методом getBoundingClientRect:

const virtualReference = {
  getBoundingClientRect() {
    return {
      x: mouseX,
      y: mouseY,
      width: 0,
      height: 0,
      top: mouseY,
      left: mouseX,
      right: mouseX,
      bottom: mouseY,
    };
  }
};

При необходимости можно полностью заменить платформенный метод isElement, чтобы библиотека корректно обрабатывала такие объекты:

const platform = {
  isElement: (value) => {
    return value && typeof value.getBoundingClientRect === 'function';
  }
};

Переопределение getOffsetParent

Этот метод влияет на систему координат, в которой происходит позиционирование.

Стандартное поведение:

  • ищется ближайший родитель с позиционированием,
  • учитываются CSS-свойства position, transform.

Кастомизация может понадобиться:

  • при использовании shadow DOM,
  • в canvas-рендеринге,
  • при игнорировании вложенных контекстов.

Пример принудительного использования document.body:

const platform = {
  getOffsetParent: () => document.body
};

Масштабирование и getScale

Если элементы масштабируются через CSS (transform: scale), координаты могут искажаться. Метод getScale позволяет учитывать это.

Переопределение полезно, если:

  • масштаб известен заранее,
  • используется нестандартный механизм масштабирования.
const platform = {
  getScale: () => ({x: 1.5, y: 1.5})
};

Частичное переопределение

Не требуется реализовывать весь интерфейс — достаточно заменить только нужные методы. Остальные будут взяты из стандартной реализации.

Комбинирование:

import {platform as defaultPlatform} from '@floating-ui/dom';

const customPlatform = {
  ...defaultPlatform,
  getElementRects: async (args) => {
    const rects = await defaultPlatform.getElementRects(args);
    
    return {
      ...rects,
      reference: {
        ...rects.reference,
        x: rects.reference.x + 5,
      }
    };
  }
};

Влияние на middleware

Middleware (например, offset, flip, shift) используют данные платформы. Любое изменение методов напрямую влияет на:

  • расчёт границ,
  • определение переполнения,
  • корректность позиционирования.

Ошибки в платформе приводят к:

  • неправильному размещению элементов,
  • дрожанию UI,
  • некорректной работе адаптивных стратегий.

Тестирование кастомной платформы

При переопределении методов необходимо проверять:

  • корректность координат (x, y),
  • соответствие размеров (width, height),
  • согласованность между методами.

Рекомендуется:

  • логировать возвращаемые значения,
  • тестировать разные сценарии (scroll, resize),
  • использовать изолированные тесты без DOM.

Переопределение в разных окружениях

React Native

Нет DOM — требуется полная реализация платформы:

  • координаты берутся из layout-системы,
  • размеры — из API компонентов.

Canvas / WebGL

  • элементы — абстрактные объекты,
  • координаты вычисляются вручную,
  • clipping — через границы сцены.

SSR

  • отсутствует доступ к window,
  • можно возвращать статические значения,
  • полезно для предварительного рендеринга.

Ошибки и ограничения

  • Несогласованные координаты между методами приводят к некорректным вычислениям
  • Игнорирование scroll-контейнеров ломает поведение flip и shift
  • Неверная реализация getScale искажает позиционирование при трансформациях
  • Отсутствие асинхронности (если требуется) может вызвать ошибки в middleware

Практическая стратегия внедрения

  1. Начинать с частичного переопределения
  2. Использовать стандартную платформу как основу
  3. Изменять только проблемные методы
  4. Проверять влияние на middleware
  5. Постепенно расширять кастомизацию

Такой подход позволяет сохранить стабильность и избежать сложных ошибок при полной замене платформенного слоя.