Хабрахабр

[Из песочницы] Аналоговые часы, CSS и ничего больше

Что здесь не так?

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

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

Вкладка Rendering в консоли Chrome помогла подтвердить факт перерисовки:


Мы видим перерисовку каждую секунду и средний fps 50 кадров и это на странице, где у нас изображены всего одни часы.

А как еще можно?

И так я поставил перед собой задачу создать аналоговые часы, который не будут вызывать перерисовку страницы и полагаться на JavaScript для вычисления положения стрелки. Часы которые не будут никак влиять на частоту кадров в браузере. Среди всех мне известных CSS свойств, только одно позволяет трансформировать элемент без перерисовки — transform. Значит его и будем использовать.

Но сначала создадим свои часы, с которыми нам будем удобно шевелить стрелку одним только transform свойством. Создадим циферблат со всеми стрелками:

Идея тут такая — если поместить каждую стрелку в отдельный контейнер, который будет в центре часов, то вращая этот контейнер относительно центра мы будем вращать и саму стрелку. Вот пример стилей такого контейнера для часовой стрелки:

.clock__hand { margin-left: -0.5em; margin-top: -0.5em; font-size: inherit; position: absolute; display: block; height: 1em; width: 1em; left: 50%; top: 50%;
}
.clock__hand--hour::after { content: ""; border-radius: 0.015em 0.015em 0.01em 0.01em; background-color: #000; margin-bottom: -0.02em; margin-left: -0.025em; font-size: inherit; position: absolute; display: block; height: 0.25em; width: 0.05em; bottom: 50%; left: 50%;
}

С помощью font-size в данном случае мы задаем размер циферблата и всех компонентов часов.

Код вида самых часов.

Хорошо, а на сколько вращать?

Вращать стрелку на все 360 градус. Правильный вопрос: как долго ее вращать? А зависит это от того, какую стрелку мы вращаем. Часовую — 12 часов, минутную — час, секундную — минуту.

.clock__hand--hour { animation: clock-hand-rotate 43200s linear infinite;
}
.clock__hand--minute { animation: clock-hand-rotate 3600s linear infinite;
}
.clock__hand--second { animation: clock-hand-rotate 60s linear infinite;
}
@keyframes clock-hand-rotate { from { transform: rotate(0deg) } to { transform: rotate(360deg) }
}

И так наши часы заработали.

А что же нам скажет про них Chrome?

Никаких перерисовок и стабильных 60 fps

Но это же не наше время.

И так наше начальное время 00:00:00 потому что все стрелки начинают анимацию с 0 градусов. Чтобы начинать с настоящего времени нам нужно рассчитывать начальный градус отдельно для каждой стрелки относительно времени. И так у нас два варианта, либо на стороне сервера рендерить CSS относительно времени запроса, либо использовать JavaScript. Конечно сервер рендеринг и без скриптовые часы это круто, но ради подтверждения концепта мы все же используем JavaScript.

var date = new Date(), hours = date.getHours(), minutes = date.getMinutes(), seconds = date.getSeconds(); if (hours > 12) { hours -= 12;
} var secondsStartDegree = 360 / 60 * seconds, minutesStartDegree = 360 / 60 * minutes + 6 / 60 * seconds, hoursStartDegree = 360 / 12 * hours + 30 / 60 * minutes + 0.5 / 60 * seconds; var style = document.createElement('style'); style.type = 'text/css';
style.innerHTML = '\ @keyframes clock-hand-rotate--hour {\ from {transform: rotate(' + hoursStartDegree + 'deg)}\ to {transform: rotate(' + (hoursStartDegree + 360) + 'deg)}\ }\ @keyframes clock-hand-rotate--minute {\ from {transform: rotate(' + minutesStartDegree + 'deg)}\ to {transform: rotate(' + (minutesStartDegree + 360) + 'deg)}\ }\ @keyframes clock-hand-rotate--second {\ from {transform: rotate(' + secondsStartDegree + 'deg)}\ to {transform: rotate(' + (secondsStartDegree + 360) + 'deg)}\ }\ .clock__hand--hour {\ animation: clock-hand-rotate--hour 43200s linear infinite;\ }\ .clock__hand--minute {\ animation: clock-hand-rotate--minute 3600s linear infinite;\ }\ .clock__hand--second {\ animation: clock-hand-rotate--second 60s steps(60) infinite;\ }'; document.getElementsByTagName('head')[0].appendChild(style);

И там мы создаем 3 анимации для каждой из стрелок и подключаем их к соответственным классам.

Вот результат.

А что еще можно?

Ну если вам не хватает того факта, что у вас часы которые почти не забирают ресурсов браузера. То вот еще пару фишек:

Хорошо а в чем минусы

Их не так уж много, но они есть:

  • Так как часы отсчитывают время CSS-ом, то если забить тред какими-то тяжёлыми заданиями — часы перестанут идти и когда страница освободится — будут отставать. Но все это можно поправить заново задав keyframe.
  • Не всюду сайт будет выглядеть идеально, вот например в том же IE11 некоторые центры немного смещены:

Итоги

Не знаю как на счет использования этих часов в продакшене, но я сделал для себя лично выводы относительно новых технологий.

И я бы сказал, что не всегда стоит кидаться за новыми технологиями, если и старые еще могут не мало. А самое главное — лучше хорошо использовать надежные инструменты, чем просто пробовать всюду всунуть новую невероятную фичу языка.

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

Надеюсь вам тоже было интересно, спасибо за прочтение.

Теги
Показать больше

Похожие статьи

Кнопка «Наверх»
Закрыть