Creando un reloj con las nuevas funciones trigonométricas CSS sin() y cos() | trucos 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 un montón 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 características más nuevas: sin()
y cos()
.
Hay otras funciones trigonométricas en camino, incluyendo tan()
Entonces, ¿por qué centrarse solo en sin()
y cos()
? Resultan perfectos para la idea que tengo en mente, que es colocar texto alrededor del borde de un círculo. Esto 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.
Eso es lo que quiero decir. Nuevamente, actualmente solo es compatible con Firefox y Safari:
Así que no es exactamente como si las palabras formaran una forma circular, pero colocamos caracteres de texto a lo largo del círculo para formar la esfera de un reloj. Aquí hay un pequeño marcado 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 .clock-face
decidí usar <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 embellecido un poco las cosas, pero solo para obtener la forma básica y el color de fondo para ayudarnos a ver lo que estamos haciendo. Fíjate cómo ahorramos width
valor en una variable CSS. Usaremos esto luego. No hay mucho que ver hasta ahora:
Parece una especie de experimento de arte moderno, ¿no? Introduzcamos una nueva variable, --_r
para almacenar los círculos radioque es igual a la mitad del ancho del círculo. Por lo tanto, 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áticas. Un círculo es de 360 grados. Tenemos 12 etiquetas en nuestro reloj, entonces queremos poner los números cada 30 grados (360 / 12
). En la tierra de las matemáticas, una ronda comienza a las 3 en punto, por lo que en realidad es mediodía. menos 90 grados de lo que son 270 grados (360 - 90
).
Agreguemos otra variable, --_d
que podemos usar para establecer un grado valor de cada número en la esfera del reloj. Aumentaremos 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; }
Bien, ahora es el momento de ensuciarnos las manos con sin()
y cos()
Lo que queremos hacer es usarlos para obtener las coordenadas X e Y de cada número para que podamos colocarlos correctamente alrededor de la esfera del reloj.
La fórmula para la coordenada X es radius + (radius * cos(degree))
Vamos a incorporar esto en 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 cosas de limpieza que debemos hacer para configurar los números, así que básicamente vamos a darles forma 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 --_sz
que usaremos para width
y height
de los números en un momento. Veamos lo que tenemos hasta ahora.
¡Esto 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 "colapsar" el radio al calcular las posiciones para cada número. Podemos yo deduzco 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);
¡Mucho mejor! Cambiemos los colores para que se vea más elegante:
¡Podemos parar aquí! Logramos el objetivo de colocar texto alrededor de un círculo, ¿verdad? Pero, ¿qué es un reloj sin correa que muestra horas, minutos y segundos?
Usemos una 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 algunas marcas generales 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 misma animación para las tres manos:
@keyframes turn {
to {
transform: rotate(1turn);
}
}
La única diferencia es el tiempo que tardan los brazos individuales en dar un giro completo. manecilla de las horasquitar 12 horas dar una vuelta completa animation-duration
solo acepta valores en milisegundos y segundos. Sigamos con los segundos, que son 43.200 segundos (60 seconds * 60 minutes * 12 hours
).
animation: turn 43200s infinite;
Se necesita Una hora para minutero para dar una vuelta completa. Pero queremos que sea una animación de varios pasos para que el movimiento entre las manos sea escalonado en lugar de lineal. Necesitaremos 60 pasos, uno por cada minuto:
animation: turn 3600s steps(60, end) infinite;
El brazo de segundos es casi lo mismo como el minutero, 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 genéricos:
.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 queremos empezar a la hora actual? 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`);
yo añadí id="app"
a la esfera del reloj y establezca dos nuevas propiedades personalizadas en él que establezcan un valor negativo animation-delay
como hizo Mate Marchalko cuando compartió un reloj solo con CSS getHours()
método JavaScript Date
objeto utiliza un formato de 24 horas, por lo que utilizamos remainder
operador para convertirlo al formato de 12 horas.
En CSS necesitamos agregar animation-delay
así como también:
.minutes {
animation-delay: var(--_dm, 0s);
/* other styles */
}
.hours {
animation-delay: var(--_dh, 0s);
/* other styles */
}
Solo una cosa más. Usando CSS @supports
y propiedades que ya hemos creado, podemos proporcionar una alternativa para los navegadores que no admiten soporte 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 voilá! ¡Nuestro reloj está listo! Aquí está la demostración final una vez más. Nuevamente, actualmente solo es compatible con Firefox y Safari.
qué más podemos hacer?
Solo estamos jugando aquí, pero podemos convertir rápidamente nuestro reloj en una galería de imágenes circular reemplazando <time>
etiquetas con <img>
luego actualice el ancho (--_w
) y radio (--_r
) valores:
Probemos uno más. Mencioné anteriormente cómo el reloj se veía como un experimento de arte moderno. Podemos apoyarnos en esto y recrear un patrón que vi en un cartel (que desafortunadamente no compré) en una galería de arte. Si mal no recuerdo se llamaba "Luna" y consistía en un montón de puntos formando un círculo.
Esta vez usaremos una lista desordenada, ya que los círculos no siguen un orden específico. Ni siquiera pondremos todos los elementos de la lista en el marcado. En cambio, inyectémoslos con JavaScript y agreguemos algunos controles que podemos usar para manipular el resultado final.
Los controles son entradas de alcance (<input type="range">)
que envolveremos en un <form>
y escucha por input
un 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 "iniciar sesión", lo que creará un montón de <li>
elementos con grado (--_d
) variable que usamos anteriormente aplicada a cada uno de ellos. También podemos reasignar nuestra variable de radio (--_r
).
También quiero que los puntos sean de diferentes colores. Así que aleatoricemos (bueno, no completamente randomizado) el valor de color HSL para cada elemento de la lista y lo almacena 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 dentro de un rango específico de números:
const random = (max, min = 0, f = true) => f ? Math.floor(Math.random() * (max - min) + min) : Math.random() * max;
Y eso es todo. Usamos JavaScript para renderizar el marcado, pero tan pronto como se renderiza, realmente no lo necesitamos. sin()
y cos()
funciones nos ayudan a colocar todos los puntos en los lugares correctos.
Pensamientos finales
Poner cosas alrededor de un círculo es un ejemplo bastante básico de demostrar el poder de las funciones trigonométricas como sin()
y cos()
. Pero es en realidad Es genial que obtengamos funciones modernas de CSS que brindan nuevas soluciones para las antiguas. Estoy seguro de que veremos casos de uso mucho más interesantes, complejos y creativos, especialmente cuando la compatibilidad con navegadores llegue a Chrome y Edge.
Deja una respuesta