Cambiar el estilo de un elemento HTML en JavaScript con su transición CSS temporalmente deshabilitada no funciona de manera confiable

Actualmente estoy trabajando en una animación para un sitio web que involucra a dos elementos que cambian de posición durante un período de tiempo y, por lo general, se restablecen a su posición inicial. Solo un elemento será visible a la vez y todo debería funcionar lo más suavemente posible.

Antes de preguntar, una solución solo para CSS no es posible ya que se genera dinámicamente y debe estar sincronizada. Por el bien de esta pregunta, usaré una versión muy simplificada que simplemente consiste en un cuadro que se mueve a la derecha. Me referiré solo a este último ejemplo, a menos que se establezca explícitamente por el rest de esta pregunta para simplificar las cosas.

De todos modos, el movimiento es manejado por la propiedad de transición CSS que se establece para que el navegador pueda hacer el trabajo pesado para eso. Esta transición debe eliminarse para restablecer la posición del elemento en un instante. La forma obvia de hacerlo sería hacer eso y luego volver a aplicar la transición cuando sea necesario volver a moverse, lo que también es inmediato. Sin embargo, esto no está funcionando. No exactamente. Lo explicaré.

Eche un vistazo al JavaScript al final de esta pregunta o en el JSFiddle vinculado y verá que eso es lo que estoy haciendo, pero setTimeout está agregando un retraso de 25 ms entre ellos. La razón de esto es (y probablemente sea mejor que lo intente usted mismo) si no hay demora (que es lo que quiero) o una demora muy breve, el elemento permanecerá en su lugar de manera intermitente o continua, lo cual no es el efecto deseado. Cuanto mayor es la demora, más probable es que funcione, aunque en mi animación real esto causa un jitter menor porque la animación funciona en dos partes y no está diseñada para tener una demora.

Esto parece ser el tipo de problema que podría ser un error en el navegador, pero lo he probado en Chrome, Firefox 52 y la versión actual de Firefox, todos con resultados similares. No estoy seguro de a dónde ir a partir de aquí, ya que no he podido encontrar este problema en ningún lugar ni ninguna solución o solución alternativa. Sería muy apreciado si alguien pudiera encontrar una manera de hacer que esto funcione de manera confiable según lo previsto. 🙂


Aquí está la página de JSFiddle con un ejemplo de lo que quiero decir.

El marcado y el código también se pegan aquí:

var box = document.getElementById("box"); //Reduce this value or set it to 0 (I //want rid of the timeout altogether) //and it will only function correctly //intermittently. var delay = 25; setInterval(function() { box.style.transition = "none"; box.style.left = "1em"; setTimeout(function() { box.style.transition = "1s linear"; box.style.left = "11em"; }, delay); }, 1000); 
 #box { width: 5em; height: 5em; background-color: cyan; position: absolute; top: 1em; left: 1em; } 
 

Obligue al DOM a recalcularse antes de establecer una nueva transición después del reinicio. Esto se puede lograr, por ejemplo, leyendo el desplazamiento de la caja, algo como esto:

 var box = document.getElementById("box"); setInterval(function(){ box.style.transition = "none"; box.style.left = "1em"; let x = box.offsetLeft; // Reading a positioning value forces DOM to recalculate all the positions after changes box.style.transition = "1s linear"; box.style.left = "11em"; }, 1000); 
 body { background-color: rgba(0,0,0,0); } #box { width: 5em; height: 5em; background-color: cyan; position: absolute; top: 1em; left: 1em; } 
 

En lugar de hacer que la transition comporte como una animación, use la animation , hará un trabajo mucho mejor, lo más importante es el rendimiento y uno no necesita un temporizador para verlo.

Con los eventos de animation , se puede sincronizar la animación de la forma que se desee, incluido el encendido de un temporizador para reiniciarlo o modificarlo.

Ya sea con algunas partes siendo configuradas con CSS

 var box = document.getElementById("box"); box.style.left = "11em"; // start box.addEventListener("animationend", animation_ended, false); function animation_ended (e) { if (e.type == 'animationend') { this.style.left = "1em"; } } 
 #box { width: 5em; height: 5em; background-color: cyan; position: absolute; top: 1em; left: 1em; animation: move_me 1s linear 4; } @keyframes move_me { 0% { left: 1em; } }