Creando un reloj con nuevas funciones de trigonometría CSS sin() y cos()

Creando un reloj con nuevas funciones de trigonometría CSS sin() y cos() |  consejos CSS

¡Las funciones de trigonometría CSS están aquí! Bueno, lo son si estás usando las últimas versiones de Firefox y Safari, eso es. Tener este tipo de poder matemático en CSS abre una gran cantidad de posibilidades. En este tutorial, pensé que sumergiríamos los dedos de los pies en el agua para tener una idea de algunas de las nuevas funciones: sin() Y cos().

Hay otras funciones trigonométricas en camino, incluyendo tan() - Entonces, ¿por qué centrarse solo en sin() Y cos()? Son perfectos para la idea que tengo en mente, que es colocar texto a lo largo del borde de un círculo. Se cubrió aquí en CSS-Tricks cuando Chris compartió un enfoque que usa una combinación de Sass. Eso fue hace seis años, así que démosle el mejor tratamiento.

 

Esto es lo que tengo en mente. Nuevamente, solo es compatible con Firefox y Safari en este momento:

Así que no es exactamente como las palabras que forman una forma circular, sino que estamos colocando caracteres de texto a lo largo del círculo para formar la esfera de un reloj. Aquí hay algunas etiquetas que podemos usar para comenzar:

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

Entonces aquí hay algunos estilos súper básicos para el .clock-face envase. Decidí usar el <time> etiqueta con un datetime atributo.

.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);
}

He decorado algunas cosas allí, pero solo para obtener la forma básica y el color de fondo para ayudarnos a ver lo que estamos haciendo. Observe cómo guardamos el width valor en una variable CSS. Lo usaremos más tarde. No hay mucho que ver por ahora:

Círculo grande de color tomate con una lista vertical de números del 1 al 12 a la izquierda.

Suena como una especie de experiencia de arte moderno, ¿verdad? Introduzcamos una nueva variable, --_rpara almacenar círculos Rayo, que es la mitad del ancho del círculo. De esta manera, si el ancho (--_w) cambia, el valor del radio (--_r) también se actualizará, gracias a otra función matemática de CSS, calc():

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

Ahora un poco de matemática. Un círculo es de 360 ​​grados. Tenemos 12 etiquetas en nuestro reloj, entonces queremos colocar los números cada 30 grados (360 / 12). En el mundo de las matemáticas, un círculo comienza a las 3 en punto, por lo que el mediodía es en realidad menos 90 grados de este, que son 270 grados (360 - 90).

Agreguemos otra variable, --_dque podemos usar para definir un grado valor de cada número en la esfera del reloj. Incrementaremos los valores en 30 grados para completar nuestro círculo:

.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; }

OK, es hora de ensuciarse las manos con el sin() Y cos() ¡las funciones! Lo que queremos hacer es usarlos para obtener las coordenadas X e Y de cada número para poder colocarlos correctamente alrededor de la esfera del reloj.

La fórmula para la coordenada X es radius + (radius * cos(degree)). Conectemos esto a nuestro nuevo --_x variable:

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

La fórmula para la coordenada Y es radius + (radius * sin(degree)). Tenemos lo que necesitamos para calcular que:

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

Hay algunas tareas que debemos hacer para configurar los números, así que vamos a ponerles un estilo básico para asegurarnos de que estén absolutamente posicionados y colocados con nuestras coordenadas:

.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);
}

Aviso --_szque usaremos para width Y height números en un momento. Veamos lo que tenemos hasta ahora.

Círculo grande de color tomate con etiquetas de número de tiempo descentradas a lo largo de su borde.

¡Definitivamente se parece más a un reloj! ¿Ves cómo la esquina superior izquierda de cada número se coloca en el lugar correcto alrededor del círculo? Necesitamos "reducir" el radio al calcular las posiciones para cada número. Podemos deducir el tamaño de un número (--_sz) del tamaño del círculo (--_w), antes de calcular el radio:

--_r: calc((var(--_w) - var(--_sz)) / 2);
Círculo grande de color tomate con etiquetas de números de tiempo a lo largo de su borde redondeado.

Mucho mejor ! Cambiemos los colores para hacerlos más elegantes:

Una esfera blanca con números sobre un fondo gris oscuro. El reloj no tiene brazos.

¡Podríamos parar allí! Logramos el objetivo de colocar el texto alrededor de un círculo, ¿no? Pero, ¿qué es un reloj sin brazo para mostrar las horas, los minutos y los segundos?

Usemos una sola animación CSS para esto. Primero, agreguemos tres elementos más a nuestro marcado,

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

Luego, un marcado común para los tres brazos. Nuevamente, la mayor parte de esto es solo asegurarse de que los brazos estén absolutamente posicionados y colocados en consecuencia:

.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);
}

Usaremos el misma animación para los tres brazos:

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

La única diferencia es el tiempo que tardan los brazos individuales en completar una revolución completa. Para el brazo horariose necesita 12 horas dar una vuelta completa. EL animation-duration La propiedad solo acepta valores en milisegundos y segundos. Sigamos con los segundos, o 43.200 segundos (60 seconds * 60 minutes * 12 hours).

animation: turn 43200s infinite;

Se necesita 1 hora para el brazo de minutos dar una vuelta completa. Pero queremos que sea una animación de varios pasos para que el movimiento entre los brazos sea escalonado en lugar de lineal. Necesitaremos 60 pasos, uno por cada minuto:

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

EL brazo de segundos Este casi lo mismo como minutos de armado, pero la duración es de 60 segundos en lugar de 60 minutos:

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

Actualicemos las propiedades que creamos en los estilos comunes:

.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;
}

¿Y si quisiéramos empezar ahora mismo? Necesitamos algo de 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`);

Añadí id="app" watchface y establezca dos nuevas propiedades personalizadas en él que definen un negativo animation-delay, como hizo Mate Marschalko cuando compartió un reloj de solo CSS. EL getHours() Método JavaScipt Date el objeto usa el formato de 24 horas, así que usamos el remainder operador para convertirlo al formato de 12 horas.

En el CSS necesitamos agregar el animation-delay también:

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

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

Una cosa más. Usa CSS @supports y las propiedades que ya hemos creado, podemos proporcionar una alternativa para los navegadores que no son compatibles sin() Y cos(). (¡Gracias, Temani Afif!):

@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)))
  }
}

¡Y ahí lo tienes! ¡Nuestro reloj ha terminado! Aquí está la demostración final una vez más. Nuevamente, solo es compatible con Firefox y Safari en este momento.

qué más podemos hacer?

Solo estoy jugando aquí, pero podemos convertir rápidamente nuestro reloj en una galería de imágenes circular reemplazando el <time> etiquetas con <img> luego actualice el ancho (--_w) y el radio (--_r) valores:

Probemos uno más. Mencioné anteriormente cómo el reloj parecía un experimento de arte moderno. Podemos investigar esto y recrear un patrón que vi en un póster (que lamentablemente no compré) en una galería de arte el otro día. Si no recuerdo mal, se llamaba "Luna" y consistía en un grupo de puntos formando un círculo.

Un gran círculo formado por un montón de pequeños círculos llenos de diferentes colores de tierra.

Usaremos una lista desordenada esta vez ya que los círculos no siguen ningún orden en particular. Ni siquiera vamos a poner todos los elementos de la lista en el marcado. En su lugar, inyectemos algo de JavaScript en ellos y agreguemos algunos controles que podemos usar para manipular el resultado final.

Los comandos son entradas de rango (<input type="range">) que envolveremos en un <form> y escúchalo input evento.

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

Ejecutaremos este método en "entrada", lo que creará un montón de <li> elementos con grado (--_d) variable que usamos anteriormente aplicada a cada uno. También podemos reutilizar nuestra variable de radio (--_r).

También quiero que los puntos sean de diferentes colores. Así que aleatoricemos (bueno, no completamente aleatorio) el valor de color HSL para cada elemento de la lista y almacenarlo como una nueva variable 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;
}

EL random() El método selecciona un valor de un rango definido de números:

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

Y es todo. Usamos JavaScript para renderizar el marcado, pero una vez que se renderiza, realmente no lo necesitamos. EL sin() Y cos() las funciones nos ayudan a posicionar todos los puntos en los lugares correctos.

Pensamientos finales

Colocar objetos alrededor de un círculo es un ejemplo bastante básico para demostrar los poderes de las funciones trigonométricas como sin() Y cos(). Pero es En realidad Genial que obtengamos funciones modernas de CSS que brindan nuevas soluciones para soluciones antiguas, estoy seguro de que veremos casos de uso mucho más interesantes, complejos y creativos, especialmente con la compatibilidad con el navegador de Chrome y Edge.

Si quieres conocer otros artículos parecidos a Creando un reloj con nuevas funciones de trigonometría CSS sin() y cos() puedes visitar la categoría Estilo.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Subir

Esta página web utiliza cookies para analizar de forma anónima y estadística el uso que haces de la web, mejorar los contenidos y tu experiencia de navegación. Para más información accede a la Política de Cookies . Ver mas