Skip to content

Виртуальная прокрутка

Почему это делает движок

Хвостовые задержки виртуальных списков на DOM берутся из цепочки: событие прокрутки возвращается в главный поток, вызывает setState, затем diff, затем переразметку. Как только главный поток занят, кадры теряются.

doper переносит расчёт окна в ядро: установившаяся прокрутка никогда не вызывает оболочку. Оболочка лишь материализует видимый диапазон по окну прогрева, которое спланировало ядро; если данные ещё не готовы, рисуется заглушка, а на следующих кадрах она дополняется.

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

ts
createElement("virtualList", {
  width: 480,
  height: 640,
  itemCount: 1_000_000,
  estimatedItemHeight: 32,
  renderItem: (index: number) =>
    createElement("container", {
      width: 480,
      height: 32,
      children: createElement("text", { value: `Строка ${index}` }),
    }),
});

estimatedItemHeight — только начальная оценка. Когда измерена настоящая высота, ядро корректирует положение якоря через дерево префиксных сумм (Фенвика), и позиция прокрутки не прыгает.

Настраиваемые параметры

propДействие
baseOverscanViewportsСимметричный диапазон прогрева (в единицах вьюпорта)
velocityHorizonSecondsГоризонт проекции скорости для предсказания направления
maximumAheadViewportsПредел прогрева в одном направлении
scrollX / scrollYПрограммная позиция прокрутки (ScrollTo только при изменении)

Предсказание направления при быстром броске прогревает прежде всего сторону движения, а не тратит бюджет симметрично на обе.

Программная прокрутка

ts
// Изменение свойства порождает ровно одну мутацию ScrollTo
root.render(createElement("virtualList", { scrollY: 500_000 * 32 /* ... */ }));

Либо API прямого управления на root — для собственных жестов:

ts
root.beginScroll(handle);
root.scrollBy(handle, 0, deltaY, elapsedMs);
root.endScroll(handle); // оценку скорости броска берёт на себя ядро

handle приходит из колбэка ref элемента (NodeHandle).

Колесо и трекпад

Расстояние прокрутки колесом совпадает с браузерным, но кривая передачи разделяется по источнику ввода: дельты высокой точности (трекпад) применяются 1:1 сразу, а инерцию по-прежнему поставляет поток событий операционной системы; дискретные щелчки колеса накапливаются в анимационную цель, к которой движение идёт по экспоненциальному замедлению, жёстко ограничивается границами содержимого и не даёт перепрокрутки — ровно как в браузере.

Вложенность и редактирование

Если перетаскивание указателя началось на редактируемом тексте, выделение текста имеет приоритет над перетаскиванием прокрутки; колесо по-прежнему прокручивает ближайшего прокручиваемого предка. Этот приоритет определяется глубиной пути попадания и не требует вмешательства приложения.

Критерий производительности

Автоматический бенчмарк на фиксированной фикстуре (миллион строк, 20 000 кадров) входит в набор проверок при слиянии. Сейчас P95/P99 воспроизведения меньше микросекунды, а тридцать минут непрерывной прокрутки не показывают неконтролируемого роста памяти.

P95/P99 на реальных устройствах и задержка ввода относятся к квалификации платформы и не являются условием завершения инженерных работ. Эта граница проведена намеренно: она не даёт невоспроизводимым данным с устройств блокировать работу и в то же время не позволяет выдавать инженерные цифры за обещание для устройств.

В демонстрации прокрутки в Playground видны покадровые метрики в реальном времени.

Опубликовано под лицензией MIT