javascript技巧

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript技巧 > requestAnimationFrame优化

requestAnimationFrame用法优化源码解析

作者:千年老妖

这篇文章主要介绍了requestAnimationFrame用法优化源码解读,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

介绍

什么是requestAnimationFrame

requestAnimationFrame是浏览器用于定时循环操作的一个API,通常用于动画和游戏开发。它会把每一帧中的所有DOM操作集中起来,在重绘之前一次性更新,并且关联到浏览器的重绘操作。

为什么使用requestAnimationFrame而不是setTimeout或setInterval

setTimeoutsetInterval相比,requestAnimationFrame具有以下优势:

requestAnimationFrame的优势和适用场景

requestAnimationFrame最适用于需要连续高频执行的动画,如游戏开发,数据可视化动画等。它与浏览器刷新周期保持一致,不会因为间隔时间不均匀而导致动画卡顿。

使用方法

requestAnimationFrame的基本语法

requestAnimationFrame接收一个回调函数作为参数,该回调函数会在浏览器下一次重绘前执行。

const animation = () => {
  // 执行动画
  requestAnimationFrame(animation); 
}
requestAnimationFrame(animation);

上面代码会不停调用requestAnimationFrame,在每次浏览器重绘前执行回调函数,实现连续动画效果。

如何在动画中使用requestAnimationFrame

可以在回调函数里更新动画的状态,然后清除上一帧,绘制新状态的这一帧:

let angle = 0; 
const render = () => {
  ctx.clearRect(0, 0, width, height); // 清除上一帧
  // 更新状态
  angle += delta;
  ctx.beginPath();
  ctx.arc(x, y, radius, 0, angle); 
  ctx.stroke();
  requestAnimationFrame(render);
}

这样通过在每次回调中更新参数,就可以实现对象的连续动画了。

requestAnimationFrame的回调函数参数

requestAnimationFrame的回调函数会收到一个参数,这个参数是一个时间戳,单位为毫秒,代表requestAnimationFrame被触发的时间。可以根据这个时间戳计算两帧的时间间隔,来调整动画速度。

let prevTimestamp;
const render = timestamp => {
  if (!prevTimestamp) prevTimestamp = timestamp;
  const delta = timestamp - prevTimestamp;
  // 根据时间间隔计算速度
  x += speed * delta;
  prevTimestamp = timestamp;
  requestAnimationFrame(render);
}

性能优化

避免在requestAnimationFrame回调函数中进行大量计算

由于requestAnimationFrame的回调会在一个高优先级的线程中被同步执行,如果回调函数中有大量计算,会导致此线程被阻塞,从而引起页面卡顿。

也就是如果 requestAnimationFrame 的回调函数执行时间超过一帧(通常是 16.67 毫秒,因为浏览器通常以每秒约 60 帧的速度刷新),则可能会导致动画性能下降,可能出现掉帧的情况,最终影响用户体验。这是因为浏览器每帧的时间是有限的,如果回调函数执行时间过长,就会导致下一帧的准备和绘制时间受到压缩,导致动画卡顿。

通常,应该尽量避免在 requestAnimationFrame 的回调函数中执行耗时的操作。为了解决这个问题,可以采取以下一些策略:

总之,确保 requestAnimationFrame 回调函数的执行时间尽量短,以确保动画的流畅性和性能。

使用硬件加速优化动画性能

启用GPU加速渲染,可以显著提升动画性能。

.animated {
  transform: translateZ(0); /* 开启硬件加速 */  
}

如何在不同设备上实现平滑的动画效果

可根据requestAnimationFrame回调的时间戳,计算这一帧与上一帧的间隔时间delta,并根据delta的值采取不同的优化手段:

与其他动画库的比较

requestAnimationFrame与setTimeout/setInterval的区别

requestAnimationFrame与CSS动画的结合使用

requestAnimationFrame可用于更新动画状态,实现复杂动画逻辑,而CSS动画用于声明式定义动画的样式变化,两者可配合实现更丰富的动画效果。

实际应用

使用requestAnimationFrame实现常见动画效果

可使用 requestAnimationFrame 实现对象轨迹动画、SVG图形动画、加载动画等效果。

// 小球拖尾效果
const positions = [];
const render = () => {
  // 添加新位置
  positions.push({x, y});
  if (positions.length > 10){
    positions.shift(); 
  }
  // 渲染小球
  ctx.clearRect(0, 0, width, height);
  positions.forEach(pos => ctx.fillRect(pos.x, pos.y, 10, 10));
  requestAnimationFrame(render);
}
// SVG绘制动画
let length = 0;
const render = () => {
  length += 4;
  svgLine.setAttribute("stroke-dasharray", length);
  if (length < 300) {
    requestAnimationFrame(render);
  }
}
requestAnimationFrame(render); 
// 进度条加载动画  
let progress = 0;
const render = () => {
  progress += 1; 
  loadBar.style.width = progress + '%';
  if(progress < 100) {
    requestAnimationFrame(render);
  }
}
requestAnimationFrame(render);

requestAnimationFrame在游戏开发中的应用

游戏需要非常流畅的画面和实时的响应,这正是requestAnimationFrame的优势,它可用于实现游戏中的物体移动、碰撞检测、帧数控制等操作。

// 飞机射击动画
const update = () => {
  // 子弹位置更新
  bullets.forEach(bullet => {
    bullet.position += speed;
  })
  // 飞机位置更新
  aircraft.position += delta * speed;
  // 绘制所有元素
  render(bullets, aircraft);
  requestAnimationFrame(update);
}

requestAnimationFrame在响应式设计中的应用

可使用requestAnimationFrame来更平滑地执行响应式布局的变化,避免布局突然大幅移动带来的视觉冲击感。

let width = 500;
const resize = () => {
  width = container.clientWidth;
  box.style.width = width + "px";
  requestAnimationFrame(resize);
}
window.addEventListener("resize", resize);

兼容性和后续发展

requestAnimationFrame的浏览器兼容性

requestAnimationFrame现在已经得到了广泛支持,可以直接使用。对于不支持的浏览器,可以用setTimeout模拟requestAnimationFrame

window.requestAnimationFrame = window.requestAnimationFrame ||
                               window.webkitRequestAnimationFrame ||
                               (cb => setTimeout(cb, 1000/60));

requestAnimationFrame的未来发展趋势

未来requestAnimationFrame可能会支持设置帧率、增强调度算法等,提升动画性能。Web工作者线程也可带来更多优化空间。

浏览器厂商也在继续改进相关API,比如setTimeoutrequestIdleCallback也在朝着更精确的调度方向发展。

如何在不支持requestAnimationFrame的浏览器中实现类似效果

可以通过自己实现一个polyfill:

window.requestAnimationFrame = window.requestAnimationFrame || 
                               window.webkitRequestAnimationFrame ||
                               (cb => setTimeout(cb, 1000/60));

这样就可以在大多数浏览器中使用 requestAnimationFrame 了。对于老旧浏览器,可以使用 setInterval 模拟,但效果会比较粗糙。

总结

requestAnimationFrame是实现复杂动画的好帮手,必须掌握其用法与优化技巧,才能发挥其最大效用。同时结合其他技术如CSS动画、Web Worker等也可以实现更好的性能。随着浏览器的不断进步,requestAnimationFrame还具有很大的拓展潜力。

以上就是requestAnimationFrame用法优化源码解析的详细内容,更多关于requestAnimationFrame优化的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文