В Popmotion управление с клавиатуры строится вокруг событийной
модели. Клавиатура рассматривается как поток дискретных сигналов,
которые преобразуются в значения для анимаций, трансформаций и состояния
интерфейса. Базовый инструмент для этого — listen,
позволяющий подписываться на DOM-события и преобразовывать их в
управляемые потоки.
Ключевая особенность подхода заключается в том, что клавиатурный ввод не обрабатывается напрямую внутри логики анимации. Вместо этого события нормализуются и становятся входными данными для функций движения.
import { listen } from 'popmotion';
listen(document, 'keydown')
.start((event) => {
console.log(event.key);
});
На этом уровне клавиатура уже превращается в поток данных, который можно фильтровать, преобразовывать и связывать с любыми значениями движения.
Работа с клавиатурой в интерактивной анимации требует выделения ограниченного набора управляющих клавиш. Чаще всего это стрелки, пробел, Enter и Escape. Фильтрация выполняется на уровне обработчика события.
listen(document, 'keydown')
.start(({ key }) => {
if (key === 'ArrowLeft') {
console.log('влево');
}
if (key === 'ArrowRight') {
console.log('вправо');
}
});
При построении более сложных систем управления ввод разделяется на логические группы: навигация, подтверждение действий, управление скоростью, переключение режимов.
В Popmotion управление движением часто требует перевода дискретных событий в непрерывные значения. Например, стрелки могут изменять координату объекта.
import { listen, value } from 'popmotion';
const x = value(0, (v) => {
box.style.transform = `translateX(${v}px)`;
});
listen(document, 'keydown')
.start(({ key }) => {
if (key === 'ArrowRight') x.set(x.get() + 10);
if (key === 'ArrowLeft') x.set(x.get() - 10);
});
Здесь клавиатура управляет состоянием value, а Popmotion
отвечает за синхронизацию состояния и визуального представления.
Прямое изменение значения даёт резкий отклик. Для более естественного
поведения ввод связывается с физическими моделями, например
spring. В этом случае клавиша задаёт цель, а система плавно
к ней движется.
import { listen, value, spring } from 'popmotion';
const x = value(0, (v) => {
box.style.transform = `translateX(${v}px)`;
});
let currentSpring;
const moveTo = (target) => {
if (currentSpring) currentSpring.stop();
currentSpring = spring({
from: x.get(),
to: target,
stiffness: 200,
damping: 20
}).start(x);
};
listen(document, 'keydown')
.start(({ key }) => {
if (key === 'ArrowRight') moveTo(x.get() + 100);
if (key === 'ArrowLeft') moveTo(x.get() - 100);
});
Такой подход создаёт ощущение инерции и физической массы объекта, управляемого клавиатурой.
Для реализации движения при удержании клавиш используется сочетание
keydown и keyup. Состояние нажатия хранится
отдельно, а движение обновляется в цикле.
import { listen, value, ticker } from 'popmotion';
const x = value(0, (v) => {
box.style.transform = `translateX(${v}px)`;
});
let left = false;
let right = false;
listen(document, 'keydown').start(({ key }) => {
if (key === 'ArrowLeft') left = true;
if (key === 'ArrowRight') right = true;
});
listen(document, 'keyup').start(({ key }) => {
if (key === 'ArrowLeft') left = false;
if (key === 'ArrowRight') right = false;
});
ticker(({ delta }) => {
if (left) x.update(v => v - delta * 0.5);
if (right) x.update(v => v + delta * 0.5);
}).start();
Здесь ticker играет роль игрового цикла,
синхронизированного с частотой обновления экрана.
Клавиатурный ввод может запускать анимации фиксированной длительности
через keyframes. Это удобно для переключения состояний
интерфейса.
import { listen, keyframes } from 'popmotion';
const fade = keyframes({
values: [1, 0, 1],
duration: 600,
onUpdate: (v) => {
box.style.opacity = v;
}
});
listen(document, 'keydown')
.start(({ key }) => {
if (key === ' ') fade.start();
});
Такое управление часто используется для эффектов подтверждения действий или визуальной обратной связи.
В более сложных интерфейсах клавиатура становится системой команд. Каждая клавиша соответствует действию, которое изменяет глобальное состояние.
const state = {
mode: 'idle',
speed: 1
};
listen(document, 'keydown')
.start(({ key }) => {
switch (key) {
case '1':
state.speed = 1;
break;
case '2':
state.speed = 2;
break;
case 'p':
state.mode = 'pause';
break;
case 'r':
state.mode = 'run';
break;
}
});
Такой подход применяется в интерфейсах, где клавиатура выполняет роль управляющей панели.
Клавиатура может управлять не одной координатой, а вектором движения. Это характерно для игр и визуальных симуляций.
import { listen, value, ticker } from 'popmotion';
const position = value({ x: 0, y: 0 }, (v) => {
box.style.transform = `translate(${v.x}px, ${v.y}px)`;
});
const velocity = { x: 0, y: 0 };
listen(document, 'keydown').start(({ key }) => {
if (key === 'ArrowUp') velocity.y = -2;
if (key === 'ArrowDown') velocity.y = 2;
if (key === 'ArrowLeft') velocity.x = -2;
if (key === 'ArrowRight') velocity.x = 2;
});
listen(document, 'keyup').start(({ key }) => {
if (key.includes('Arrow')) {
velocity.x = 0;
velocity.y = 0;
}
});
ticker(({ delta }) => {
position.update(v => ({
x: v.x + velocity.x * delta * 0.1,
y: v.y + velocity.y * delta * 0.1
}));
}).start();
Здесь клавиатура задаёт направление, а ticker отвечает
за непрерывное движение.
Для расширенного управления используется отслеживание нескольких нажатий одновременно. Это позволяет реализовать модификаторы и сложные действия.
const keys = new Set();
listen(document, 'keydown').start(({ key }) => {
keys.add(key);
if (keys.has('Shift') && keys.has('ArrowRight')) {
console.log('ускоренное движение вправо');
}
});
listen(document, 'keyup').start(({ key }) => {
keys.delete(key);
});
Комбинации позволяют расширять управляющий интерфейс без увеличения количества отдельных клавиш.
В Popmotion клавиатурный ввод часто объединяется с
pointer-событиями, создавая единый механизм управления. Это
особенно важно для интерфейсов, где пользователь может переключаться
между устройствами ввода без изменения логики.
Клавиатура в таком случае становится альтернативным источником тех же команд, что и pointer или drag.
Клавиатура может динамически изменять параметры анимации, включая скорость, сопротивление и амплитуду движения.
let speed = 1;
listen(document, 'keydown').start(({ key }) => {
if (key === '+') speed += 0.2;
if (key === '-') speed = Math.max(0.2, speed - 0.2);
});
Далее этот параметр применяется в любом вычислении движения или обновления состояния.
Ключевой архитектурный принцип — изоляция клавиатурного слоя от анимационного. Клавиатура только формирует команды и значения, но не содержит логики движения.
Слой ввода:
listen(document, 'keydown')Слой движения:
valuespringtickerkeyframesТакое разделение позволяет переиспользовать анимационную логику с разными источниками ввода без изменений.
При удержании клавиш браузер генерирует повторные
keydown события. В системах управления анимацией это может
приводить к нежелательным скачкам. Для контроля используется фильтрация
по состоянию нажатия.
let pressed = false;
listen(document, 'keydown').start(({ key }) => {
if (key === ' ' && !pressed) {
pressed = true;
console.log('однократное действие');
}
});
listen(document, 'keyup').start(({ key }) => {
if (key === ' ') pressed = false;
});
Клавиатура становится первым звеном реактивной цепочки, которая может включать трансформации, физику и визуальные обновления. Поток выглядит как последовательность преобразований:
Такая структура позволяет строить предсказуемые системы управления сложной интерактивной графикой и интерфейсами.