平滑的SVG动画

3

我试图使用给定的参数 rotateX 和 rotateY 来刷新动画:

$(document).ready(function() {
        var svg = document.getElementById("DC");
        var x = 10;
        var y = 0;
        for (j = 0; j<8 ; j++) {
            svg.setAttribute('style', 'transform: perspective(30em) rotateX(' + x + 'deg) rotateY(' + y + 'deg) scale(0.6, 0.6); perspective: 30em;');
            x = x  + 10;
        }
    });

但它没有起作用。如何平滑地刷新动画?以下是HTML代码:
<body><svg id="DC" version="1.0" viewBox="0 0 500 408" class="sim" x="0px" y="0px" width="100%" height="100%" style="transform: perspective(30em) rotateX(0deg) rotateY(0deg) scale(0.6, 0.6); perspective: 30em;"><style></body>


1
为什么不尝试使用GreenSock:https://greensock.com/ 另外,我不确定动画SVG标签是否是一个很好的主意,最好使用组或其他显示元素。 - Rodrigo
3个回答

2
问题在于每次执行动画时,您需要给浏览器一个绘制更改的机会。但是您并没有这样做。您的for循环只是不断地更改style属性,从未将控制权返回给浏览器。它从来没有机会更新页面。
有几种方法可以解决这个问题。曾经常用的方法是使用window.setTimeout()调用,以返回到浏览器并让它调用您的代码以更新动画。现在首选的方法是requestAnimationFrame()调用。

var svg = document.getElementById("DC");
var x = 10;
var y = 0;

function step(timestamp)
{
   svg.setAttribute('style', 'transform: perspective(30em) rotateX(' + x + 'deg) rotateY(' + y + 'deg) scale(0.6, 0.6); perspective: 30em;');
   x = x  + 10;
   window.requestAnimationFrame(step);
}

window.requestAnimationFrame(step);
svg {
  background-color: red;
}
<body>
<svg id="DC" version="1.0" viewBox="0 0 500 408" class="sim" x="0px" y="0px" width="100%" height="100%" style="transform: perspective(30em) rotateX(0deg) rotateY(0deg) scale(0.6, 0.6); perspective: 30em;"></svg>
</body>

在这里,每个动画步骤都是在step()函数中计算的。然后在从函数返回之前,您必须再次调用requestAnimation()来请求它再次调用step()

最后的调用是为了从头开始启动动画。


1

一个简单的解决方案是使用CSS过渡。
这将允许您轻松定义速度甚至使用的时间函数。

var svg = document.getElementById("DC");

function update(x)
{
   svg.setAttribute('style', 'transform: perspective(30em) rotateX('+x+'deg) rotateY(0deg) scale(0.6, 0.6); perspective: 30em;');
}
svg.parentNode.offsetWidth;
update(80);

num.onchange = e => update(num.value);
svg {
  background-color: red;
  transition: transform 2s ease-out;
}
<label>x rotation<input type="number" value="80" id="num"></label>
<svg id="DC" version="1.0" viewBox="0 0 500 408" class="sim" x="0px" y="0px" width="100%" height="100%" style="transform: perspective(30em) rotateX(0deg) rotateY(0deg) scale(0.6, 0.6); perspective: 30em;"></svg>

如果您需要循环播放,可以监听transitionend事件:

var svg = document.getElementById("DC");
x = 0;
function loop()
{
  x += 180;
  svg.setAttribute('style', 'transform: perspective(30em) rotateX('+x+'deg) rotateY(0deg) scale(0.6, 0.6); perspective: 30em;');
}
svg.addEventListener('transitionend', loop);
svg.parentNode.offsetWidth;
loop();
svg {
  background-color: red;
  transition: transform .5s linear;
}
<svg id="DC" version="1.0" viewBox="0 0 500 408" class="sim" x="0px" y="0px" width="100%" height="100%" style="transform: perspective(30em) rotateX(0deg) rotateY(0deg) scale(0.6, 0.6); perspective: 30em;"></svg>


0
一些尝试的建议: 1. 通过使用绝对定位和静态尺寸来隔离在DOM中被转换的元素 2. 设置CSS属性will-change: transform;
<body>
       <div class="viewport" style="position: relative; width: 500px; height: 408px;">
             <svg id="DC" version="1.0" viewBox="0 0 500 408" class="sim" width="500px" height="408px" style="transform: perspective(30em) rotateX(0deg) rotateY(0deg) scale(0.6, 0.6); transform-origin: 0px 0px 0px; will-change: transform;">
               </svg>
         </div>

3.使用过渡时间函数,例如jquery.animate而不是您的for循环,后者会指示浏览器立即呈现所有帧。我不确定jquery是否可以动画变换复杂性,但D3肯定可以。您也可以将此操作用于将动画放入动画队列中,尽管仍建议使用带有时间函数的库:

function animate(frames,x) {
    if(frames > 0) {
        svg.setAttribute('style', 'transform: perspective(30em) rotateX(' + x + 'deg) rotateY(' + y + 'deg) scale(0.6, 0.6); perspective: 30em;');

         requestAnimationFrame(function() { animate(--frames,x+= 10); });
    }
    return;
}

谢谢您的建议。如果我想设置值X,并且每次移动鼠标时增加10,我该怎么做?使用该函数动画无法正常工作。 - user3736228
你应该监听mousemove事件,然后将动画代码放在其中。一旦你得到了mousemove事件,请求requestAnimationFrame(function() { animate(...) ; });使用框架来完成所有这些工作会更容易 - 我喜欢D3,它有很多易于复制的示例。 - Xingzhou Liu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接