Поддерживайте точность групп строк SplitText, пересоздавая их при изменении текстовой раскладки. Используйте автоматическое разделение для поддерживаемых триггеров, добавляйте явный пересчёт после изменений типографики и сравнивайте результат с неразбитым текстом браузера в предварительном просмотре на Mac.

Это руководство предназначено для тех, кто редактирует страницу в браузере с помощью GSAP SplitText и адаптивного заголовка. Задача — следить, чтобы каждая созданная обёртка строки соответствовала словам, которые сейчас находятся на этой визуальной строке, в том числе когда ширина заголовка остаётся неизменной, а размер шрифта меняется.

Почему при адаптивной типографике группы строк могут устареть?

Созданная обёртка строки охватывает определённую группу слов. Когда доступное пространство или типографика меняются, эту группу может потребоваться пересоздать. В документации GSAP указано, что autoSplit: true отслеживает завершение загрузки шрифтов и изменение ширины, если разделение включает строки. Эти задокументированные триггеры не означают, что существует общий наблюдатель за любыми изменениями типографики. Документация SplitText

Рассмотрим заголовок с font-size: clamp(2rem, 4vw, 4rem) внутри контейнера с максимальной шириной 42rem. MDN определяет clamp() как предпочтительное значение, ограниченное минимумом и максимумом. В этом примере предпочтительный размер шрифта зависит от ширины окна просмотра даже после того, как контейнер достиг ограничения. Функция CSS clamp()

В следующих расчётах используется условный размер корневого шрифта 16 CSS-пикселей и предполагается, что доступного пространства достаточно для достижения контейнером максимальной ширины. Это расчётные размеры, а не результаты измерений в браузере и не прогноз количества строк.

Ширина окна просмотра Ширина контейнера с учётом ограничения Расчётный размер шрифта
900 CSS px 672 CSS px 36 CSS px
1200 CSS px 672 CSS px 48 CSS px
1600 CSS px 672 CSS px 64 CSS px

Ширина контейнера остаётся прежней, а буквы увеличиваются. Отсюда следует, что одного отслеживания ширины для такого случая недостаточно. Считайте изменение вычисленного размера шрифта ещё одним поводом пересоздать строки, а затем проверьте расположение слов.

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

Сначала создайте эталон без разделения, затем изменяйте анимацию

Создайте в локальном предварительном просмотре две копии короткого тестового заголовка. Задайте им одинаковые ширину, шрифт, размер, насыщенность, интервалы и правила переноса. Первую оставьте без изменений, а ко второй примените SplitText. Это временное сравнение позволит заметить устаревшую группировку, не предполагая заранее, где должно переноситься конкретное слово.

Начните с обычных пробелов и переноса строк. Спецификация W3C CSS Text описывает, как white-space управляет схлопыванием пробелов и мягким переносом; в ней также заданы отдельные правила разрыва строк и интервалов. При пересоздании следует сохранять нужные правила, а не заменять их вручную вставленными разрывами. Модуль CSS Text Level 3

Если страница использует веб-шрифты, дождитесь document.fonts.ready перед первым разделением текста. MDN указывает, что этот промис разрешается после завершения соответствующих операций загрузки шрифтов и раскладки. Это задаёт начальную точку измерения, но не фиксирует последующие изменения раскладки. FontFaceSet: свойство ready

Также проверьте, какой шрифт фактически используется в браузере. MDN различает используемые шрифты и все объявленные шрифты: например, необязательный шрифт может не использоваться, если не успевает загрузиться в отведённое ему время. Ожидание готовности не доказывает, что каждый объявленный шрифт стал отображаемым шрифтом. Document: свойство fonts

Настройте автоматическое разделение и явную проверку адаптивного размера

Следующий законченный браузерный модуль создаёт неразбитый эталон, анимированную копию и кнопку Toggle spacing. Используйте отдельную страницу предварительного просмотра в существующем JavaScript-проекте, где сборщик разрешает npm-импорты, поддерживает верхнеуровневый await и запускает браузерный модуль после появления элемента body. Установите Node.js и npm на Mac.

Установите GSAP из каталога проекта следующей командой. Для этого сценария с обратным вызовом используйте GSAP версии 3.13 или новее. В документации GSAP по установке описаны установка через npm и явная регистрация плагина. Установка

npm install gsap

Замените браузерный модуль отдельной страницы предварительного просмотра этим кодом. Инициализация следует задокументированному шаблону SplitText.create(). Для плавного появления используется gsap.from(), который анимирует переход от нулевой непрозрачности к исходному виду. SplitText.create(), gsap.from()

import { gsap } from 'gsap';
import { SplitText } from 'gsap/SplitText';

gsap.registerPlugin(SplitText);

const style = document.createElement('style');
style.textContent = `
.flow-demo {
  width: min(100%, 42rem);
  font-family: system-ui, sans-serif;
}
.flow-demo h2 {
  font-size: clamp(2rem, 4vw, 4rem);
  line-height: 1.15;
  font-weight: 600;
  margin: 1rem 0;
  white-space: normal;
  text-wrap: wrap;
}
.flow-line { outline: 1px solid #aaa; }
`;
document.head.append(style);

const host = document.createElement('section');
host.className = 'flow-demo';
const reference = document.createElement('h2');
reference.textContent =
  'Panel Alpha holds a series of soft shapes beside Panel Beta.';
const target = reference.cloneNode(true);
const button = document.createElement('button');
button.type = 'button';
button.textContent = 'Toggle spacing';
host.append(reference, target, button);
document.body.append(host);

await document.fonts.ready;

const split = SplitText.create(target, {
  type: 'lines',
  linesClass: 'flow-line',
  autoSplit: true,
  onSplit(self) {
    return gsap.from(self.lines, {
      opacity: 0,
      duration: 0.3,
      stagger: 0.04
    });
  }
});

let timer;
let fontSize = getComputedStyle(target).fontSize;

function checkFluidSize() {
  clearTimeout(timer);
  timer = setTimeout(() => {
    const next = getComputedStyle(target).fontSize;
    if (next !== fontSize) {
      fontSize = next;
      split.split();
    }
  }, 150);
}
window.addEventListener('resize', checkFluidSize);

let spaced = false;
button.addEventListener('click', () => {
  spaced = !spaced;
  reference.style.letterSpacing = target.style.letterSpacing =
    spaced ? '0.06em' : 'normal';
  split.split();
});

Анимация создаётся внутри onSplit() и возвращается, чтобы SplitText мог очистить её и синхронизировать при пересоздании строк. GSAP описывает этот сценарий с обратным вызовом для версии 3.13 и новее. Документация SplitText

Добавленный обработчик изменения размера проверяет только вычисленный размер шрифта и запрашивает пересоздание строк после паузы в изменении размера. Задержка в 150 миллисекунд — условный выбор для примера. Цель примера — обеспечить точные группы после завершения изменения размера; он не гарантирует правильную группировку на каждом промежуточном кадре изменения размера.

Кнопка изменения интервалов демонстрирует второй явный триггер. Она меняет обе копии, а затем вызывает split.split(). В документации GSAP указано, что этот метод при необходимости сначала отменяет существующее разделение. В этом небольшом примере изменение ширины и размера шрифта также может привести к нескольким перекрывающимся запросам на пересоздание строк; если профилирование покажет лишнюю работу на большой странице, объедините их. split()

Модуль прошёл проверку синтаксиса JavaScript, а расчёты примерных размеров были выполнены. Раскладка и анимация в браузере здесь не запускались; выполните приведённые ниже визуальные проверки в локальном предварительном просмотре.

Выбирайте триггеры пересоздания с учётом изменений раскладки

В примере отслеживается одно выражение адаптивного размера шрифта. При адаптации примера определите, какой код или событие раскладки меняет текст, и запускайте пересоздание в этом месте. Не рассчитывайте, что пример обнаружит посторонние изменения классов, типографику, зависящую от контейнера, или постоянно анимируемые свойства шрифта.

Что меняется на странице Рекомендуемый способ обработки
Ширина текстового элемента Включите задокументированное автоматическое разделение и проверьте результат.
Размер шрифта зависит от окна просмотра при фиксированной ширине текста Сравнивайте вычисленный размер после изменения размера окна и пересоздавайте строки, если он изменился.
Элемент управления меняет трекинг, насыщенность или настройки шрифта Запросите пересоздание после применения элементом управления новых стилей.
Типографика зависит от окружающего контейнера Наблюдайте за контейнером, который задаёт размер, и проверяйте соответствующие вычисленные параметры типографики.
Компонент становится видимым после скрытия Инициализируйте его или пересоздайте строки, когда он получит нужные отображаемые размеры.

Для раскладки, зависящей от контейнера, ResizeObserver сообщает об изменениях размеров элементов независимо от изменения окна просмотра. Наблюдайте за контейнером, который задаёт входное значение размера. Наблюдение только за текстовым блоком не гарантирует уведомлений обо всех изменениях вычисленных стилей. ResizeObserver

Не пересоздавайте строки без разбора при каждом изменении наблюдаемой высоты. Пересоздание само может повлиять на раскладку. MDN описывает циклы Resize Observer и объясняет, что для предотвращения зацикливания нужно контролировать зависимости; перенос работы на следующий кадр не устраняет исходный бесконечный цикл. Перед новым запросом на пересоздание сравнивайте значимые входные данные. Ошибки наблюдения ResizeObserver

Если текст заголовка меняется, восстановите исходный вид старого разделения до замены содержимого, затем создайте новый экземпляр на основе нового исходного текста. GSAP указывает, что revert() восстанавливает содержимое до разделения. Если редактировать созданные обёртки, а затем отменить разделение, изменения могут быть потеряны. revert()

Проверяйте перенос, не меняя формирование текста в браузере

Откройте локальный предварительный просмотр в браузере на Mac и медленно меняйте размер окна, затем отпустите его. После завершения пересоздания и появления текста сравните последние слова каждой строки в обеих копиях. Повторите проверку при максимальной ширине контейнера и при достаточно узком контейнере, который может сжиматься. На каждом размере нажмите Toggle spacing.

Если копии не совпадают, выполните диагностику в таком порядке:

  1. Убедитесь, что у обеих копий одинаковы доступная ширина и вычисленные параметры типографики.
  2. Пересоздайте строки при текущем размере. Если несовпадение исчезло, выясните, какого триггера не хватало.
  3. Если несовпадение осталось, удалите стили с созданных обёрток строк и проверьте снова.
  4. Восстановите неразбитый текст и убедитесь, что базовый CSS задаёт нужный перенос.
  5. Меняйте по одному входному параметру раскладки, пока не удастся воспроизвести различие.

Оставьте формирование символов браузерному текстовому движку. W3C описывает случаи, когда формирование должно продолжаться через границы встроенных элементов, и случаи, когда оно должно прерываться, в том числе при разделении отступами, рамками или внешними полями встроенных элементов. Это означает, что стили обёрток могут повлиять на измеряемый результат. Не добавляйте интервалы между отдельными символьными блоками, чтобы лишь воссоздать прежний снимок экрана. Формирование текста через границы элементов

Не считайте визуальные строки, разбивая innerText по символам новой строки. WHATWG определяет innerText как отображаемый текст со своими правилами сбора текста, явных разрывов и структурных границ; это свойство не возвращает массив визуальных строк с мягким переносом. Сравнивайте результат с неразбитым отображением текста в браузере. Свойства innerText и outerText

Соблюдайте задокументированные ограничения библиотеки: GSAP рекомендует не использовать text-wrap: balance при разделении текста и указывает, что SplitText не предназначен для узлов SVG с текстом. Для заголовков с балансировкой оставьте текст неразбитым и анимируйте весь элемент; для обёрток строк используйте обычный перенос в HTML-элементе с текстом. Советы и ограничения SplitText

Завершайте работу, когда разделённая копия следует за неразбитой при фактических изменениях ширины и типографики. Размещайте явные вызовы пересоздания рядом с изменениями, которые их требуют, сохраняйте эталон для диагностики и удалите временный дубликат и контуры строк перед тем, как встроить заголовок в готовую страницу.

Источники