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

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


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


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


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

-b3bddbzdas_g5qshd6t7rmapq8.png

_hdtxqi0-_pjub3z7wnbkd24eeq.png

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


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


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

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

ocruhtyl-sr_xvqakw9eqndgnag.png

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

.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?


8inlozoi4r5pfwxej4wfncden5q.png

Никаких перерисовок и стабильных 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 анимации для каждой из стрелок и подключаем их к соответственным классам.

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

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


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

  • Часы с прыгающей минутной и секундной стрелками
  • Часов с разными часовыми поясами
  • Также все работает в Internet Explorer 11 и мобильных браузерах


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


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

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


Итоги


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

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


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


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

© Habrahabr.ru