Аналоговые часы, CSS и ничего больше

в 11:09, , рубрики: css, html, javascript

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

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

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

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

Аналоговые часы, CSS и ничего больше - 1

Аналоговые часы, CSS и ничего больше - 2

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

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

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

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

Аналоговые часы, CSS и ничего больше - 3

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

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

Аналоговые часы, CSS и ничего больше - 4

Никаких перерисовок и стабильных 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 некоторые центры немного смещены:
    Аналоговые часы, CSS и ничего больше - 5

Итоги

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

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

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

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

Автор: StasL

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js