当使用具有matrix3d值的初始关键帧时,用于变换的CSS动画无法正常工作

时间:2017-01-11 11:25:40

标签: javascript html css css3 css-animations

我需要使用CSS动画在属性scaleZ()translateZ()的div上执行动画。

transform属性的动画中的初始和最后一个关键帧值具有类似的“格式”时,以下代码可以正常工作:

  • 0%是transform: rotateY(-179deg) scaleZ(2) translateZ(200px);
  • 100%是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);*/
        }
    <div id="wrapper1" style="position:fixed; top: 100px; left:300px; perspective: 1000px; width: 250px; height:250px; border: dotted 1px blue">
        <div id="content1" style="width: 250px; height:250px; background-color:lightsalmon; opacity:0.2;">
        </div>
    </div>

transform返回的关键帧0%写为matrix3D的{​​{1}}相同的动画无法使动画正常工作:

  • 0%是Window.getComputedStyle()
  • 100%是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: 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);
        }

由于技术原因,我需要使用 <div id="wrapper1" style="position:fixed; top: 100px; left:300px; perspective: 1000px; width: 250px; height:250px; border: dotted 1px blue"> <div id="content1" style="width: 250px; height:250px; background-color:lightsalmon; opacity:0.2;"> </div> </div>或其他函数(如果可用)作为关键帧0%使用transformation或其他函数从DOM中的计算样式正确返回Window.getComputedStyle()的值。

我的问题:

  1. 我的代码是第二个例子吗?
  2. 您能否建议另一种从DOM获取计算值的方法?
  3. 您是否知道与Window.getComputedStyle()返回的值相关的任何错误?
  4. 你知道CSS动画的任何错误和transform的不同“符号”吗?
  5. 注意:我在最新的Chrome(55.0.2883.87 m)和FireFox(50.1.0)上看到此问题。

    欢迎任何解决方案或想法。

    编辑:

    我创建了一些新的示例(适用于Chrome)以供进一步调查。

    基本上两个例子是

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

    将变换与一种“符号”一起使用,按预期工作

    期望的效果 https://jsbin.com/bodaxefake/edit?html,output

    当值由计算的CSS样式取代并使用matrix3d重新应用于动画时,动画会有轻微的失真。

    相反,我希望重现完全相同结果的动画,因为我理解来自Window.getComputedStyle()的matrix3d应返回相同的值。

    效果不正确 https://jsbin.com/luhikahexi/edit?html,output

2 个答案:

答案 0 :(得分:5)

你的问题是第4点。但它并不是一个真正的错误,它是一个复杂的算法,试图找出你想要做什么。

当您将动画从旋转(0度)移动到旋转(360度)时,您是否曾想过2个矩阵是否相同?如果仅指定了最初的最终状态,则动画将不存在。

当您按照自己的方式设置动画时,算法无法做什么,因此行为不是您所期望的。但我不会说这是一个错误。

我设置了一个动画,可以做你想要的。诀窍是让矩阵保持不变,添加一个我们将要改变的初始旋转,然后添加另一个与此相反的旋转以保持原始状态。

由于这个片段有点夸张,我删除了webkit前缀。 (另一方面,现在也不需要)

&#13;
&#13;
console.clear();
document.addEventListener('DOMContentLoaded', () => {
  let content1 = document.querySelector('#content1');
  var computedTransform = window.getComputedStyle(content1).transform;
  console.log(computedTransform);

});
&#13;
@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);
}
&#13;
<div id="wrapper1" style="position:fixed; top: 100px; left:300px; perspective: 1000px; width: 250px; height:250px; border: dotted 1px blue">
  <div id="content1" style="width: 250px; height:250px; background-color:lightsalmon; opacity:0.2;">
  </div>
</div>
&#13;
&#13;
&#13;

真正的问题是动画的起点和终点没有足够的信息来重建它。变换的矩阵不具备您在一系列单独变换中提供的信息

参见片段(2D):开始和结束状态相同,动画不是。在3D中,还有更多的可能性

&#13;
&#13;
.test {
  width: 50px;
  height: 50px;
  position: absolute;
  top: 400px;
  left: 100px;
}
#one {
  background-color: lightgreen;
  transform: translateX(200px);
  animation: tr1 6s infinite;
}
@keyframes tr1 {
  0% {
    transform: rotate(0deg) translateX(200px)
  }
  33%,
  100% {
    transform: rotate(-90deg) translateX(200px)
  }
}
#two {
  background-color: lightblue;
  transform: translate(200px, 0px);
  animation: tr2 6s infinite;
}
@keyframes tr2 {
  0%, 33% {
    transform: translate(200px, 0px) rotate(0deg)
  }
  66%,
  100% {
    transform: translate(0px, -200px) rotate(-90deg)
  }
}
#three {
  background-color: tomato;
  transform: translate(200px, -200px) rotate(0deg) translateY(200px);
  animation: tr3 6s infinite;
}
@keyframes tr3 {
  0%, 66% {
    transform: translate(200px, -200px) rotate(0deg) translateY(200px) rotate(0deg);
  }
  100% {
    transform:  translate(200px, -200px) rotate(-270deg)  translateY(200px) rotate(180deg);
  }
}
&#13;
<div class="test" id="one">1</div>
<div class="test" id="two">2</div>
<div class="test" id="three">3</div>
&#13;
&#13;
&#13;

答案 1 :(得分:0)

根据规范https://www.w3.org/TR/css-transforms-1/

由于开始和结束关键帧都没有被描述为变换函数, 变换函数之间的数值插值是不可能的,浏览器需要使用矩阵插值(如变换插值https://www.w3.org/TR/css-transforms-1/#interpolation-of-transforms的最后一条规则所述),.

在矩阵插值过程中,一些关于匝数的信息会丢失,动画可能会出现与预期不同的行为。

使用Web Animation API演示(最新的Chrome): - 对于Keyframe Start,将rotateZ设置为0。 - 对于Keyframe Start,将rotateZ设置为360或350。 - 单击按钮Animate。

动画丢失转弯信息。

值得注意的是,如果将起始和结束关键帧都描述为转换函数但它们的顺序或参数数量不同,则会发生此行为。

&#13;
&#13;
 (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 += `<label>${name}<input id="${name}-${type}" type="${props.type}" value="${props.value[type]}" name="${name}" min="${props.min}" max="${props.max}" steps="${props.steps}" ><input id="${name}-${type}-display" type="text" value="${props.value[type]}" name="${name}-${type}-show" readonly><br></label>`;
                    }
                    if (props.type === 'text') {
                        html += `<label>${name}<input id="${name}-${type}" type="${props.type}" value="${props.value[type]}" name="${name}" ></label><br>`;
                    }
                });
                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();
            });
        })();
&#13;
 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;
        }
&#13;
   <div id="wrapper" style="position:absolute; perspective: 500px; top:10%; left:10%;">
        <div id="content">
            Hello world!
        </div>
    </div>
    <form id="form">
        <div id="panel-start-wrapper">
            <h1>Keyframe Start</h1>
            <div id="panel-start">
            </div>
        </div>
        <div id="panel-end-wrapper">
            <h1>Keyframe End</h1>
            <div id="panel-end">
            </div>
        </div>
        <div id="panel-controls">
            <button id="button-animate" type="button">Animate</button>
            <button id="button-reset" type="button">Reset</button>
        </div>
    </form>
&#13;
&#13;
&#13;