javascript基础学习系列二百九十:cancelAnimationFrame

本文介绍了requestAnimationFrame的用法,如何通过它进行事件节流,以及如何在Canvas上进行2D绘图,包括获取绘图上下文、使用toDataURL导出图像,同时关注了浏览器兼容性问题。
摘要由CSDN通过智能技术生成

与 setTimeout()类似,requestAnimationFrame()也返回一个请求 ID,可以用于通过另一个 方法 cancelAnimationFrame()来取消重绘任务。下面的例子展示了刚把一个任务加入队列又立即将 其取消:


let requestID = window.requestAnimationFrame(() => {
  console.log('Repaint!');
     window.cancelAnimationFrame(requestID);

通过requestAnimationFrame节流

requestAnimationFrame 这个名字有时候会让人误解,因为看不出来它跟排期任务有关。支持这 个方法的浏览器实际上会暴露出作为钩子的回调队列。所谓钩子(hook),就是浏览器在执行下一次重 绘之前的一个点。这个回调队列是一个可修改的函数列表,包含应该在重绘之前调用的函数。每次调用 requestAnimationFrame()都会在队列上推入一个回调函数,队列的长度没有限制。
这个回调队列的行为不一定跟动画有关。不过,通过 requestAnimationFrame()递归地向队列 中加入回调函数,可以保证每次重绘最多只调用一次回调函数。这是一个非常好的节流工具。在频繁执 行影响页面外观的代码时(比如滚动事件监听器),可以利用这个回调队列进行节流。
先来看一个原生实现,其中的滚动事件监听器每次触发都会调用名为 expensiveOperation()(耗 时操作)的函数。当向下滚动网页时,这个事件很快就会被触发并执行成百上千次:

   function expensiveOperation() {
      console.log('Invoked at', Date.now());
}
         window.addEventListener('scroll', () => {
      expensiveOperation();
});

如果想把事件处理程序的调用限制在每次重绘前发生,那么可以像这样下面把它封装到 request- AnimationFrame()调用中:

   function expensiveOperation() {
      console.log('Invoked at', Date.now());
}
    window.addEventListener('scroll', () => {
      window.requestAnimationFrame(expensiveOperation);
});

这样会把所有回调的执行集中在重绘钩子,但不会过滤掉每次重绘的多余调用。此时,定义一个标 志变量,由回调设置其开关状态,就可以将多余的调用屏蔽:

   let enqueued = false;
function expensiveOperation() { console.log('Invoked at', Date.now()); enqueued = false;
}
    window.addEventListener('scroll', () => {
      if (!enqueued) {
        enqueued = true;
        window.requestAnimationFrame(expensiveOperation);
}
});

因为重绘是非常频繁的操作,所以这还算不上真正的节流。更好的办法是配合使用一个计时器来限 制操作执行的频率。这样,计时器可以限制实际的操作执行间隔,而 requestAnimationFrame 控制 在浏览器的哪个渲染周期中执行。下面的例子可以将回调限制为不超过 50 毫秒执行一次:

   let enabled = true;
    function expensiveOperation() {
      console.log('Invoked at', Date.now());
}
    window.addEventListener('scroll', () => {
if (enabled) {
enabled = false; window.requestAnimationFrame(expensiveOperation); window.setTimeout(() => enabled = true, 50);
}
});

基本的画布功能

创建元素时至少要设置其 width 和 height 属性,这样才能告诉浏览器在多大面积上绘 图。出现在开始和结束标签之间的内容是后备数据,会在浏览器不支持元素时显示。比如:

<canvas id="drawing" width="200" height="200">A drawing of something.</canvas>

与其他元素一样,width 和 height 属性也可以在 DOM 节点上设置,因此可以随时修改。整个元 素还可以通过 CSS 添加样式,并且元素在添加样式或实际绘制内容前是不可见的。
要在画布上绘制图形,首先要取得绘图上下文。使用 getContext()方法可以获取对绘图上下文的 引用。对于平面图形,需要给这个方法传入参数"2d",表示要获取 2D 上下文对象:

let drawing = document.getElementById("drawing");
// 确保浏览器支持<canvas> if (drawing.getContext) {
  let context = drawing.getContext("2d");
// 其他代码 }

使用元素时,最好先测试一下 getContext()方法是否存在。有些浏览器对 HTML 规范 中没有的元素会创建默认 HTML 元素对象。这就意味着即使 drawing 包含一个有效的元素引用, getContext()方法也未必存在。
可以使用 toDataURL()方法导出元素上的图像。这个方法接收一个参数:要生成图像 的 MIME 类型(与用来创建图形的上下文无关)。例如,要从画布上导出一张 PNG 格式的图片,可以这 样做:

2D 绘图上下文

   let drawing = document.getElementById("drawing");
// 确保浏览器支持<canvas> if (drawing.getContext) {
// 取得图像的数据URI
let imgURI = drawing.toDataURL("image/png");
// 显示图片
let image = document.createElement("img"); image.src = imgURI; document.body.appendChild(image);
}

浏览器默认将图像编码为 PNG 格式,除非另行指定。Firefox 和 Opera 还支持传入"image/jpeg" 24 进行 JPEG 编码。因为这个方法是后来才增加到规范中的,所以支持的浏览器也是在后面的版本实现的,
包括 IE9、Firefox 3.5 和 Opera 10。

注意 如果画布中的图像是其他域绘制过来的,toDataURL()方法就会抛出错误。相关内 容本章后面会讨论。

  • 10
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值