CSS Animation for transform no funciona correctamente cuando se utiliza un fotogtwig clave inicial con valor matrix3d

Necesito ejecutar una animación en un div para las propiedades scaleZ() y translateZ() usando CSS Animations.

El siguiente código funciona bien cuando los valores iniciales y últimos de fotogtwigs clave en la animación para la propiedad de transform están en un “formato” similar:

  • 0% es transform: rotateY(-179deg) scaleZ(2) translateZ(200px);
  • 100% es transform: rotateY(179deg) scaleZ(2) translateZ(200px);
  console.clear(); document.addEventListener('DOMContentLoaded', () => { let content1 = document.querySelector('#content1'); var computedTransform = window.getComputedStyle(content1).transform; console.log(computedTransform); }); 
  @-webkit-keyframes animation { 0% { /*works*/ -webkit-transform: rotateY(-179deg) scaleZ(2) translateZ(200px); transform: rotateY(-179deg) scaleZ(2) translateZ(200px); /*issue*/ /*transform: matrix3d(-0.999848, 0, 0.0174524, 0, 0, 1, 0, 0, -0.0349048, 0, -1.9997, 0, -6.98096, 0, -399.939, 1);*/ } 100% { -webkit-transform: rotateY(179deg) scaleZ(2) translateZ(200px); transform: rotateY(179deg) scaleZ(2) translateZ(200px); } } @keyframes animation { 0% { /*works*/ -webkit-transform: rotateY(-179deg) scaleZ(2) translateZ(200px); transform: rotateY(-179deg) scaleZ(2) translateZ(200px); /*issue*/ /*transform: matrix3d(-0.999848, 0, 0.0174524, 0, 0, 1, 0, 0, -0.0349048, 0, -1.9997, 0, -6.98096, 0, -399.939, 1);*/ } 100% { -webkit-transform: rotateY(179deg) scaleZ(2) translateZ(200px); transform: rotateY(179deg) scaleZ(2) translateZ(200px); } } #content1 { -webkit-animation: animation 2s; animation: animation 2s; -webkit-animation-fill-mode: forwards; animation-fill-mode: forwards; /*works*/ -webkit-transform: rotateY(-179deg) scaleZ(2) translateZ(200px); transform: rotateY(-179deg) scaleZ(2) translateZ(200px); /*issue*/ /*transform: matrix3d(-0.999848, 0, 0.0174524, 0, 0, 1, 0, 0, -0.0349048, 0, -1.9997, 0, -6.98096, 0, -399.939, 1);*/ } 
  


La misma animación con transform para key-frame 0% escrita como matrix3D devuelta desde Window.getComputedStyle() no hace que la animación funcione correctamente:

  • 0% es transform: matrix3d(-0.999848, 0, 0.0174524, 0, 0, 1, 0, 0, -0.0349048, 0, -1.9997, 0, -6.98096, 0, -399.939, 1);
  • 100% es transform: rotateY(179deg) scaleZ(2) translateZ(200px);
  console.clear(); document.addEventListener('DOMContentLoaded', () => { let content1 = document.querySelector('#content1'); var computedTransform = window.getComputedStyle(content1).transform; console.log(computedTransform); }); 
  @-webkit-keyframes animation { 0% { /*works*/ /*transform: rotateY(-179deg) scaleZ(2) translateZ(200px);*/ /*issue*/ -webkit-transform: matrix3d(-0.999848, 0, 0.0174524, 0, 0, 1, 0, 0, -0.0349048, 0, -1.9997, 0, -6.98096, 0, -399.939, 1); transform: matrix3d(-0.999848, 0, 0.0174524, 0, 0, 1, 0, 0, -0.0349048, 0, -1.9997, 0, -6.98096, 0, -399.939, 1); } 100% { -webkit-transform: rotateY(179deg) scaleZ(2) translateZ(200px); transform: rotateY(179deg) scaleZ(2) translateZ(200px); } } @keyframes animation { 0% { /*works*/ /*transform: rotateY(-179deg) scaleZ(2) translateZ(200px);*/ /*issue*/ -webkit-transform: matrix3d(-0.999848, 0, 0.0174524, 0, 0, 1, 0, 0, -0.0349048, 0, -1.9997, 0, -6.98096, 0, -399.939, 1); transform: matrix3d(-0.999848, 0, 0.0174524, 0, 0, 1, 0, 0, -0.0349048, 0, -1.9997, 0, -6.98096, 0, -399.939, 1); } 100% { -webkit-transform: rotateY(179deg) scaleZ(2) translateZ(200px); transform: rotateY(179deg) scaleZ(2) translateZ(200px); } } #content1 { -webkit-animation: animation 2s; animation: animation 2s; -webkit-animation-fill-mode: forwards; animation-fill-mode: forwards; /*works*/ /*transform: rotateY(-179deg) scaleZ(2) translateZ(200px);*/ /*issue*/ -webkit-transform: matrix3d(-0.999848, 0, 0.0174524, 0, 0, 1, 0, 0, -0.0349048, 0, -1.9997, 0, -6.98096, 0, -399.939, 1); transform: matrix3d(-0.999848, 0, 0.0174524, 0, 0, 1, 0, 0, -0.0349048, 0, -1.9997, 0, -6.98096, 0, -399.939, 1); } 
  

Por razones técnicas, necesito usar como key-frame 0% un valor para la transformation devuelto correctamente desde un estilo calculado en el DOM, utilizando Window.getComputedStyle() u otra función, si está disponible.

Mis preguntas:

  1. Es mi código para el segundo ejemplo con errores?
  2. ¿Podría sugerir una forma alternativa de obtener el valor calculado del DOM?
  3. ¿Conoce algún error relacionado con el valor devuelto por Window.getComputedStyle() ?
  4. ¿Conoces algún error con CSS Animations y diferentes “notación” para transform ?

Notas: Veo este problema en los últimos Chrome (55.0.2883.87 m) y FireFox (50.1.0).

Cualquier solución o idea es bienvenida.


EDITAR:

He creado algunos ejemplos nuevos (para Chrome) para una mayor investigación.

Básicamente, los dos ejemplos giran

from rotateY(20deg) to rotateY(90deg)

El uso de la transformación con un tipo de “notación” funciona como se esperaba

Efecto deseado https://jsbin.com/bodaxefake/edit?html,output

Cuando los valores son tomados por un estilo CSS calculado y reaplicados a la animación usando matrix3d, la animación tiene una ligera distorsión.

En su lugar, esperaría reproducir la animación que obtuviera exactamente el mismo resultado, ya que entiendo que matrix3d ​​de Window.getComputedStyle() debería devolver el mismo valor.

Efecto incorrecto https://jsbin.com/luhikahexi/edit?html,output

Su problema es el punto número 4. Pero no es realmente un error, es un algoritmo complejo que intenta averiguar qué es lo que quiere hacer.

Cuando haces una animación, cambias de rotar (0deg) a girar (360deg), ¿alguna vez te has preguntado si las 2 matrices son las mismas? Si solo se especifican los estados iniciales y finales, la animación no existiría.

Cuando configura la animación de la forma en que lo hizo, el algoritmo no tiene idea de qué hacer y, por lo tanto, el comportamiento no es el esperado. Pero no diría que esto es un error.

He configurado una animación que hace lo que quieres. El truco consiste en dejar la matriz constante, agregar una rotación inicial que es lo que vamos a cambiar, y simplemente agregar otra rotación opuesta a esta para mantener el estado original.

Como este fragmento es un poco extenso, he eliminado los prefijos de webkit. (por otro lado, ni realmente se necesita hoy en día)

 console.clear(); document.addEventListener('DOMContentLoaded', () => { let content1 = document.querySelector('#content1'); var computedTransform = window.getComputedStyle(content1).transform; console.log(computedTransform); }); 
 @keyframes animation { 0% { transform: matrix3d(-0.999848, 0, 0.0174524, 0, 0, 1, 0, 0, -0.0349048, 0, -1.9997, 0, -6.98096, 0, -399.939, 1); } 0.1% { transform: rotateY(-179deg) rotateY(179deg) matrix3d(-0.999848, 0, 0.0174524, 0, 0, 1, 0, 0, -0.0349048, 0, -1.9997, 0, -6.98096, 0, -399.939, 1); } 100% { transform: rotateY(179deg) rotateY(179deg) matrix3d(-0.999848, 0, 0.0174524, 0, 0, 1, 0, 0, -0.0349048, 0, -1.9997, 0, -6.98096, 0, -399.939, 1); } } #content1 { animation: animation 2s; animation-fill-mode: forwards; transform: matrix3d(-0.999848, 0, 0.0174524, 0, 0, 1, 0, 0, -0.0349048, 0, -1.9997, 0, -6.98096, 0, -399.939, 1); } 
 

De acuerdo con la especificación https://www.w3.org/TR/css-transforms-1/

Como los fotogtwigs clave de inicio y final no se describen como funciones de transformación, la interpolación numérica entre las funciones de transformación no es posible y el navegador necesita utilizar la interpolación de la matriz (como se describe en la última regla de Interpolación de transformaciones https://www.w3.org / TR / css-transformaciones-1 / # interpolación-de-transformaciones ) ,.

Durante la interpolación de la matriz, se pierde cierta información sobre el número de giros y la animación podría tener un comportamiento diferente del esperado.

Demostración con Web Animation API (última versión de Chrome): – Para Keyframe Start, configure rotateZ en 0. – Para Keyframe Start configure rotateZ en 360 o 350. – Haga clic en el botón Animate.

La animación pierde la información de turnos.

Vale la pena observar que este comportamiento también ocurre si los fotogtwigs clave de inicio y final se describen como funciones de transformación, pero el orden o el número de argumentos es diferente.

  (function () { let data = { translateX: { type: 'range', unit: 'px', min: -200, max: 200, steps: 400, value: { start: 0, end: 0 } }, translateY: { type: 'range', unit: 'px', min: -200, max: 200, steps: 400, value: { start: 0, end: 0 } }, translateZ: { type: 'range', unit: 'px', min: -200, max: 200, steps: 400, value: { start: 0, end: 0 } }, scaleX: { type: 'range', unit: '', min: -3, max: 3, steps: 6, value: { start: 1, end: 1 } }, scaleY: { type: 'range', unit: '', min: -3, max: 3, steps: 6, value: { start: 1, end: 1 } }, scaleZ: { type: 'range', unit: '', min: -3, max: 3, steps: 6, value: { start: 1, end: 1 } }, rotateX: { type: 'range', unit: 'deg', min: -180, max: 180, steps: 360, value: { start: 0, end: 0 } }, rotateY: { type: 'range', unit: 'deg', min: -180, max: 180, steps: 360, value: { start: 0, end: 0 } }, rotateZ: { type: 'range', unit: 'deg', min: 0, max: 360, steps: 360, value: { start: 0, end: 0 } }, skewX: { type: 'range', unit: 'deg', min: 0, max: 180, steps: 180, value: { start: 0, end: 0 } }, skewY: { type: 'range', unit: 'deg', min: 0, max: 180, steps: 180, value: { start: 0, end: 0 } }, perspective: { type: 'range', unit: 'px', min: 200, max: 1000, steps: 800, value: { start: 1000, end: 1000 } }, perspectiveOrigin: { type: 'text', unit: '', value: { start: '50% 50%', end: '50% 50%' } } }; let player; let submitedBy; let elmForm, elmPanelStart, elmPanelEnd, elmWrapper, elmContent, elmButtonAnimate, elmButtonReset; let getDoms = () => { elmForm = document.querySelector('#form'); elmPanelStart = document.querySelector('#panel-start'); elmPanelEnd = document.querySelector('#panel-end'); elmWrapper = document.querySelector('#wrapper'); elmContent = document.querySelector('#content'); elmButtonAnimate = document.querySelector('#button-animate'); elmButtonReset = document.querySelector('#button-reset'); }; let init = () => { getDoms(); createUi('start'); createUi('end'); createUiHandlers('start'); createUiHandlers('end'); createUiHandlersControls(); }; let createUi = (type) => { let html = ''; Object.keys(data).forEach((name) => { let props = data[name]; if (props.type === 'range') { html += ``; } if (props.type === 'text') { html += `
`; } }); if (type === 'start') { elmPanelStart.innerHTML = html; } if (type === 'end') { elmPanelEnd.innerHTML = html; } }; let updateDisplay = (type, name, value) => { if (name === 'perspectiveOrigin') { return; } let elmDisplay = document.querySelector(`#${name}-${type}-display`); elmDisplay.value = value; }; let handler = (type, event) => { let name = event.target.name, value = event.target.value; updateDisplay(type, name, value); updateData(type, name, value); // update data updateDomTarget(); }; let updateDomTarget = () => { let inline = ''; Object.keys(data).forEach((name) => { if (name === 'perspective' || name === 'perspectiveOrigin') { return; } let props = data[name]; inline += ` ${name}(${props.value.start}${props.unit})`; }); inline = inline.trim(); elmContent.style.transform = inline; elmWrapper.style.perspective = `${data.perspective.value.start}${data.perspective.unit}`; elmWrapper.style.perspectiveOrigin = `${data.perspectiveOrigin.value.start}${data.perspectiveOrigin.unit}`; }; let updateData = (type, name, value) => { data[name].value[type] = value; }; let refresh = () => { location.reload(); }; let resetPlayer = () => { if (player) { player.cancel(); player = null; } }; let logicAnimate = () => { resetPlayer(); let timing = { duration: 1000, fill: 'forwards' }, kfStart = '', kfEnd = ''; Object.keys(data).forEach((name) => { if (name === 'perspective' || name === 'perspectiveOrigin') { return; } let props = data[name]; kfStart += ` ${name}(${props.value.start}${props.unit})`; kfEnd += ` ${name}(${props.value.end}${props.unit})`; }); kfStart = kfStart.trim(); kfEnd = kfEnd.trim(); // change here kfStartComputedStyle = window.getComputedStyle(elmContent).transform; console.log(kfStartComputedStyle); kfStart = { transform: kfStartComputedStyle }; kfEnd = { transform: kfEnd }; elmContent.animate([kfStart, kfEnd], timing); }; let createUiHandlers = (type) => { Object.keys(data).forEach((name) => { let elm = document.querySelector(`#${name}-${type}`); elm.addEventListener('change', (event) => { handler(type, event); }); }); }; let createUiHandlersControls = (type) => { elmButtonAnimate.addEventListener('click', (event) => { logicAnimate(); }); elmButtonReset.addEventListener('click', (event) => { refresh(); }); }; document.addEventListener('DOMContentLoaded', () => { init(); }); })();
  h1 { font-size: 15px; } #content { width: 150px; height: 200px; background-color: red; } #panel-start-wrapper { position: fixed; top: 0; right: 0; margin: 10px; } #panel-end-wrapper { position: fixed; top: 420px; right: 0; margin: 10px; } #panel-controls { position: fixed; top: 850px; right: 0; margin: 10px; } label { display: block; } input[type=text] { width: 50px; } 
  
Hello world!

Keyframe Start

Keyframe End