Нет времени объяснять! Пишем таймер обратного отсчёта на чистом CSS

Нет времени объяснять! Пишем таймер обратного отсчёта на чистом CSS

CSS – мощный инструмент современного разработчика. Он многое умеет, в нём есть переменные, функции, наследование и ещё много крутых штук. Но всё-таки это не язык программирования – у него совсем другая сфера ответственности. Тем интереснее использовать CSS для решения задач программирования 🙂 Именно этим сегодня и займёмся.

Дисклеймер! Многие вещи в принципе невозможно сделать на CSS. Ещё больше вещей делать на CSS нерационально. Мы занимаемся этим только из болезненного любопытства и стремления познать все скрытые возможности инструмента.

Условие задачи

Нужно сделать таймер обратного отсчёта. Предъявляемые требования:

  • Таймер должен выводить миллисекунды от 99 до 0.
  • Когда остаётся меньше 10 миллисекунд, нужно выводить только одну цифру (от 9 до 0) и центрировать её.
  • Бонус #1: Цвет шрифта и фона можно настраивать в процессе работы (без кусочка JS не обойтись).
  • Бонус #2. После остановки таймера его можно перезапустить.
  • Код должен работать и на ПК, и на мобильных устройствах.

Чтобы выполнить указанные условия, пойдём напролом. Все нужные цифры (от 0 до 9) запишем прямо в базовую разметку страницы. Затем для имитации таймера анимируем их в нужном ритме и правильной последовательности.

Да, не очень элегантно. Но сработает, вот увидите.

Что нам потребуется?

  1. CSS трансформации
  2. CSS анимации
  3. Flexbox-модель
  4. CSS переменные
  5. Различные селекторы

Вот что получится в итоге:

Реализация на JavaScript

Сразу посмотрим, как это можно было сделать на JS.

Простой и понятный код, состоящий из пары функций. Для обновления таймера подписываемся на момент перерисовки браузера с помощью метода window.requestAnimationFrame().

Всего пара блоков в HTML:

И элементарные стили для выравнивания:

Согласитесь, ровным счётом ничего интересного. Поэтому бросаем эту ерунду и идём писать по-настоящему крутой таймер.

Общий подход

Сложно придумать более прямое решение, чем простое перечисление в HTML всех цифр. Расположим их в две группы (два разряда). По мере необходимости будем скрывать ненужные символы.

Анимация будет заключаться в простом прокручивании каждого блока по вертикали. В каждый момент времени в каждой колонке будет отображаться только одна цифра.

CSS трансформации

Большинство CSS-свойств плохо подходят для анимирования, так как их изменение вызывает перерисовку страницы. Но есть два «безопасных» свойства, которыми мы можем воспользоваться: transform и opacity .

Подробнее об анимации в вебе можно прочитать в замечательном руководстве High Performance Animations.

Для оживления таймера возьмём надёжное свойство translateY . Оно обеспечит перемещение блока только по y -оси.

Можно воспользоваться и полным свойством translate , но помните, что его первый аргумент соответствует x -координате. Если хотите перемещать элемент только по вертикали, то передайте первым параметром 0 .

Чтобы лучше понять функции трансформации, загляните в спецификацию CSS Transforms Module Level 1. Там всё разобрано на подробных примерах, так что вы разберётесь, даже если не очень любите математику.

CSS animations

Следующий шаг – анимировать применение трансформаций. Для этого мы используем CSS-анимации.

Самое важное правило, которое вы должны знать, – это @keyframes. Оно позволяет разбить анимацию на кадры и описать каждый из них в отдельности.

Здесь мы создали две анимации – по одной для каждого блока с цифрами.

Обратите внимание на два последних кадра в анимации первого блока. В этот момент там должна отображаться цифра 0 , но она нам не нужна, поэтому скрываем её с помощью width: 0 .

Чтобы применить анимации, используем краткий синтаксис свойства animation :

Если вы зайдете в панель инструментов разработчика и откроете вкладку с вычисленными значениями (computed), то увидите, что вместо одного свойства animation к элементу применились сразу несколько:

Нет времени объяснять! Пишем таймер обратного отсчёта на чистом CSS

animation-name

Имя анимации, использующееся для её идентификации. Для него можно использовать латинские буквы, цифры, нижнее подчёркивание и дефисы. Первой должна идти буква. В начале не могут стоять зарезервированные слова none , unset , initial или inherit , а также сочетание — . Р егистр символов имеет значение.

animation-duration

Продолжительность одного цикла анимации. Для первой колонки цифр анимация будет длиться 10 секунд ( 10s ). Вторая колонка двигается в 10 раз быстрее ( 1s ).

animation-iteration-count

Количество циклов анимации, которое должно выполниться до ее остановки. Первую колонку нужно прокрутить лишь один раз – от 9 до 0. Вторую – целых 10 раз, по одному на каждое положение первой колонки.

animation-timing-function

Это свойство описывает прогресс анимации в течение одного цикла. Если вы знакомы с кривыми Безье, то можете контролировать его до мельчайших подробностей с помощью функции cubic-bezier() . Для простых смертных есть несколько готовых значений animation-timing-function, обозначенных ключевыми словами ( ease , ease-in и т.д)

Нам же больше подойдёт значение step-end . Это то же самое, что и steps(1, jump-end) .

Функция steps() разбивает анимацию на равные «шаги», то есть величина изменяется не плавно, а прерывисто. Первый аргумент – количество шагов, второй – момент, когда начинается анимация. Ключевое слово jump-end означает, что анимация запускается в конце, а не начале каждого шага.

Чтобы лучше разобраться в этой функции, обратитесь к статье Дэна Уилсона Jumps: The New Steps() in Web Animation.

animation-fill-mode

Состояние целевого объекта до и после завершения анимации. Нам требуется, чтобы колонки останавливались на последней цифре (последний ключевой кадр), поэтому используем значение forwards .

Когда анимация остановится, первая цифра будет скрыта, а вторая колонка замрёт на позиции -9em .

Ещё больше о CSS анимациях вы можете узнать в спецификации CSS Animations Level 1.

Flexbox

Цифру 0 в первом разряде мы уже скрыли с помощью инструкции @keyframes , осталось только выровнять таймер по центру страницы:

Для вертикального выравнивания используем автоматический расчёт маргинов у потомка флекс-контейнера. Другими словами, назначаем display: flex родительскому блоку, и margin: auto дочернему.

Горизонтальное выравнивание достигается обычным text-align: center .

Больше информации о Flex-модели – в спецификации CSS Flexible Box Layout Module Level 1.

Бонус #1: Динамическое изменение цвета

В условиях задачи была также бонусная возможность кастомизации цвета текста и фона нашего таймера.

Используем для ее решения кастомные свойства CSS и инпут выбора цвета.

Положим цвета в переменные:

И используем их в таймере:

Добавим на страницу два инпута для изменения цветовой схемы:

Теперь придётся написать пару строк JS-кода, чтобы динамически изменять переменные цветов при изменении инпутов:

Да, не всё можно решить на чистом CSS.

Бонус #2: Перезапуск таймера

Сейчас таймер отрабатывает только один раз. Чтобы запустить его сначала, необходимо перезагрузить страницу. Давайте добавим возможность перезапуска.

Возможно, вы подумали, что нам придётся снова смошенничать и добавить JavaScript? А вот и нет, мы справимся своими силами!

Воспользуемся чекбоксом, который обладает состоянием checked . Мы легко можем получить к нему доступ из CSS.

Теперь анимация работает только при включенном чекбоксе, а при выключенном таймер сбрасывается и возвращается в исходное состояние.

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

). А лейбл для него может находиться где угодно.

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

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