如何使Vanilla Javascript动画不那么迟钝?

时间:2015-11-16 00:20:40

标签: javascript html css css3

我有一段代码应该移动this图片(id =' background'),我已在本地下载,并且非常大。当我将鼠标悬停在某个div的顶部时,它应该移动。然后,这会更改不透明度CSS值,而后者又由js检测到,然后使图像移动。 js代码如下所示:

setInterval(function(){
    var element = document.getElementById('background'),
        style = window.getComputedStyle(element),
        left = style.getPropertyValue('left');
    left = left.replace(/px/g, "")
    left = parseInt(left,10)
    if(getOpacity('rightbar') == .5){
        document.getElementById('background').style.left = left - 8 + 'px'
    }
    if(getOpacity('leftbar') == .5){
        document.getElementById('background').style.left = left + 8 + 'px'
    }
},10)

getOpacity(idName)函数如下所示:

function getOpacity(param){
    var element = document.getElementById(param),
        style = window.getComputedStyle(element),
        opacity = style.getPropertyValue('opacity');
    return opacity
}

所以问题在于,无论我使用什么移动值或setInteveral时间,动画总是会变得迟钝。是否有办法使用vanilla js来平滑,或者更好的是,废弃不透明度检测并使用CSS完成所有操作?

当我将上述代码放在fiddle中时,它工作正常,但当它实际运行完整浏览器时(在我个人的Chrome窗口中),它看起来像this

注意:我在4k显示器上运行这个完整的浏览器窗口,这对于处理chrome来说太多了吗?

2 个答案:

答案 0 :(得分:1)

1。使用requestAnimationFrame代替setInterval

这表示您希望在下次重绘之前执行某些操作。您提供的回调每帧执行一次

(如果这很重要:requestAnimationFrame在IE9及以下版本中不起作用。)

2。不要增加每帧的固定值,值之间的补间

使用requestAnimationFrame和使用setInterval时,帧之间的时差会有所不同。

您可以在开发人员工具栏中使用以下内容验证自己:

var last = new Date();
function onFrame(){
  var now = new Date();
  console.log(new Date() - last);
  last = now;
  requestAnimationFrame(onFrame);
}
onFrame();

开发人员控制台将以ms为单位输出帧时间,如下所示:

16
17
17
15
19
...

如果您在平均间隔时增加固定金额的位置(例如不透明度不明显),则动画看起来会呈锯齿状。所以,不要做left = left + 8;,而是根据当前时间计算动画中的哪个位置,如下所示:

var myElement = ...;
var initialOpacity = 1.0;
var targetOpacity = 0.5;
var duration = 2000;
var startTime = new Date();

function animation() {
  var delta = Math.min(1, (new Date() - startTime) / duration);
  // delta is now a number in the range [0 ... 1]

  myElement.style.opacity = initialOpacity + delta * (targetOpacity - initialOpacity);

  if (delta < 1) requestAnimationFrame(animation);
}
requestAnimationFrame(animation);

是的,这个例子补充了不透明度而不是位置,但是你得到了这个想法 - 你的老师不能要求你复制粘贴; - )

3。不要在JS和CSS之间来回读写

假设您的图像的初始位置与视口无关(例如,left: -10%),则无需在每一帧上读取位置。 当您的JavaScript是唯一更改left属性的内容时,为什么要从CSS中读取它?将其保存在变量中,并将其设置为JavaScript中的CSS。

var initialX = ComputeCssXPosition(myElement);
...
function animate() {
  ...
  myElement.style.left = computedNewXPosition;
}

如果您想在用户悬停元素时更改帖子,请在JS中使用鼠标事件。

myElement.addEventListener('mouseover', function (ev) { ... });
myElement.addEventListener('mouseout', function (ev) { ... });

替代方案:使用CSS过渡

answer by Shomz已经涵盖。

答案 1 :(得分:0)

最好的方法是使用requestAnimationFrame代替setInterval而不是检查样式更改,但是使用鼠标悬停监听器(过多的通信:CSS-&gt; JS-&gt; CSS- &GT; JS ...)。见这里:https://jsfiddle.net/yqpun3eb/4/

但是,如果您仍想使用setInterval,则只需将转换CSS规则放在background元素上即可。例如:

#background {transition: left 0.2s linear}

这样可以消除所有的值变化,因为CSS的表现更好,所以即使在4K屏幕上也应该没问题。问题是您的更改可以跳过8个像素。

似乎在我的机器上以0.2秒工作:https://jsfiddle.net/yqpun3eb/3/

哦,顺便说一下。你想要良好的表现,但为什么要用这个来强奸系统:

function getOpacity(param){
    var element = document.getElementById(param),
        style = window.getComputedStyle(element),
        opacity = style.getPropertyValue('opacity');
    return opacity
}

这不会创建额外的变量(无论如何你都不需要):

function getOpacity(param){
    return window.getComputedStyle(document.getElementById(param))
                 .getPropertyValue('opacity');
}

最后,这是使用requestAnimationFrame稍微优化的版本(我将如何操作+使用听众而不是阅读样式值):https://jsfiddle.net/yqpun3eb/4/