Sincronización precisa con API de animaciones web

 

 

 


Índice
  1. Problemas de sincronización de JavaScript: en orden pero fuera de ritmo
    1. Baja precisión
    2. Amontonamientos
    3. Cola llena de gente
  2. API de animaciones web: dónde están sincronizadas las cosas
    1. Línea de tiempo
    2. Hora de inicio
    3. Manifestación
  3. Deficiencias de este enfoque
    1. Algunas propiedades no se animarán como usted desea
    2. La división no siempre resulta en partes iguales
    3. Nunca se sabe cuánto durará el próximo mes sin consultar el calendario
  4. Conclusión
    1. Otras lecturas

En JavaScript, es natural recurrir a temporizadores cuando algo va a suceder a tiempo. Pero cuando algo va a suceder en el momento exacto porque otras cosas dependen de ello, rápidamente aprendes que los temporizadores son más un problema que una solución. La verdad es que nunca llegan a tiempo. La API de animaciones web podría eliminar la necesidad de temporizadores en ciertos casos y al mismo tiempo ser precisa.

 

Anteriormente veía las animaciones como algo divertido. Algo que añade confusión a las interfaces. Aparte de eso, en buenas manos, la animación puede hacer que las interfaces sean más claras. Una propiedad de las animaciones en la Web de la que no escuché mucho es su precisión. Esa API de animaciones web nos permite eliminar soluciones relacionadas con problemas de sincronización de JavaScript. En este artículo, verás cómo no hacer animaciones y cómo coordinar la animación de varios elementos.

Cuando trabajas en una presentación visual de algo que requiere ser preciso, rápidamente aprendes que pasas demasiado tiempo solucionando la incapacidad de JavaScript para ser exacto sobre cuándo se ejecutará realmente el código. Intente implementar algo que dependa del ritmo o del tiempo compartido y entenderá mi punto. Es posible que te acerques en ciertos casos, pero nunca es perfecto.

Una de las cosas que encuentro útiles cuando trabajo en problemas complejos es dividirlos en problemas más pequeños y simples . Sucede que las piezas más pequeñas –aunque sean muchas– tienen algo que las unifica. Algo que te permita tratarlos de manera uniforme. En el caso de las animaciones, ahora que tienes muchos más elementos con los que lidiar, necesitas algo que garantice un nivel de precisión de tiempo que excluya la posibilidad de desvío, de que los elementos se salgan de ritmo.

Primero, veamos qué podría salir mal con las herramientas típicas que ofrece JavaScript.

Problemas de sincronización de JavaScript: en orden pero fuera de ritmo

En JavaScript, cada tarea pasa por una cola. Su código, interacciones de usuarios, eventos de red. Cada tarea espera su turno para ser realizada por un bucle de eventos. De esa manera, garantiza que todo suceda en orden: cuando llamas a una función, puedes estar seguro de que ningún movimiento repentino del mouse se inyectará en el medio. Cuando necesite que sucedan cosas más tarde, puede registrar un detector de eventos o un temporizador.

Cuando se activa un evento o vence un temporizador, la tarea que definiste en una devolución de llamada pasa a la cola. Una vez que llega el bucle de eventos, su código se ejecuta. Es un concepto poderoso que nos permite ignorar en gran medida la concurrencia. Funciona bien, pero es mejor que comprendas cómo funciona.

Analizaremos las consecuencias de esto en el contexto de las animaciones. Te animo a que aprendas este tema más profundamente. Comprender la naturaleza de cómo funciona JavaScript le ahorrará horas y mantendrá el color en su cabello. Jake Archibald ha hecho un gran trabajo al desglosarlo todo en su artículo " Tareas, microtareas, colas y horarios " y, más recientemente, en su charla " In The Loop " en JSConf.

Esto es lo que te espera una vez que hayas decidido hacer animaciones con setTimeouto setInteval:

  • Baja precisión
  • Amontonamientos
  • Cola llena de gente

Baja precisión

Podemos definir exactamente cuánto tiempo debe esperar un tiempo de espera antes de colocar nuestra tarea en la cola. Lo que no podemos predecir es qué habrá en la cola en este momento. Es posible implementar temporizadores autoajustables verificando la diferencia entre la duración del tick planificada y el momento real en que se ejecuta el código. Esa diferencia se aplica al siguiente tiempo de espera del tick.

 

Funciona principalmente, pero si la distancia requerida entre tics se mide en milisegundos de dos dígitos o menos, rara vez impactará en el momento adecuado. Además, su naturaleza (que se ajusta durante la ejecución) hace que sea difícil visualizar algo rítmico. Mostrará el estado preciso en el que se llamó, pero no el momento exacto en que cambió el estado.

Esto se debe a que setTimeoutgarantiza un retraso mínimo antes de que algo se ponga en la cola. Pero no hay forma de saber qué habrá ya en la cola.

Amontonamientos

Si la baja precisión está bien para usted de vez en cuando, obtendrá un montón. Las cosas que pretendías espaciar en el tiempo podrían ejecutarse todas a la vez si hubiera muchas tareas en las que trabajar el bucle de eventos, o podrían suspenderse todas.

Los avances en la duración de la batería vienen acompañados de un mejor hardware y un software eficiente. Las pestañas del navegador pueden suspenderse para reducir el consumo de energía cuando no están en uso. Cuando las pestañas vuelven a estar enfocadas, el bucle de eventos puede encontrarse con un puñado de devoluciones de llamada (algunas de las cuales son emitidas por temporizadores) en la cola para procesar.

Una vez tuve que implementar mosaicos volteados aleatoriamente para un sitio web, y uno de los errores fue causado por pestañas inactivas. Debido a que cada mosaico tenía su propio temporizador, todos se activaron simultáneamente cuando la pestaña se activó.

Observe cómo los bloques de la fila superior se retrasan y luego voltea tres a la vez. (Consulte el lápiz [CodePen Home Timeouts vs DocumentTimeline](https://codepen.io/smashingmag/pen/BaYVLGo) de Kirill Myshkin )

Cola llena de gente

Probablemente su código ya esté restringido por bibliotecas y marcos. Eso significa que es más probable que las devoluciones de llamada de sus temporizadores se pongan en cola en un momento desafortunado. Es posible que no tenga muchas oportunidades de optimización, ya que ya hay mucho código en funcionamiento.

Las deficiencias mencionadas anteriormente podrían resolverse en determinados casos. Tú decides qué se valora más en cada proyecto en particular. Si todos sus elementos pudieran ser administrados por un solo temporizador, es posible que pueda hacerlo funcionar.

Aún así, buscaría en requestAnimationFramelugar de temporizadores para gestionar las animaciones. La charla de Jake a la que he vinculado anteriormente lo ilustra brillantemente. Lo que te da es ritmo. Puede estar seguro de que su código se ejecutará justo antes de que el usuario pueda ver algo . Debido a que tiene una marca de tiempo de cuándo se llama a su función, puede usarla para calcular el estado exacto que necesita.

Depende de usted con qué vale la pena dedicar su tiempo. Bien podría ser que una solución alternativa en particular esté bien y usted pueda seguir adelante con lo que esté intentando implementar. Usted es un mejor juez de lo que funciona en su caso.

Si lo que estás intentando implementar encaja en el ámbito de las animaciones, te beneficiará sacarlo de la cola. Veamos cómo llegamos a un lugar donde el tiempo es el rey.

API de animaciones web: dónde están sincronizadas las cosas

En mi artículo anterior, " Orquestando la complejidad con la API de animaciones web ", analizamos formas de hacer que varias animaciones sean controlables como si fueran una sola. Ahora veremos cómo asegurarnos de que todas sus animaciones comiencen en el momento correcto.

 

Línea de tiempo

La API de animaciones web introduce la noción de línea de tiempo. De forma predeterminada, todas las animaciones están vinculadas a la línea de tiempo del archivodocument . Eso significa que las animaciones comparten el mismo "reloj interno", un reloj que comienza cuando se carga la página.

Ese reloj compartido es el que nos permite coordinar animaciones. Ya sea un cierto ritmo o un patrón, no tienes que preocuparte de que algo se arrastre o se adelante a sí mismo.

Hora de inicio

Para hacer que una animación comience en un momento determinado, usas la startTimepropiedad . El valor de startTimese mide en milisegundos desde la carga de la página. Las animaciones con una hora de inicio establecida en 1000.5comenzarán su reproducción exactamente cuando la currentTimepropiedad de la línea de tiempo del documento sea igual a 1000.5.

¿Observa el punto en el valor de la hora de inicio? Sí, puedes usar fracciones de milisegundos, es así de preciso. Sin embargo, la precisión exacta depende de la configuración del navegador .

Otra cosa útil es que la hora de inicio también puede ser negativa. Eres libre de configurarlo en un momento en el futuro o en un momento en el pasado. Establezca el valor en -1000y el estado de su animación será como si ya se hubiera reproducido durante un segundo cuando se carga la página. Para el usuario, parecería como si la animación hubiera comenzado a reproducirse incluso antes de que pensara en visitar su página.

Nota : Ojo que timelineaún startTimeson tecnologías experimentales.

Manifestación

Para demostrar cómo puedes usarlo, he configurado una demostración. He implementado un indicador que, más que cualquier otro, depende de la precisión del tiempo: un reloj. Bueno hice dos, de esa manera uno revelaría la grandeza del otro. Ciertas cosas en esta demostración son lo suficientemente simples como para demostrar los conceptos básicos. También hay algunas partes difíciles que muestran dónde falta este enfoque.

Reloj digital y analógico, ambos implementados digitalmente. (Ver el bolígrafo [Reloj] (https://codepen.io/smashingmag/pen/BaYVLMj) de Kirill Myshkin )

El movimiento del reloj analógico es bastante sencillo. Tres manecillas realizan la misma rotación en un solo giro, de manera bastante optimista, infinitas veces. Como el reloj es un instrumento preciso, he hecho que las manecillas de los segundos y los minutos cambien de posición en el momento exacto en que cambian sus valores correspondientes. Eso ayuda a ilustrar que cambian en el momento exacto que sus primos en el reloj digital a continuación. Phasmophobia Game - Todo sobre el juego Phasmophobia

const clock = document.getElementById("analog-clock");const second = clock.querySelector(".second");const minute = clock.querySelector(".minute");const hour = clock.querySelector(".hour");const s = 1000;const m = s * 60;const h = m * 60;const d = h * 24;const hands = [second, minute, hour];const hand_durations = [m, h, d];const steps = [60, 60, 120];const movement = hands.map(function (hand, index) { return hand.animate( [ {transform: "rotate(0turn)"}, {transform: "rotate(1turn)"} ], { duration: hand_durations[index], iterations: Infinity, easing: `steps(${steps[index]}, end)` } );});movement.forEach(function (move) { move.startTime = start_time;});

La animación para cada una de las tres manos difiere en cuánto tiempo hacen su rotación y en cuántos pasos se divide. El segundero hace una sola revolución en sesenta mil milisegundos. El minutero lo hace sesenta veces más lento. La manecilla de las horas (porque es un reloj de veinticuatro horas ) da una vuelta en el mismo tiempo que le toma al minutero dar veinticuatro revoluciones.

 

Para vincular la operación de las manecillas del reloj a la misma noción de tiempo (para asegurar que el minutero actualice su posición exactamente en el momento en que el segundero termina su rotación), utilicé la startTimepropiedad. Todas las animaciones de esta demostración están configuradas a la misma hora de inicio. Y eso es todo lo que necesitas. No te preocupes por la cola, las pestañas suspendidas o los montones de tiempos de espera. Defínelo una vez y listo.

El reloj digital, por otro lado, es un poco contradictorio. Cada dígito es un contenedor con overflow: hidden;. En el interior, hay una fila de números del cero al uno en celdas del mismo ancho. Cada dígito se revela trasladando la fila horizontalmente por el ancho de una celda multiplicado por el valor del dígito. Al igual que con las manecillas del reloj analógico, se trataba de establecer la duración adecuada para cada dígito. Si bien todos los dígitos, desde milisegundos hasta minutos, fueron fáciles de hacer, las horas requirieron algunos trucos, que cubriré a continuación.

Veamos el valor de start_timela variable:

const start_time = (function () { const time = new Date(); const hour_diff = time.getHours() - time.getUTCHours(); const my_current_time = (Number(time) % d) + (hour_diff * h); return document.timeline.currentTime - my_current_time;}());

Para calcular el momento exacto en el que deben iniciarse todos los elementos (que es después de la medianoche), tomé el valor de Date.now()(milisegundos desde el 1 de enero de 1970), le quité días completos y lo ajusté por la diferencia con la hora UTC. Eso me dejó con la cantidad de milisegundos que han pasado desde el comienzo de hoy. Es el único dato que mi reloj necesita para mostrar lo que está destinado a mostrar: horas , minutos y segundos .

Para convertir ese valor al ámbito del documento, necesito ajustarlo en función de cuánto tiempo ha pasado desde la carga de la página de esta demostración hasta Date.now()la llamada. Para hacer eso, lo resté del currentTimedocumento. Aplicar el resultado a una animación significa que esta animación en particular se ha estado reproduciendo desde medianoche. Aplica eso a todas las animaciones y obtendrás una demostración que se ha estado reproduciendo desde medianoche.

En teoría, podríamos tener un reloj que funcione desde el 1 de enero de 1970 (52 años al momento de escribir este artículo), pero algunos navegadores tienen límites no documentados para el valor de la duración de la animación. También sería correcto aplicar algo de CSS para envejecer artificialmente dicho reloj, ya que no habrá ninguna otra distinción con respecto al que ha estado funcionando desde anoche. Ambos relojes estarían en perfecta sincronización.

 

Deficiencias de este enfoque

Es enriquecedor poder implementar algo de esta precisión sin ningún cálculo sofisticado. Pero solo funciona en casos en los que las cosas que intentas implementar se pueden definir con fotogramas clave. Debe decidir, basándose en su caso particular, dónde sería beneficioso y dónde resultaría más engorroso y costoso abordar las deficiencias.

Una alternativa a la API de animaciones web sería utilizar requestAnimationFrameo performance.now(). Con ellos, necesitarías calcular la interpolación tú mismo.

Si elige confiar en la API de animaciones web, tendrá que enfrentar el hecho de que las cosas encajan de manera diferente en una definición de fotograma clave . Es posible que algunas cosas no requieran prácticamente ningún trabajo para definirlas porque encajan de forma natural. Otros requieren soluciones alternativas. Si esas soluciones agregan mucho costo o no a lo que está tratando de implementar, debería dictar su enfoque.

La demostración del reloj tiene ejemplos de ambos casos. Las manos mismas eran lo más fácil de hacer. Es un fotograma clave de rotación de un giro con stepsfunción de aceleración para que funcione. Al final, el movimiento principal del reloj de demostración casi no requirió trabajo. Ojalá pudiera decir que el reloj digital fuera tan fácil de implementar. Pero yo diría que eso se debe a mis propios defectos.

Hay tres ejemplos de soluciones a las que tuve que volver. Espero que te den una idea de lo que podrías necesitar hacer si optas por el enfoque de animaciones. No son una buena representación de los límites de la API de animaciones web, solo muestran cómo una implementación particular que elegí tuvo que cambiarse para adaptarse. Veamos dónde tuve que hacer trabajo adicional.

Algunas propiedades no se animarán como usted desea

Si miras de cerca, cada manecilla del reloj analógico tiene una sombra. Añaden algo de profundidad y hacen que el reloj luzca más bonito. Las sombras se aplican fácilmente usando box-shadowpropiedades filterCSS. Es animable hasta cierto punto, pero donde se queda corto es en la animación de la dirección de la sombra. No lo configuras con valor de ángulo sino por coordenadas.

No pude encontrar una manera de implementar un movimiento circular de una sombra usando dos coordenadas. En lugar de eso, dividí cada mano en tres elementos animados por separado (consulte mi artículo anterior " Orquestando la complejidad con la API de animaciones web " para conocer esa técnica). El primero es un envoltorio que contiene las otras dos partes de una mano: cuerpo y sombra. La envoltura es el elemento al que se aplica la rotación principal. El cuerpo define la forma, el tamaño y el color de una mano, mientras que la sombra copia las propiedades del cuerpo (excepto el color). Además, tiene su propia animación definida: gira alrededor del centro de su mano.

 

Multiplicar el número de elementos a tratar puede parecer más difícil. En el caso de la sombra, sin embargo, el hecho de que finalmente se separara de la mano dio más flexibilidad. Podrías diseñarlo usando CSS. Y como ya se ha solucionado el momento, tener más elementos no lo hace más difícil.

La división no siempre resulta en partes iguales

La segunda solución fue necesaria para la sección de horas del reloj digital. El reloj se implementa con elementos de un solo dígito. Tres por milisegundos, dos por segundos y dos por minutos. Los dígitos de las horas no encajan en una lógica de fotograma clave en bucle.

Los bucles no son regulares porque sólo hay cuatro horas en las veinte. Tuve que introducir un dígito "ancho" para solucionar este problema. El dígito ancho tiene la misma lógica que tendría un dígito normal, sólo que admite números del cero al noventa y nueve, lo cual es suficiente para este caso. Al final, el indicador de hora del reloj digital reutilizó las mismas opciones de sincronización que las manecillas de las horas de los relojes analógicos.

Nunca se sabe cuánto durará el próximo mes sin consultar el calendario

La tercera solución es para la complicación de la fecha. Ahora que tenía el elemento de dígitos "anchos", lo reutilicé para mostrar fechas y simplemente aumenté la duración de horas a días.

El problema con ese enfoque fue que la duración del mes no se correspondía perfectamente con las animaciones de la misma duración utilizadas en la demostración. Verá, el calendario que utilizamos hoy tiene una historia confusa y es difícil encajarlo en un bucle simple. Habría que definir todas las excepciones del calendario gregoriano en un único fotograma clave. Dejaré de hacer eso. Estoy aquí para mostrarte una solución.

Elegí confiar en Dateotro calendario defectuoso en lugar de definirlo. Quién sabe cuántos días tendrán los meses en el futuro, ¿verdad?

function month() { const now = new Date(); return digits( (new Date(now.getFullYear(), now.getMonth() + 1, 0)).getDate(), false );}function create_animation(digit) { const nr_digits = digit.firstElementChild.children.length; const duration = d * nr_digits; return digit.firstElementChild.animate( [ {transform: "translateX(0)"}, {transform: "translateX(calc(var(--len) * -2ch)"} ], { duration, easing: `steps(${nr_digits}, end)`, iterationStart: (d * ((new Date()).getDate() - 1)) / duration } );}(function set_up_date_complication() { const day = document.querySelector(".day"); const new_day = day.cloneNode(); new_day.append(month()); day.replaceWith(new_day); Array.from(new_day.children).forEach(function (digit) { const complication = create_animation(digit); complication.startTime = start_time; complication.finished.then(set_up_date_complication); });}());

Para que la complicación de la fecha sea a prueba de balas, definí su duración como la duración del mes actual. Para seguir usando la misma hora de inicio y evitar el límite de durationvalor, "rebobinamos" la animación a la fecha correcta usando iterationStartla propiedad.

Cuando termina el mes, necesitamos reconstruir la complicación de fecha para el mes siguiente. El momento adecuado para hacerlo sería cuando terminaran las animaciones de complicación. A diferencia de otras animaciones en esta demostración, la fecha no se repite, por lo que crearemos una nueva fecha utilizando la finishedpromesa de la animación del mes actual.

Ese enfoque adolece de las deficiencias descritas al comienzo de este artículo. Pero en el caso de los meses, podríamos cerrar los ojos ante una ligera imprecisión.

Tendrías que creer mi palabra de que funciona. De lo contrario, vuelva a este artículo cualquier último día de un mes cerca de la medianoche. Quién sabe, podrías encontrarme en la misma página, con los ojos llenos de esperanza y los dedos cruzados.

Conclusión

Las animaciones comparten la misma referencia de tiempo y, al ajustar sus startTimepropiedades, puedes alinearlas con cualquier patrón que necesites. Puedes estar seguro de que no se desviarán.

Web Animations API viene con poderosas herramientas que le permiten reducir drásticamente la cantidad de trabajo que su aplicación y usted tienen que hacer. También viene con una precisión que abre posibilidades para implementar nuevos tipos de aplicaciones.

Ese poder está contenido en el ámbito de las animaciones. Si es adecuado para su caso o no, es algo que usted decide en función de sus necesidades. Espero que los ejemplos que proporcioné en este artículo le den una mejor idea de qué camino elegir.

Otras lecturas

  • “ Orquestando la complejidad con la API de animaciones web ”, Kirill Myshkin
  • “ Comprensión de las funciones de facilitación para transiciones y animaciones CSS ”, Adrian Bece
  • “ Técnicas prácticas de diseño de animación ”, Sarah Drasner
  • " Diseño con movimiento reducido para sensibilidades de movimiento ", Val Head

(NL, IL)Explora más en

  • API
  • Animación
  • Interfaces
  • javascript





Tal vez te puede interesar:

  1. ¿Deberían abrirse los enlaces en ventanas nuevas?
  2. 24 excelentes tutoriales de AJAX
  3. 70 técnicas nuevas y útiles de AJAX y JavaScript
  4. Más de 45 excelentes recursos y repositorios de fragmentos de código

Sincronización precisa con API de animaciones web

Sincronización precisa con API de animaciones web

Índice Problemas de sincronización de JavaScript: en orden pero fuera de ritmo

programar

es

https://aprendeprogramando.es/static/images/programar-sincronizacion-precisa-con-api-de-animaciones-web-1148-0.jpg

2024-04-04

 

Sincronización precisa con API de animaciones web
Sincronización precisa con API de animaciones web

Si crees que alguno de los contenidos (texto, imagenes o multimedia) en esta página infringe tus derechos relativos a propiedad intelectual, marcas registradas o cualquier otro de tus derechos, por favor ponte en contacto con nosotros en el mail [email protected] y retiraremos este contenido inmediatamente

 

 

Top 20