Имитация стрелочных часов с помощью новых тригонометрических функций CSS sin() и cos ()

Имитация стрелочных часов с помощью новых тригонометрических функций CSS sin() и cos ()
Имитация стрелочных часов с помощью новых тригонометрических функций CSS sin() и cos ()

Тригонометрические функции CSS есть в последних версиях Firefox и Safari. Наличие такого рода математической мощи в CSS открывает целую кучу возможностей. В этом материале мы применим пару новых функций: sin()и cos().

В конвейере есть и другие тригонометрические функции, tan() в том числе — так зачем фокусироваться только на sin()и cos()? Они идеально подходят для идеи, которая заключается в размещении текста вдоль края круга.

Вот что имеется в виду. Опять же, на данный момент это поддерживается только в Firefox и Safari:

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

<div class="clock">
  <div class="clock-face">
    <time datetime="12:00">12</time>
    <time datetime="1:00">1</time>
    <time datetime="2:00">2</time>
    <time datetime="3:00">3</time>
    <time datetime="4:00">4</time>
    <time datetime="5:00">5</time>
    <time datetime="6:00">6</time>
    <time datetime="7:00">7</time>
    <time datetime="8:00">8</time>
    <time datetime="9:00">9</time>
    <time datetime="10:00">10</time>
    <time datetime="11:00">11</time>
  </div>
</div>

Далее, вот несколько супер базовых стилей для .clock-faceконтейнера. Используем <time>тег с datetimeатрибутом.

.clock {
  --_ow: clamp(5rem, 60vw, 40rem);
  --_w: 88cqi;
  aspect-ratio: 1;
  background-color: tomato;
  border-radius: 50%;
  container-type: inline;
  display: grid;
  height: var(--_ow);
  place-content: center;
  position: relative;
  width var(--_ow);
}

Немного украсим там всё, но только для того, чтобы получить основную форму и цвет фона, которые помогут нам увидеть, что мы делаем. Обратите внимание, как мы сохраняем widthзначение в переменной CSS. Мы воспользуемся этим позже. Пока смотреть особо не на что:

Большой кружок помидорного цвета с вертикальным списком чисел 1-12 слева.

Похоже на какой-то эксперимент в области современного искусства, верно? Давайте введем новую переменную, --_r, для хранения радиуса окружности, который равен половине ширины окружности. Таким образом, при изменении width (--_w) значение radius (--_r) также будет обновляться — благодаря другой математической функции CSS,calc():

.clock {
  --_w: 300px;
  --_r: calc(var(--_w) / 2);
  /* rest of styles */
}

Теперь немного математики. Окружность равна 360 градусам. У нас на часах 12 меток, поэтому мы хотим размещать цифры через каждые 30 градусов (360 / 12). В math-land круг начинается с 3 часов, поэтому полдень на самом деле минус 90 градусов от этого, что составляет 270 градусов (360 - 90) .

Давайте добавим еще одну переменную, --_d, которую мы можем использовать для установки значения степени для каждого числа на циферблате. Мы собираемся увеличить значения на 30 градусов, чтобы завершить наш круг:

.clock time:nth-child(1) { --_d: 270deg; }
.clock time:nth-child(2) { --_d: 300deg; }
.clock time:nth-child(3) { --_d: 330deg; }
.clock time:nth-child(4) { --_d: 0deg; }
.clock time:nth-child(5) { --_d: 30deg; }
.clock time:nth-child(6) { --_d: 60deg; }
.clock time:nth-child(7) { --_d: 90deg; }
.clock time:nth-child(8) { --_d: 120deg; }
.clock time:nth-child(9) { --_d: 150deg; }
.clock time:nth-child(10) { --_d: 180deg; }
.clock time:nth-child(11) { --_d: 210deg; }
.clock time:nth-child(12) { --_d: 240deg; }

Хорошо, сейчас самое время запачкать руки функциями sin()иcos()! Что мы хотим сделать, так это использовать их для получения координат X и Y для каждого числа, чтобы мы могли правильно разместить их вокруг циферблата.

Формула для координаты X такова radius + (radius * cos(degree)). Давайте добавим это в нашу новую --_xпеременную:

--_x: calc(var(--_r) + (var(--_r) * cos(var(--_d))));

Формула для координаты Y такова radius + (radius * sin(degree)). У нас есть все, что нам нужно для вычисления этого:

--_y: calc(var(--_r) + (var(--_r) * sin(var(--_d))));

Для настройки чисел нам нужно выполнить несколько хозяйственных действий, поэтому давайте добавим к ним некоторый базовый стиль, чтобы убедиться, что они абсолютно расположены и размещены с нашими координатами:

.clock-face time {
  --_x: calc(var(--_r) + (var(--_r) * cos(var(--_d))));
  --_y: calc(var(--_r) + (var(--_r) * sin(var(--_d))));
  --_sz: 12cqi;
  display: grid;
  height: var(--_sz);
  left: var(--_x);
  place-content: center;
  position: absolute;
  top: var(--_y);
  width: var(--_sz);
}

Обратите --_szвнимание, что мы будем использовать для widthи heightчисел через мгновение. Давайте посмотрим, что у нас есть на данный момент.

Большой круг цвета помидора со смещенными от центра метками с номерами часов вдоль его края.

Это определенно больше похоже на часы! Видите, как верхний левый угол каждого числа расположен в правильном месте по кругу? Нам нужно “уменьшить” радиус при вычислении позиций для каждого числа. Мы можем вычесть размер числа (--_sz) из размера окружности (--_w), прежде чем вычислять радиус:

--_r: calc((var(--_w) - var(--_sz)) / 2);
Большой круг цвета помидора с метками часов вдоль его закругленного края.

Намного лучше! Давайте изменим цвета, чтобы они выглядели более элегантно:

Белый циферблат с цифрами на темно-сером фоне. У часов нет рычагов.

Мы могли бы остановиться прямо здесь! Мы достигли цели размещения текста по кругу, верно? Но что такое часы без стрелок, чтобы показывать часы, минуты и секунды?

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

<div class="clock">
  <!-- after <time>-tags -->
  <span class="arm seconds"></span>
  <span class="arm minutes"></span>
  <span class="arm hours"></span>
  <span class="arm center"></span>
</div>

Затем некоторая общая разметка для всех трех плеч. Опять же, большая часть этого - просто убедиться, что рычаги абсолютно расположены и размещены соответствующим образом:

.arm {
  background-color: var(--_abg);
  border-radius: calc(var(--_aw) * 2);
  display: block;
  height: var(--_ah);
  left: calc((var(--_w) - var(--_aw)) / 2);
  position: absolute;
  top: calc((var(--_w) / 2) - var(--_ah));
  transform: rotate(0deg);
  transform-origin: bottom;
  width: var(--_aw);
}

Мы будем использовать одну и ту же анимацию для всех трех рычагов:

@keyframes turn {
  to {
    transform: rotate(1turn);
  }
}

Единственное различие заключается во времени, которое требуется отдельным стрелкам, чтобы совершить полный оборот. Для стрелки часов требуется 12 часов, чтобы сделать полный оборот. animation-durationСвойство принимает значения только в миллисекундах и секундах. Давайте придерживаться секунд, что составляет 43 200 секунд (60 seconds * 60 minutes * 12 hours).

animation: turn 43200s infinite;

Минутной стрелке требуется 1 час, чтобы совершить полный оборот. Но мы хотим, чтобы это была многоступенчатая анимация, чтобы движение между рычагами было ступенчатым, а не линейным. Нам понадобится 60 шагов, по одному на каждую минуту:

animation: turn 3600s steps(60, end) infinite;

Секундная стрелка почти такая же, как и минутная, но ее продолжительность составляет 60 секунд вместо 60 минут:

animation: turn 60s steps(60, end) infinite;

Давайте обновим свойства, которые мы создали в общих стилях:

.seconds {
  --_abg: hsl(0, 5%, 40%);
  --_ah: 145px;
  --_aw: 2px;
  animation: turn 60s steps(60, end) infinite;
}
.minutes {
  --_abg: #333;
  --_ah: 145px;
  --_aw: 6px;
  animation: turn 3600s steps(60, end) infinite;
}
.hours {
  --_abg: #333;
  --_ah: 110px;
  --_aw: 6px;
  animation: turn 43200s linear infinite;
}

Что, если мы хотим начать с текущего времени? Нам нужно немного JavaScript:

const time = new Date();
const hour = -3600 * (time.getHours() % 12);
const mins = -60 * time.getMinutes();
app.style.setProperty('--_dm', `${mins}s`);
app.style.setProperty('--_dh', `${(hour+mins)}s`);

Добавим id="app"к циферблату и установим для него два новых пользовательских свойства, которые устанавливают отрицательное animation-delayзначение. getHours()Метод объекта JavaScipt Dateиспользует 24-часовой формат, поэтому мы используем remainderоператор для преобразования его в 12-часовой формат.

В CSS нам animation-delayтакже нужно добавить:

.minutes {
  animation-delay: var(--_dm, 0s);
  /* other styles */
}

.hours {
  animation-delay: var(--_dh, 0s);
  /* other styles */
}

Еще одна вещь. Используя CSS @supportsи свойства, которые мы уже создали, мы можем предоставить запасной вариант для браузеров, которые не поддерживают sin()и cos():

@supports not (left: calc(1px * cos(45deg))) {
  time {
    left: 50% !important;
    top: 50% !important;
    transform: translate(-50%,-50%) rotate(var(--_d)) translate(var(--_r)) rotate(calc(-1*var(--_d)))
  }
}

И вуаля! Наши часы готовы! Вот еще одна финальная демонстрация. Опять же, на данный момент это поддерживается только в Firefox и Safari.

Что еще мы можем сделать?

Здесь мы просто балуемся, но мы можем быстро превратить наши часы в круговую галерею изображений, заменив <time>теги на <img>затем обновив значения width (--_w) и radius (--_r):

Давайте попробуем еще один вариант. Мы можем создать вот такой шаблон, который называется “Луна” и состоит из множества точек, образующих круг.

Большой круг, сформированный из множества меньших заполненных кругов различных цветов earthtone.

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

Элементы управления - это входные данные диапазона (<input type="range">)которые мы обернем в a <form>и прослушаем inputсобытие.

<form id="controls">
  <fieldset>
    <label>Number of rings
      <input type="range" min="2" max="12" value="10" id="rings" />
    </label>
    <label>Dots per ring
      <input type="range" min="5" max="12" value="7" id="dots" />
    </label>
    <label>Spread
      <input type="range" min="10" max="40" value="40" id="spread" />
    </label>
  </fieldset>
</form>

Мы запустим этот метод для “ввода”, который создаст кучу <li>элементов с переменной degree (--_d), которую мы использовали ранее, примененной к каждому из них. Мы также можем перепрофилировать нашу переменную radius (--_r) .

Также сделаем так, чтобы точки были разных цветов. Итак, давайте рандомизируем (ну, не полностью рандомизируем) значение цвета HSL для каждого элемента списка и сохраним его как новую переменную CSS,--_bgc:

const update = () => {
  let s = "";
  for (let i = 1; i <= rings.valueAsNumber; i++) {
    const r = spread.valueAsNumber * i;
    const theta = coords(dots.valueAsNumber * i);
    for (let j = 0; j < theta.length; j++) {
      s += `<li style="--_d:${theta[j]};--_r:${r}px;--_bgc:hsl(${random(
        50,
        25
      )},${random(90, 50)}%,${random(90, 60)}%)"></li>`;
    }
  }
  app.innerHTML = s;
}

random()Метод выбирает значение в пределах определенного диапазона чисел:

const random = (max, min = 0, f = true) => f ? Math.floor(Math.random() * (max - min) + min) : Math.random() * max;

И это все. Мы используем JavaScript для рендеринга разметки, но как только она визуализируется, она нам действительно не нужна. Функции sin()и cos()помогают нам расположить все точки в нужных местах.

Заключительные мысли

Размещение объектов по кругу - довольно простой пример для демонстрации возможностей тригонометрических функций, таких как sin()и cos(). Но это действительно здорово, что мы получаем современные функции CSS, которые предоставляют новые решения для старых обходных путей. Уверены, что мы увидим гораздо более интересные, сложные и креативные варианты использования, особенно с поддержкой браузеров в Chrome и Edge.

Понравилась статья?
Будем признательны, если поделитесь в соцсетях или мессенджерах, а также присоединитесь к нашей группе Вконтакте. Будет интересно!

Вас может заинтересовать:

Цитаты на CSS и Blockquote. 11 примеров использования
Цитаты на CSS и Blockquote. 11 примеров использования

Первая статья в этом году будет полностью посвящена цитатам в тексте и такому замечательному CSS тегу, как <blockquote>, а также оформлению цитат с помощью CSS стилей.


Переливающийся текст на CSS
Переливающийся текст на CSS

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


Анимационные указатели прокрутки страницы
Анимационные указатели прокрутки страницы
Такие указатели чаще всего применяют на первом экране Главной страницы сайта, когда на ней размещена полноэкранная картинка, видео или слайдер. Чтобы показать Пользователю, что основной контент расположен ниже, в нижней части экрана размещают стрелку, направленную вниз. Чтобы привлечь к ней дополнительное внимание, её делаю анимационный...

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

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

Варианты оформления поля Поиска по сайту на CSS
Варианты оформления поля Поиска по сайту на CSS
Обычное поле Поиска по сайту можно оформить более оригинально, чем просто инпут с лупой. Например, благодаря анимационным эффектам на CSS3 поле для ввода поисковой фразы можно скрыть, чтобы освободить место для другого контента...

QRcode

2010-2024 © Веб студия iNikSite.ru (г. Подольск). Все права сохранены.

Цены на сайте носят ознакомительный характер и не являются публичной офертой! Просим уточнять цены при отправке заявки в нашу компанию. У нас действуют специальные предложения и скидки на различные варианты исполнения заказа и 100% предоплату!

Мы используем файлы cookie. Они помогают улучшить ваше взаимодействие с сайтом.