← HTML & CSS/CSS Анимации: Web Animations API#34 из 383← ПредыдущийСледующий →+20 XP
Полезно по теме:Гайд: старт в frontendПрактика: DOM и событияТермин: DOMМаршрут: старт с нуля

CSS Анимации: Web Animations API

CSS @keyframes + animation — декларативный способ анимировать. Web Animations API (WAAPI) — его JavaScript-аналог: те же возможности, но с программным управлением: пауза, перемотка, реверс, динамические параметры.

element.animate() — основы

const element = document.querySelector('.box')

// Создаёт анимацию и сразу запускает её
const animation = element.animate(
  // Keyframes — массив или объект
  [
    { transform: 'translateX(0)',    opacity: 1 },
    { transform: 'translateX(200px)', opacity: 0.5 },
    { transform: 'translateX(400px)', opacity: 1 },
  ],
  // Timing options
  {
    duration: 1000,       // мс
    iterations: 2,        // Количество повторений (Infinity — бесконечно)
    direction: 'alternate', // normal | reverse | alternate | alternate-reverse
    easing: 'ease-in-out',  // или кривая Безье: 'cubic-bezier(0.4, 0, 0.2, 1)'
    fill: 'forwards',       // none | forwards | backwards | both
    delay: 200,             // Задержка перед стартом
  }
)

Управление воспроизведением

animation.pause()           // Пауза
animation.play()            // Запуск/продолжение
animation.reverse()         // Реверс (меняет направление)
animation.cancel()          // Отменить и вернуть в исходное состояние
animation.finish()          // Перемотать в конец

// Прогресс
animation.currentTime = 500  // Установить позицию в мс
animation.playbackRate = 2   // Скорость (2 = вдвое быстрее, -1 = назад)

// Состояние
console.log(animation.playState)      // 'idle' | 'running' | 'paused' | 'finished'
console.log(animation.currentTime)    // текущее время в мс
console.log(animation.effect.getTiming().duration)  // длительность

События анимации

animation.addEventListener('finish', () => {
  console.log('Анимация завершена')
})

animation.addEventListener('cancel', () => {
  console.log('Анимация отменена')
})

// Promise-based API
animation.finished.then(() => {
  console.log('Можно делать следующее действие')
})

animation.ready.then(() => {
  console.log('Анимация готова к воспроизведению')
})

KeyframeEffect — разделение эффекта и воспроизведения

// Создаём эффект отдельно от анимации
const effect = new KeyframeEffect(
  element,
  [
    { transform: 'scale(1)', offset: 0 },    // offset: 0–1 (как % в @keyframes)
    { transform: 'scale(1.2)', offset: 0.5 },
    { transform: 'scale(1)', offset: 1 },
  ],
  { duration: 600, easing: 'ease' }
)

const animation = new Animation(effect, document.timeline)
animation.play()

Советы по производительности

Анимировать безопасно (через compositing, не вызывает layout):

  • transform (translate, scale, rotate)
  • opacity
  • filter
  • Анимировать дорого (вызывает layout/paint):

  • width, height, top, left
  • margin, padding
  • font-size
  • // Правильно — анимируем transform
    element.animate([
      { transform: 'translateY(0)' },
      { transform: 'translateY(-20px)' },
    ], { duration: 300, fill: 'forwards' })
    
    // Дорого — анимируем height
    element.animate([
      { height: '0px' },
      { height: '200px' },
    ], { duration: 300 })  // Вызывает layout на каждом кадре

    Сравнение с CSS-анимациями

    | CSS @keyframes | Web Animations API |

    |-----------------------------|----------------------------------|

    | Декларативно в CSS | Программно в JS |

    | Нет pause/play из JS | Полное управление |

    | Статические параметры | Динамические: duration, easing |

    | animationend событие | Promise animation.finished |

    | Нет доступа к прогрессу | currentTime / playbackRate |

    Примеры

    Контроллер анимации через Web Animations API с play, pause, reverse и отображением прогресса

    // Web Animations API — полный контроль над анимацией
    const box = document.createElement('div')
    box.style.cssText = `
      width: 60px; height: 60px;
      background: #7b2ff7;
      border-radius: 8px;
      margin: 20px;
    `
    document.body.appendChild(box)
    
    // Создаём анимацию через element.animate()
    const animation = box.animate(
      [
        { transform: 'translateX(0px) rotate(0deg)',   background: '#7b2ff7' },
        { transform: 'translateX(200px) rotate(180deg)', background: '#06b6d4' },
        { transform: 'translateX(0px) rotate(360deg)', background: '#7b2ff7' },
      ],
      {
        duration: 2000,
        iterations: Infinity,
        easing: 'ease-in-out',
      }
    )
    
    // Панель управления
    const controls = document.createElement('div')
    controls.style.cssText = 'display: flex; gap: 8px; margin: 8px 20px; flex-wrap: wrap;'
    document.body.appendChild(controls)
    
    function makeBtn(label, onClick) {
      const btn = document.createElement('button')
      btn.textContent = label
      btn.style.cssText = 'padding: 6px 12px; border-radius: 4px; border: 1px solid #7b2ff7; background: white; cursor: pointer; font-size: 13px;'
      btn.onclick = onClick
      controls.appendChild(btn)
      return btn
    }
    
    makeBtn('Пауза', () => { animation.pause(); logState() })
    makeBtn('Играть', () => { animation.play(); logState() })
    makeBtn('Реверс', () => { animation.reverse(); logState() })
    makeBtn('2x скорость', () => { animation.playbackRate = 2; logState() })
    makeBtn('0.5x скорость', () => { animation.playbackRate = 0.5; logState() })
    makeBtn('Перемотать на 50%', () => {
      const timing = animation.effect.getTiming()
      animation.currentTime = timing.duration * 0.5
      logState()
    })
    
    const stateEl = document.createElement('pre')
    stateEl.style.cssText = 'margin: 8px 20px; font-size: 12px; background: #1a202c; color: #a0aec0; padding: 8px; border-radius: 4px;'
    document.body.appendChild(stateEl)
    
    function logState() {
      const timing = animation.effect.getTiming()
      const progress = animation.currentTime !== null
        ? ((animation.currentTime % timing.duration) / timing.duration * 100).toFixed(1)
        : 0
      stateEl.textContent = [
        `playState: ${animation.playState}`,
        `currentTime: ${Math.round(animation.currentTime ?? 0)}ms`,
        `progress: ${progress}%`,
        `playbackRate: ${animation.playbackRate}x`,
        `duration: ${timing.duration}ms`,
      ].join('\n')
    }
    
    // Обновляем состояние каждые 100ms
    setInterval(logState, 100)
    logState()
    
    console.log('animation.playState:', animation.playState)  // running
    console.log('Используй кнопки для управления анимацией')

    CSS Анимации: Web Animations API

    CSS @keyframes + animation — декларативный способ анимировать. Web Animations API (WAAPI) — его JavaScript-аналог: те же возможности, но с программным управлением: пауза, перемотка, реверс, динамические параметры.

    element.animate() — основы

    const element = document.querySelector('.box')
    
    // Создаёт анимацию и сразу запускает её
    const animation = element.animate(
      // Keyframes — массив или объект
      [
        { transform: 'translateX(0)',    opacity: 1 },
        { transform: 'translateX(200px)', opacity: 0.5 },
        { transform: 'translateX(400px)', opacity: 1 },
      ],
      // Timing options
      {
        duration: 1000,       // мс
        iterations: 2,        // Количество повторений (Infinity — бесконечно)
        direction: 'alternate', // normal | reverse | alternate | alternate-reverse
        easing: 'ease-in-out',  // или кривая Безье: 'cubic-bezier(0.4, 0, 0.2, 1)'
        fill: 'forwards',       // none | forwards | backwards | both
        delay: 200,             // Задержка перед стартом
      }
    )

    Управление воспроизведением

    animation.pause()           // Пауза
    animation.play()            // Запуск/продолжение
    animation.reverse()         // Реверс (меняет направление)
    animation.cancel()          // Отменить и вернуть в исходное состояние
    animation.finish()          // Перемотать в конец
    
    // Прогресс
    animation.currentTime = 500  // Установить позицию в мс
    animation.playbackRate = 2   // Скорость (2 = вдвое быстрее, -1 = назад)
    
    // Состояние
    console.log(animation.playState)      // 'idle' | 'running' | 'paused' | 'finished'
    console.log(animation.currentTime)    // текущее время в мс
    console.log(animation.effect.getTiming().duration)  // длительность

    События анимации

    animation.addEventListener('finish', () => {
      console.log('Анимация завершена')
    })
    
    animation.addEventListener('cancel', () => {
      console.log('Анимация отменена')
    })
    
    // Promise-based API
    animation.finished.then(() => {
      console.log('Можно делать следующее действие')
    })
    
    animation.ready.then(() => {
      console.log('Анимация готова к воспроизведению')
    })

    KeyframeEffect — разделение эффекта и воспроизведения

    // Создаём эффект отдельно от анимации
    const effect = new KeyframeEffect(
      element,
      [
        { transform: 'scale(1)', offset: 0 },    // offset: 0–1 (как % в @keyframes)
        { transform: 'scale(1.2)', offset: 0.5 },
        { transform: 'scale(1)', offset: 1 },
      ],
      { duration: 600, easing: 'ease' }
    )
    
    const animation = new Animation(effect, document.timeline)
    animation.play()

    Советы по производительности

    Анимировать безопасно (через compositing, не вызывает layout):

  • transform (translate, scale, rotate)
  • opacity
  • filter
  • Анимировать дорого (вызывает layout/paint):

  • width, height, top, left
  • margin, padding
  • font-size
  • // Правильно — анимируем transform
    element.animate([
      { transform: 'translateY(0)' },
      { transform: 'translateY(-20px)' },
    ], { duration: 300, fill: 'forwards' })
    
    // Дорого — анимируем height
    element.animate([
      { height: '0px' },
      { height: '200px' },
    ], { duration: 300 })  // Вызывает layout на каждом кадре

    Сравнение с CSS-анимациями

    | CSS @keyframes | Web Animations API |

    |-----------------------------|----------------------------------|

    | Декларативно в CSS | Программно в JS |

    | Нет pause/play из JS | Полное управление |

    | Статические параметры | Динамические: duration, easing |

    | animationend событие | Promise animation.finished |

    | Нет доступа к прогрессу | currentTime / playbackRate |

    Примеры

    Контроллер анимации через Web Animations API с play, pause, reverse и отображением прогресса

    // Web Animations API — полный контроль над анимацией
    const box = document.createElement('div')
    box.style.cssText = `
      width: 60px; height: 60px;
      background: #7b2ff7;
      border-radius: 8px;
      margin: 20px;
    `
    document.body.appendChild(box)
    
    // Создаём анимацию через element.animate()
    const animation = box.animate(
      [
        { transform: 'translateX(0px) rotate(0deg)',   background: '#7b2ff7' },
        { transform: 'translateX(200px) rotate(180deg)', background: '#06b6d4' },
        { transform: 'translateX(0px) rotate(360deg)', background: '#7b2ff7' },
      ],
      {
        duration: 2000,
        iterations: Infinity,
        easing: 'ease-in-out',
      }
    )
    
    // Панель управления
    const controls = document.createElement('div')
    controls.style.cssText = 'display: flex; gap: 8px; margin: 8px 20px; flex-wrap: wrap;'
    document.body.appendChild(controls)
    
    function makeBtn(label, onClick) {
      const btn = document.createElement('button')
      btn.textContent = label
      btn.style.cssText = 'padding: 6px 12px; border-radius: 4px; border: 1px solid #7b2ff7; background: white; cursor: pointer; font-size: 13px;'
      btn.onclick = onClick
      controls.appendChild(btn)
      return btn
    }
    
    makeBtn('Пауза', () => { animation.pause(); logState() })
    makeBtn('Играть', () => { animation.play(); logState() })
    makeBtn('Реверс', () => { animation.reverse(); logState() })
    makeBtn('2x скорость', () => { animation.playbackRate = 2; logState() })
    makeBtn('0.5x скорость', () => { animation.playbackRate = 0.5; logState() })
    makeBtn('Перемотать на 50%', () => {
      const timing = animation.effect.getTiming()
      animation.currentTime = timing.duration * 0.5
      logState()
    })
    
    const stateEl = document.createElement('pre')
    stateEl.style.cssText = 'margin: 8px 20px; font-size: 12px; background: #1a202c; color: #a0aec0; padding: 8px; border-radius: 4px;'
    document.body.appendChild(stateEl)
    
    function logState() {
      const timing = animation.effect.getTiming()
      const progress = animation.currentTime !== null
        ? ((animation.currentTime % timing.duration) / timing.duration * 100).toFixed(1)
        : 0
      stateEl.textContent = [
        `playState: ${animation.playState}`,
        `currentTime: ${Math.round(animation.currentTime ?? 0)}ms`,
        `progress: ${progress}%`,
        `playbackRate: ${animation.playbackRate}x`,
        `duration: ${timing.duration}ms`,
      ].join('\n')
    }
    
    // Обновляем состояние каждые 100ms
    setInterval(logState, 100)
    logState()
    
    console.log('animation.playState:', animation.playState)  // running
    console.log('Используй кнопки для управления анимацией')

    Задание

    Создай страницу с тремя анимированными карточками, которые появляются последовательно с задержкой (cascading). Используй `@keyframes fadeInUp` — карточки должны выезжать снизу вверх и проявляться. Каждая следующая карточка начинает анимацию на 150ms позже предыдущей через `animation-delay`.

    Подсказка

    В `fadeInUp`: `from` — `opacity: 0`, `translateY(20px)`; `to` — `opacity: 1`, `translateY(0)`. Длительность анимации `0.4s`. Задержки: первая — `0s`, вторая — `0.15s`, третья — `0.3s`. `animation-fill-mode: both` сохраняет конечное состояние.

    Загружаем среду выполнения...
    Загружаем AI-помощника...