Взлом состояния CSS-анимации и времени воспроизведения PlatoBlockchain Data Intelligence. Вертикальный поиск. Ай.

Взлом состояния CSS-анимации и времени воспроизведения

Wolfenstein только на CSS это небольшой проект, который я сделал несколько недель назад. Это был эксперимент с 3D-трансформациями и анимацией CSS.

Вдохновленный демо FPS и другое Кодовая ручка Wolfenstein, я решил построить свою собственную версию. Он основан на Эпизоде ​​1 — Этаж 9 оригинальной 3D-игры Wolfenstein.

Редактор: Эта игра намеренно требует быстрой реакции, чтобы избежать экрана Game Over.

Вот видео прохождения:

[Встраиваемое содержимое]

Короче говоря, мой проект — это не что иное, как тщательно написанная длинная CSS-анимация. Плюс несколько экземпляров взлом флажка.

:checked ~ div { animation-name: spin; }

Среда состоит из 3D-граней сетки, а анимация в основном представляет собой простые 3D-перемещения и повороты. Ничего особенного.

Однако особенно сложно было решить две проблемы:

  • Воспроизведение анимации «выстрела из оружия» всякий раз, когда игрок нажимает на врага.
  • Когда быстро движущийся босс нанесет последний удар, начните драматическое замедленное движение.

На техническом уровне это означало:

  • Воспроизвести анимацию, когда установлен следующий флажок.
  • Замедлить анимацию, когда установлен флажок.

На самом деле, ни один из них не был правильно решен в моем проекте! Я либо использовал обходные пути, либо просто сдался.

С другой стороны, немного покопавшись, я в конце концов нашел ключ к обеим проблемам: изменение свойств запускаемой CSS-анимации. В этой статье мы продолжим изучение этой темы:

  • Много интерактивных примеров.
  • Dissections: как работает (или не работает) каждый пример?
  • За кулисами: как браузеры обрабатывают состояния анимации?

Разрешите «Брось мои кирпичи».

Проблема 1: Воспроизведение анимации

Первый пример: «просто еще один флажок»

Моей первой интуицией было «просто добавить еще один флажок», который не работает:

Каждый флажок работает по отдельности, но не вместе. Если один флажок уже установлен, другой больше не работает.

Вот как это работает (или «не работает»):

  1. Ассоциация animation-name of <div> is none по умолчанию.
  2. Пользователь нажимает на один флажок, animation-name становится spin, и анимация начинается сначала.
  3. Через некоторое время пользователь нажимает на другой флажок. Новое правило CSS вступает в силу, но animation-name is все еще spin, что означает, что анимация не добавляется и не удаляется. Анимация просто продолжает воспроизводиться, как будто ничего не произошло.

Второй пример: «клонирование анимации»

Один из рабочих подходов — клонировать анимацию:

#spin1:checked ~ div { animation-name: spin1; }
#spin2:checked ~ div { animation-name: spin2; }

Вот как это работает:

  1. animation-name is none первоначально.
  2. Пользователь нажимает «Вращать!», animation-name становится spin1. Анимация spin1 запускается с самого начала, потому что он был только что добавлен.
  3. Пользователь нажимает «Вращать снова!», animation-name становится spin2. Анимация spin2 запускается с самого начала, потому что он был только что добавлен.

Обратите внимание, что на шаге № 3 spin1 удаляется из-за порядка правил CSS. Это не сработает, если «Снова закрутить!» проверяется в первую очередь.

Третий пример: «добавление той же анимации»

Другой рабочий подход — «добавить ту же анимацию»:

#spin1:checked ~ div { animation-name: spin; }
#spin2:checked ~ div { animation-name: spin, spin; }

Это похоже на предыдущий пример. На самом деле вы можете понять поведение следующим образом:

#spin1:checked ~ div { animation-name: spin1; }
#spin2:checked ~ div { animation-name: spin2, spin1; }

Обратите внимание, что когда «Вращать снова!» установлен флажок, старая анимация бега становится второй анимацией в новом списке, что может быть неинтуитивно. Прямое следствие: трюк не сработает, если animation-fill-mode is forwards. Вот демо:

Если вам интересно, почему это так, вот несколько подсказок:

  • animation-fill-mode is none по умолчанию, что означает «Анимация не имеет никакого эффекта, если не воспроизводится».
  • animation-fill-mode: forwards; означает «После того, как анимация закончила воспроизведение, она должна оставаться на последнем ключевом кадре навсегда».
  • spin1решение всегда важнее spin2потому что spin1 появляется позже в списке.
  • Предположим, пользователь нажимает «Вращать!», ждет полного вращения, затем нажимает «Вращать снова!». В этот момент. spin1 уже закончено, и spin2 только начинается.

Обсуждение

Эмпирическое правило: вы не можете «перезапустить» существующую анимацию CSS. Вместо этого вы хотите добавить и воспроизвести новую анимацию. Это может быть подтверждено спецификация W3C:

Как только анимация началась, она продолжается до тех пор, пока не закончится или не будет удалено имя анимации.

Теперь, сравнивая последние два примера, я думаю, что на практике «клонирование анимации» часто должно работать лучше, особенно когда доступен препроцессор CSS.

Проблема 2: Медленное движение

Можно подумать, что замедление анимации — это просто установка более длинного animation-duration:

div { animation-duration: 0.5s; }
#slowmo:checked ~ div { animation-duration: 1.5s; }

Действительно, это работает:

…или нет?

С помощью нескольких настроек должно быть легче увидеть проблему.

Да, анимация замедлена. И нет, это не выглядит хорошо. Собака (почти) всегда «прыгает», когда вы переключаете флажок. Кроме того, кажется, что собака прыгает в случайное положение, а не в исходное. Почему?

Было бы проще это понять, если бы мы ввели два «теневых элемента»:

Оба теневых элемента запускают одну и ту же анимацию с разными animation-duration. И они не затрагиваются флажком.

Когда вы переключаете флажок, элемент сразу же переключается между состояниями двух теневых элементов.

квотирование спецификация W3C:

Изменения значений свойств анимации во время ее выполнения применяются так, как если бы анимация имела эти значения с момента ее начала.

Это следует из без гражданства дизайн, который позволяет браузерам легко определять анимированное значение. Фактический расчет описан здесь и здесь.

Еще одна попытка

Одна из идей состоит в том, чтобы приостановить текущую анимацию, а затем добавить более медленную анимацию:

div {
  animation-name: spin1;
  animation-duration: 2s;
}

#slowmo:checked ~ div {
  animation-name: spin1, spin2;
  animation-duration: 2s, 5s;
  animation-play-state: paused, running;
}

Итак, это работает:

…или нет?

Он замедляется, когда вы нажимаете «Slowmo!». Но если вы дождетесь полного круга, то увидите «прыжок». На самом деле, он всегда переходит в положение, когда «Slowmo!» нажимается.

Причина в том, что у нас нет from ключевой кадр определен — и мы не должны. Когда пользователь нажимает «Slowmo!», spin1 останавливается в каком-то месте, и spin2 начинается точно с той же позиции. Мы просто не можем предсказать эту позицию заранее… или можем?

Рабочее решение

Мы можем! Используя пользовательское свойство, мы можем зафиксировать угол в первой анимации, а затем передать его во вторую анимацию:

div {
  transform: rotate(var(--angle1));
  animation-name: spin1;
  animation-duration: 2s;
}

#slowmo:checked ~ div {
  transform: rotate(var(--angle2));
  animation-name: spin1, spin2;
  animation-duration: 2s, 5s;
  animation-play-state: paused, running;
}

@keyframes spin1 {
  to {
    --angle1: 360deg;
  }
}

@keyframes spin2 {
  from {
    --angle2: var(--angle1);
  }
  to {
    --angle2: calc(var(--angle1) + 360deg);
  }
}

Примечание: @property используется в этом примере, т.е. не поддерживается всеми браузерами.

«Идеальное» решение

К предыдущему решению есть одна оговорка: «выход из слоумо» работает не очень хорошо.

Вот лучшее решение:

В этой версии замедленное движение можно плавно включать и выключать. Экспериментальная функция также не используется. Так это идеальное решение? Да и нет.

Это решение работает как «переключение передач»:

  • Шестерни: есть две <div>с. Один является родителем другого. Оба имеют spin анимация, но с другим animation-duration. Конечным состоянием элемента является накопление обеих анимаций.
  • Сдвиг: В начале только один <div> имеет свою анимацию. Другой поставлен на паузу. Когда флажок установлен, обе анимации меняются местами.

Хотя мне очень нравится результат, есть одна проблема: это хороший эксплойт spin анимация, которая вообще не работает для других типов анимаций.

Практическое решение (с JS)

Для общей анимации можно реализовать функцию замедленного движения с помощью небольшого количества JavaScript:

Краткое объяснение:

  • Пользовательское свойство используется для отслеживания хода анимации.
  • Анимация «перезапускается» при установке флажка.
  • Код JS вычисляет правильный animation-delay чтобы обеспечить плавный переход. Я рекомендую этой статье если вы не знакомы с отрицательными значениями animation-delay.

Вы можете рассматривать это решение как гибрид «анимации перезапуска» и подхода «переключения передач».

Здесь важно правильно отслеживать ход анимации. Обходные пути возможны, если @property не доступен. В качестве примера в этой версии используется z-index для отслеживания прогресса:

Примечание: изначально я также пытался создать версию только для CSS, но безуспешно. Хотя не уверен на 100%, я думаю, что это потому, что animation-delay is не анимируемый.

Вот версия с минимальным JavaScript. Работает только «вход в слоумо».

Пожалуйста, дайте мне знать, если вам удастся создать рабочую версию только для CSS!

Slow-mo Любая анимация (с JS)

Наконец, я хотел бы поделиться решением, которое работает (почти) для любой анимации, даже с несколькими сложными @keyframes:

По сути, вам нужно добавить трекер прогресса анимации, а затем тщательно вычислить animation-delay для новой анимации. Однако иногда может быть сложно (но возможно) получить правильные значения.

Например:

  • animation-timing-function Не linear.
  • animation-direction Не normal.
  • несколько значений в animation-name с различными animation-durationИ animation-delay«S.

Этот метод также описан здесь для API веб-анимации.

Благодарности

Я пошел по этому пути после того, как столкнулся с проектами, основанными только на CSS. Некоторые были нежное произведение искусства, а некоторые были сложные приспособления. Мои любимые те, которые включают 3D-объекты, например, этот прыгающий мяч и это упаковочный куб.

В начале я понятия не имел, как они были сделаны. Позже я читал и многому научился у этот хороший учебник Аны Тюдор.

Как оказалось, создание и анимация 3D-объектов с помощью CSS мало чем отличается от того, что делается с помощью смеситель, только с немного другим вкусом.

Заключение

В этой статье мы рассмотрели поведение анимации CSS, когда animate-* имущество изменено. В частности, мы разработали решения для «повторного воспроизведения анимации» и «замедленной анимации».

Я надеюсь, что вы найдете эту статью интересной. Пожалуйста, поделись своими мыслями!

Отметка времени:

Больше от CSS хитрости