Клавиатурная навигация в интерфейсах с анимациями Lottie становится критическим аспектом доступности, когда анимация перестаёт быть декоративной и начинает взаимодействовать с пользователем. В таких случаях важно обеспечить полный контроль с клавиатуры над воспроизведением, паузой, перемоткой и переходом по кадрам, сохраняя предсказуемое поведение и корректную интеграцию с API Lottie Web.
Библиотека Lottie Web предоставляет объект анимации, через который осуществляется управление воспроизведением:
play() — запуск анимацииpause() — остановкаstop() — сброс в началоgoToAndStop(frame, isFrame) — переход к конкретному
кадруgoToAndPlay(frame, isFrame) — переход и запускsetSpeed(speed) — изменение скоростиdestroy() — уничтожение экземпляраКлавиатурная навигация строится поверх этих методов, превращая их в реакцию на события клавиатуры.
Для начала необходимо сделать контейнер анимации доступным для фокуса. Без этого клавиатурные события не будут логически привязаны к компоненту.
<div id="lottie" tabindex="0" aria-label="Анимация" role="application"></div>
Ключевые моменты:
tabindex="0" делает элемент доступным через Tabaria-label задаёт смысловое описаниеrole="application" используется, когда внутри есть
сложное интерактивное поведениеЕсли анимация является декоративной, вместо этого используется:
aria-hidden="true"
и клавиатурное управление не подключается.
import lottie from "lottie-web";
const animation = lottie.loadAnimation({
container: document.getElementById("lottie"),
renderer: "svg",
loop: false,
autoplay: false,
path: "/animation.json"
});
Для клавиатурной навигации важно отключить autoplay,
чтобы управление полностью принадлежало пользователю.
Основная логика строится вокруг keydown. Используется
именно это событие, чтобы перехватывать действие до стандартной
обработки браузера.
const container = document.getElementById("lottie");
container.addEventListener("keydown", (e) => {
switch (e.key) {
case " ":
case "Spacebar":
e.preventDefault();
togglePlay();
break;
case "ArrowRight":
e.preventDefault();
stepForward();
break;
case "ArrowLeft":
e.preventDefault();
stepBackward();
break;
case "Home":
e.preventDefault();
animation.goToAndStop(0, true);
break;
case "End":
e.preventDefault();
animation.goToAndStop(animation.totalFrames, true);
break;
}
});
Ключевой принцип — каждая клавиша должна иметь предсказуемое действие и не конфликтовать со стандартными браузерными сценариями.
Функция переключения состояния:
let isPlaying = false;
function togglePlay() {
if (isPlaying) {
animation.pause();
} else {
animation.play();
}
isPlaying = !isPlaying;
}
Состояние лучше отслеживать явно, так как Lottie Web не всегда предоставляет удобный реактивный флаг воспроизведения.
Для точной клавиатурной навигации используется управление кадрами:
function stepForward() {
const frame = animation.currentFrame + 1;
animation.goToAndStop(frame, true);
}
function stepBackward() {
const frame = animation.currentFrame - 1;
animation.goToAndStop(frame, true);
}
Важный момент — использование goToAndStop, а не
seek, поскольку Lottie Web работает через явные переходы
кадров.
Для более удобного взаимодействия можно вводить ускоренные шаги:
function stepForwardLarge() {
animation.goToAndStop(animation.currentFrame + 10, true);
}
Если известен FPS:
const FPS = 60;
function stepForwardSecond() {
animation.goToAndStop(animation.currentFrame + FPS, true);
}
Для плавной перемотки может использоваться удержание клавиши:
let interval;
container.addEventListener("keydown", (e) => {
if (e.key === "ArrowRight" && !interval) {
interval = setInterval(stepForward, 50);
}
});
container.addEventListener("keyup", (e) => {
if (e.key === "ArrowRight") {
clearInterval(interval);
interval = null;
}
});
Это создаёт эффект “скраббинга” по анимации, но требует осторожности, чтобы не перегружать main thread.
При работе с клавиатурной навигацией важно синхронизировать состояние анимации с доступностью:
function updateAriaState() {
container.setAttribute(
"aria-busy",
animation.isPaused ? "false" : "true"
);
}
Также можно добавлять:
aria-valueminaria-valuemaxaria-valuenowдля представления прогресса анимации как слайдера.
Если анимация управляется как временная шкала, контейнер можно представить как ползунок:
<div
id="lottie"
role="slider"
aria-valuemin="0"
aria-valuemax="100"
aria-valuenow="0"
tabindex="0">
</div>
Обновление значения:
function updateProgress() {
const progress = (animation.currentFrame / animation.totalFrames) * 100;
container.setAttribute("aria-valuenow", Math.round(progress));
}
Для синхронизации UI и анимации используется цикл обновления:
function loop() {
updateProgress();
requestAnimationFrame(loop);
}
loop();
Это позволяет клавиатурным действиям мгновенно отражаться в интерфейсе.
Клавиатурная навигация должна активироваться только при фокусе:
container.addEventListener("focus", () => {
container.classList.add("focused");
});
container.addEventListener("blur", () => {
container.classList.remove("focused");
});
И при необходимости можно приостанавливать анимацию:
container.addEventListener("blur", () => {
animation.pause();
isPlaying = false;
});
Некоторые клавиши имеют системное поведение (например, Space прокручивает страницу). Поэтому обязательна явная блокировка:
preventDefault() для Spaceif (document.activeElement !== container) return;
При наличии нескольких Lottie-инстансов клавиатурное управление должно быть изолировано:
document.querySelectorAll(".lottie").forEach((el) => {
el.addEventListener("keydown", handler);
});
И управление всегда привязывается к активному элементу:
let activeAnimation = null;
container.addEventListener("focus", () => {
activeAnimation = animation;
});
Для сложных интерфейсов применяется единый контроллер:
class LottieKeyboardController {
constructor(animation, container) {
this.animation = animation;
this.container = container;
this.bind();
}
bind() {
this.container.addEventListener("keydown", this.onKeyDown.bind(this));
}
onKeyDown(e) {
if (e.key === " ") {
e.preventDefault();
this.toggle();
}
}
toggle() {
this.animation.isPaused
? this.animation.play()
: this.animation.pause();
}
}
При использовании renderer: "svg" клавиатурная навигация
может взаимодействовать с DOM-элементами внутри SVG. Это требует:
Частая перемотка клавишами может приводить к нагрузке:
goToAndStop вызововlet lastUpdate = 0;
function throttledUpdate() {
const now = performance.now();
if (now - lastUpdate > 16) {
updateProgress();
lastUpdate = now;
}
}
Поверх базовой клавиатурной навигации можно добавлять:
+ / -)L)R)Каждое действие должно быть явно документировано через
aria-keyshortcuts:
<div aria-keyshortcuts="Space ArrowLeft ArrowRight Home End"></div>