Базовый обзор того, как создавать анимацию разделенных букв и слов.
В этой публикации я хочу поделиться мыслями о способах решения проблемы анимации и взаимодействия с разделённым текстом в вебе, которые были бы минималистичны, доступны и работали бы во всех браузерах. Попробуйте демо .
Если вы предпочитаете видео, вот версия этого поста на YouTube:
Обзор
Анимация разделённого текста может быть потрясающей. В этой статье мы лишь поверхностно коснёмся потенциала анимации, но она закладывает основу для дальнейшего развития. Цель — постепенная анимация. Текст должен быть читабельным по умолчанию, а анимация должна быть построена поверх него. Эффекты движения разделённого текста могут быть экстравагантными и потенциально деструктивными, поэтому мы будем изменять HTML или применять стили движения только в том случае, если пользователь не против движения.
Вот общий обзор рабочего процесса и результатов:
- Подготовьте условные переменные сокращенного движения для CSS и JS.
- Подготовка утилит разделения текста в JavaScript.
- Организуйте условные операторы и утилиты при загрузке страницы.
- Напишите CSS-переходы и анимацию для букв и слов (крутая часть!).
Вот предварительный просмотр условных результатов, которые мы получим:

Если пользователь предпочитает минимальную анимацию, мы оставляем HTML-документ без изменений и не анимируем его. Если же с анимацией всё в порядке, мы разбиваем его на части. Вот предварительный просмотр HTML-кода после того, как JavaScript разделил текст по буквам.

Подготовка условных предложений
В этом проекте будет использоваться удобный медиазапрос @media (prefers-reduced-motion: reduce)
из CSS и JavaScript. Этот медиазапрос — наше основное условие для принятия решения о разделении текста. CSS-медиазапрос будет использоваться для блокировки переходов и анимации, а JavaScript-медиазапрос — для блокировки HTML-манипуляций.
Подготовка условного CSS
Я использовал PostCSS для включения синтаксиса Media Queries Level 5 , где я могу сохранить логическое значение медиа-запроса в переменной:
@custom-media --motionOK (prefers-reduced-motion: no-preference);
Подготовка условного выражения JS
В JavaScript браузер предоставляет способ проверки медиа-запросов. Я использовал деструктуризацию для извлечения и переименования логического результата проверки медиа-запроса:
const {matches:motionOK} = window.matchMedia(
'(prefers-reduced-motion: no-preference)'
)
Затем я могу проверить motionOK
и изменить документ только в том случае, если пользователь не запросил уменьшение движения.
if (motionOK) {
// document split manipulations
}
Я могу проверить то же значение, используя PostCSS для включения синтаксиса @nest
из Nesting Draft 1. Это позволяет хранить всю логику анимации и требования к её стилю для родительского и дочерних элементов в одном месте:
letter-animation {
@media (--motionOK) {
/* animation styles */
}
}
Используя пользовательское свойство PostCSS и логическое значение JavaScript, мы готовы к условному обновлению эффекта. Это подводит нас к следующему разделу, где я подробно разберу JavaScript-код для преобразования строк в элементы.
Разделение текста
Буквы текста, слова, строки и т. д. невозможно анимировать по отдельности с помощью CSS или JavaScript. Для достижения этого эффекта нам нужны блоки. Если мы хотим анимировать каждую букву, то каждая буква должна быть элементом. Если мы хотим анимировать каждое слово, то каждое слово должно быть элементом.
- Создание вспомогательных функций JavaScript для разделения строк на элементы
- Организовать использование этих утилит
Функция полезности разделения букв
Интересно начать с функции, которая принимает строку и возвращает каждую букву в массиве.
export const byLetter = text =>
[...text].map(span)
Расширенный синтаксис ES6 действительно помог сделать эту задачу быстрой.
Функция полезности разделения слов
Подобно разделению букв, эта функция принимает строку и возвращает каждое слово в массиве.
export const byWord = text =>
text.split(' ').map(span)
Метод split()
в строках JavaScript позволяет нам указывать, по каким символам следует производить разделение. Я передал пустой пробел, обозначающий разделение между словами.
Создание полезной функции коробок
Эффект требует создания квадратов для каждой буквы, и в этих функциях мы видим, что map()
вызывается с функцией span()
. Вот функция span()
.
const span = (text, index) => {
const node = document.createElement('span')
node.textContent = text
node.style.setProperty('--index', index)
return node
}
Важно отметить, что пользовательское свойство --index
задаётся с позицией массива. Наличие полей для анимации букв — это здорово, но индекс для использования в CSS — это, казалось бы, небольшое дополнение с большим эффектом. Наиболее заметным в этом большом эффекте является staggering . Мы сможем использовать --index
для смещения анимации, чтобы добиться эффекта сдвига.
Заключение по коммунальным услугам
Модуль splitting.js
в завершенном виде:
const span = (text, index) => {
const node = document.createElement('span')
node.textContent = text
node.style.setProperty('--index', index)
return node
}
export const byLetter = text =>
[...text].map(span)
export const byWord = text =>
text.split(' ').map(span)
Далее следует импорт и использование функций byLetter()
и byWord()
.
Раздельная оркестровка
После того, как утилиты для разделения готовы к использованию, собрать все это вместе означает:
- Поиск элементов, которые нужно разделить
- Разделяем их и заменяем текст на HTML
После этого CSS возьмет на себя управление и начнет анимировать элементы/блоки.
Поиск элементов
Я решил использовать атрибуты и значения для хранения информации о желаемой анимации и способе разделения текста. Мне понравилось добавлять эти декларативные параметры в HTML. Атрибут split-by
используется в JavaScript для поиска элементов и создания блоков для букв или слов. Атрибут letter-animation
или word-animation
используется в CSS для выделения дочерних элементов и применения преобразований и анимации.
Вот пример HTML, демонстрирующий два атрибута:
<h1 split-by="letter" letter-animation="breath">animated letters</h1>
<h1 split-by="word" word-animation="trampoline">hover the words</h1>
Поиск элементов из JavaScript
Я использовал синтаксис селектора CSS для наличия атрибута, чтобы собрать список элементов, текст которых нужно разделить:
const splitTargets = document.querySelectorAll('[split-by]')
Поиск элементов из CSS
Я также использовал селектор наличия атрибута в CSS, чтобы задать для всех анимаций букв одинаковые базовые стили. Позже мы используем значение атрибута, чтобы добавить более специфичные стили для достижения нужного эффекта.
letter-animation {
@media (--motionOK) {
/* animation styles */
}
}
Разделение текста на месте
Для каждой из целей разделения, найденных в JavaScript, мы разделим текст на основе значения атрибута и сопоставим каждую строку с <span>
. Затем мы можем заменить текст элемента созданными нами блоками:
splitTargets.forEach(node => {
const type = node.getAttribute('split-by')
let nodes = null
if (type === 'letter') {
nodes = byLetter(node.innerText)
}
else if (type === 'word') {
nodes = byWord(node.innerText)
}
if (nodes) {
node.firstChild.replaceWith(...nodes)
}
})
Заключение оркестровки
index.js
в завершении:
import {byLetter, byWord} from './splitting.js'
const {matches:motionOK} = window.matchMedia(
'(prefers-reduced-motion: no-preference)'
)
if (motionOK) {
const splitTargets = document.querySelectorAll('[split-by]')
splitTargets.forEach(node => {
const type = node.getAttribute('split-by')
let nodes = null
if (type === 'letter')
nodes = byLetter(node.innerText)
else if (type === 'word')
nodes = byWord(node.innerText)
if (nodes)
node.firstChild.replaceWith(...nodes)
})
}
JavaScript можно прочитать на следующем английском языке:
- Импортируем некоторые вспомогательные функции.
- Проверьте, разрешено ли движение для этого пользователя, если нет, то ничего не делайте.
- Для каждого элемента, который требуется разделить.
- Разделите их в зависимости от того, как они хотят, чтобы их разделили.
- Замените текст элементами.
Разделение анимаций и переходов
Описанная выше манипуляция с разделением документа открыла множество потенциальных возможностей для создания анимаций и эффектов с помощью CSS и JavaScript. В конце статьи есть несколько ссылок, которые помогут вам развить свой творческий потенциал.
Пора показать, на что вы способны! Я поделюсь четырьмя примерами CSS-анимаций и переходов. 🤓
Раздельные буквы
В качестве основы для эффектов разделения букв мне пригодился следующий CSS-код. Я помещаю все переходы и анимацию за медиазапрос движения, а затем присваиваю каждому новому дочернему span
свойство display и стиль для работы с пробелами:
[letter-animation] > span {
display: inline-block;
white-space: break-spaces;
}
Стиль пробелов важен, чтобы элементы span, состоящие только из пробелов, не схлопывались движком вёрстки. Теперь перейдём к интересным вещам с состоянием.
Пример перехода через разделенные буквы
В этом примере CSS-переходы используются для создания эффекта разделения текста. С помощью переходов нам нужны состояния, между которыми движок будет анимировать. Я выбрал три состояния: без наведения, наведение курсора на предложение и наведение курсора на букву.
Когда пользователь наводит курсор на предложение, то есть на контейнер, я уменьшаю масштаб всех дочерних элементов, как будто пользователь отодвинул их. Затем, когда пользователь наводит курсор на букву, я вывожу её вперёд.
@media (--motionOK) {
[letter-animation="hover"] {
&:hover > span {
transform: scale(.75);
}
& > span {
transition: transform .3s ease;
cursor: pointer;
&:hover {
transform: scale(1.25);
}
}
}
}
Пример анимированных разделенных букв
В этом примере используется предопределенная анимация @keyframe
для бесконечной анимации каждой буквы, а также встроенный индекс пользовательского свойства для создания эффекта смещения.
@media (--motionOK) {
[letter-animation="breath"] > span {
animation:
breath 1200ms ease
calc(var(--index) * 100 * 1ms)
infinite alternate;
}
}
@keyframes breath {
from {
animation-timing-function: ease-out;
}
to {
transform: translateY(-5px) scale(1.25);
text-shadow: 0 0 25px var(--glow-color);
animation-timing-function: ease-in-out;
}
}
Разделить слова
В этих примерах Flexbox использовался мной в качестве типа контейнера, удачно используя единицу ch
в качестве разумной длины зазора.
word-animation {
display: inline-flex;
flex-wrap: wrap;
gap: 1ch;
}
Пример перехода разделенных слов
В этом примере перехода я снова использую наведение. Поскольку эффект изначально скрывает контент до наведения, я позаботился о том, чтобы взаимодействие и стили применялись только при наличии возможности наведения на устройство.
@media (hover) {
[word-animation="hover"] {
overflow: hidden;
overflow: clip;
& > span {
transition: transform .3s ease;
cursor: pointer;
&:not(:hover) {
transform: translateY(50%);
}
}
}
}
Пример анимированного разделения слов
В этом примере анимации я снова использую CSS @keyframes
для создания ступенчатой бесконечной анимации для обычного абзаца текста.
[word-animation="trampoline"] > span {
display: inline-block;
transform: translateY(100%);
animation:
trampoline 3s ease
calc(var(--index) * 150 * 1ms)
infinite alternate;
}
@keyframes trampoline {
0% {
transform: translateY(100%);
animation-timing-function: ease-out;
}
50% {
transform: translateY(0);
animation-timing-function: ease-in;
}
}
Заключение
Теперь, когда вы знаете, как это сделал я, как бы поступили вы?! 🙂
Давайте разнообразим наши подходы и изучим все способы разработки в интернете. Создайте Codepen или разместите свою собственную демо-версию, напишите мне в Твиттере, и я добавлю её в раздел «Ремиксы сообщества» ниже.
Источник
Больше демонстраций и вдохновения
Ремиксы сообщества
- Веб-компонент
<text-hover>
от gnehcwu на CodeSandbox