События клика являются одним из ключевых триггеров интерактивной
анимации. В библиотеке Motion One реакция на клики реализуется через
стандартные механизмы JavaScript (обработчики событий) в сочетании с
функцией animate. Это позволяет создавать мгновенные,
плавные и контролируемые визуальные отклики на действия
пользователя.
Минимальный сценарий включает:
clickimport { animate } from "motion"
const button = document.querySelector(".button")
button.addEventListener("click", () => {
animate(button, { scale: 1.2 }, { duration: 0.2 })
})
Ключевые моменты:
Один из самых распространённых паттернов — имитация физического нажатия:
button.addEventListener("click", () => {
animate(
button,
{ scale: [1, 0.9, 1] },
{ duration: 0.3, easing: "ease-out" }
)
})
Особенности:
Часто требуется не просто одноразовая анимация, а переключение между состояниями:
let active = false
button.addEventListener("click", () => {
active = !active
animate(button, {
backgroundColor: active ? "#4caf50" : "#f44336",
scale: active ? 1.2 : 1
})
})
Здесь:
Клик по одному элементу может запускать анимацию других:
const items = document.querySelectorAll(".item")
button.addEventListener("click", () => {
animate(items, { opacity: [0, 1], y: [20, 0] }, {
delay: (i) => i * 0.1,
duration: 0.4
})
})
Особенности:
delay для создания каскадаMotion One поддерживает широкий спектр CSS-свойств:
animate(el, { rotate: 180, scale: 1.5 })
animate(el, { opacity: 0 })
animate(el, { backgroundColor: "#000" })
animate(el, { x: 100, y: -50 })
Настройки времени играют ключевую роль:
animate(el,
{ scale: 1.3 },
{
duration: 0.5,
easing: "cubic-bezier(0.22, 1, 0.36, 1)"
}
)
Параметры:
duration — длительностьdelay — задержкаeasing — кривая ускоренияrepeat — количество повторовПо умолчанию Motion One всегда запускает новую анимацию, даже если предыдущая не завершена. Это важно учитывать:
button.addEventListener("click", () => {
animate(button, { rotate: 360 }, { duration: 1 })
})
Каждый клик добавляет новую анимацию — возможен “накопительный” эффект.
animate возвращает контроллер:
const animation = animate(el, { x: 200 }, { duration: 1 })
animation.pause()
animation.play()
animation.cancel()
Использование внутри клика:
let animation
button.addEventListener("click", () => {
if (animation) {
animation.cancel()
}
animation = animate(el, { x: 200 })
})
Можно считывать текущее значение перед анимацией:
button.addEventListener("click", () => {
const currentScale = getComputedStyle(button).transform
animate(button, { scale: 1.5 })
})
Это полезно при сложных сценариях, где состояние изменяется вне анимации.
Иногда логика разделяется:
button.addEventListener("click", () => {
button.classList.toggle("active")
animate(button, {
scale: button.classList.contains("active") ? 1.2 : 1
})
})
Motion One отлично работает с SVG:
const circle = document.querySelector("circle")
circle.addEventListener("click", () => {
animate(circle, { r: 50 }, { duration: 0.3 })
})
Можно запускать последовательные действия:
button.addEventListener("click", async () => {
await animate(button, { scale: 0.8 }, { duration: 0.1 }).finished
await animate(button, { scale: 1.2 }, { duration: 0.2 }).finished
await animate(button, { scale: 1 }, { duration: 0.1 }).finished
})
Важно:
finished (Promise)awaitРазличение типов событий:
button.addEventListener("mousedown", () => {
animate(button, { scale: 0.9 }, { duration: 0.1 })
})
button.addEventListener("mouseup", () => {
animate(button, { scale: 1 }, { duration: 0.1 })
})
Это даёт более “живую” реакцию, чем обычный click.
Чтобы избежать перегрузки:
let isAnimating = false
button.addEventListener("click", async () => {
if (isAnimating) return
isAnimating = true
await animate(button, { rotate: 360 }, { duration: 1 }).finished
isAnimating = false
})
Клик может изменять не только UI, но и данные:
let count = 0
button.addEventListener("click", () => {
count++
animate(button, {
scale: [1, 1.3, 1]
})
console.log(count)
})
Рекомендуется:
transform и opacitywidth, height, top,
leftПример оптимального варианта:
animate(el, { transform: "translateX(100px)" })
1. Повторное создание обработчиков
// плохо — обработчик добавляется многократно
button.oncl ick = () => { ... }
2. Отсутствие контроля состояния
Приводит к конфликтующим анимациям.
3. Анимация layout-свойств
Снижает производительность.
button.addEventListener("click", () => {
animate(".particle", {
x: () => Math.random() * 200 - 100,
y: () => Math.random() * 200 - 100,
opacity: [1, 0]
}, {
duration: 0.6
})
})
card.addEventListener("click", () => {
animate(card, {
rotateY: [0, 180]
}, {
duration: 0.6
})
})
Типичный поток: