Canvas 的基本使用及动画效果

基本使用

在 HTML 文件中创建一个 <canvas> 标签

html 复制代码
<canvas id="myCanvas" width="500" height="400"></canvas>

获取<canvas>元素,然后调用getContext('2d')获得绘图的2D上下文对象:

js 复制代码
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');

示例:绘制一个红色矩形:

js 复制代码
// 设置填充颜色
ctx.fillStyle = 'red';
// 绘制矩形(x, y, 宽, 高)<- 左上角的点(x, y)
ctx.fillRect(50, 50, 200, 100);

示例:绘制线条:

js 复制代码
ctx.strokeStyle = 'blue';
ctx.lineWidth = 5;
ctx.beginPath();// 开始路径
ctx.moveTo(50, 50);// 起点
ctx.lineTo(250, 150);// 终点
ctx.stroke();// 绘制线条/多边形边框要用stroke

示例:绘制圆形(利用arc):

js 复制代码
ctx.beginPath();
ctx.arc(150, 150, 50, 0, Math.PI * 2);
ctx.fillStyle = 'green';
ctx.fill();

实例:绘制三角形

js 复制代码
<canvas id="myCanvas" width="500" height="400"></canvas>
const canvas = document.getElementById('myConvas')
const ctx = canvas.getContext('2d')
ctx.beginPath()
ctx.moveTo(30, 30)  // 第一个点
ctx.lineTo(40, 40)  // 第二个点
ctx.lineTo(20, 40)  // 第三个点
ctx.closePath()     // 封闭路径
ctx.fillStyle = 'red'
ctx.fill()

状态保存与恢复

  • ctx.save(): 将当前的绘图状态压入栈中。
  • ctx.restore(): 从栈中弹出状态,恢复到上一个保存的状态。
js 复制代码
ctx.save(); // 保存状态 A 
ctx.fillStyle = 'blue'; 
ctx.fillRect(100, 500, 50, 50); 
ctx.save(); // 保存状态 B (此时包含蓝色填充) ctx.fillStyle = 'yellow'; 
ctx.fillRect(150, 500, 50, 50); 
ctx.restore(); // 恢复到状态 B (此时颜色又变回蓝色,因为之前保存的是蓝色填充) 
ctx.restore(); // 恢复到状态 A (此时颜色是默认的黑色,或者之前设置的其他值)

几何变化

平移(Translate)

js 复制代码
// 绘制一个未进行平移的红色矩形 
context.fillStyle = 'red'; 
context.fillRect(10, 10, 50, 50); 
// 保存当前状态 
context.save(); 
// 将原点向下移动 100px,向右移动 100px 
context.translate(100, 100); 
// 这个矩形现在将相对于新的原点 (100, 100) 绘制
context.fillStyle = 'blue'; 
context.fillRect(10, 10, 50, 50); 
// 实际会绘制在 (110, 110) 的位置 
// 恢复状态 
context.restore();

旋转(Rotate)

js 复制代码
context.rotate(Math.PI / 4);
  • 正值表示顺时针旋转。
  • 负值表示逆时针旋转。

缩放(Scale)

js 复制代码
context.scale(x, y)
  • x: 水平方向上的缩放因子。
  • y: 垂直方向上的缩放因子。

requestAnimationFrame

使用 setTimeoutsetInterval 来实现 Canvas 动画 和 requestAnimationFrame的对比:

使用定时器动画干活,实际上是可以的,但是存在一个最大的问题,就是动画会抖动,体验效果不是非常好。

而使用requestAnimationFrame去做动画,就不会出现抖动的现象。

requestAnimationFramejs中的setTimeout定时器函数基本一致,不过setTimeout可以自由设置间隔时间,而requestAnimationFrame的间隔时间是由浏览器自身决定的,大约是17毫秒左右

那为什么定时器会卡顿呢?

1. 为什么定时器会卡

  • 我们在手机或者电脑显示屏上看东西时,显示屏会默默的不停地干活(刷新画面)
  • 这个刷新值得是每秒钟刷新次数,普通显示器的刷新率约为60Hz(每秒刷新60次),高档的有75Hz、90Hz、120Hz、144Hz等等
  • 刷新率次数越高,显示器显示的图像越清晰、越流畅、越丝滑
  • 不刷新就是静态的画面,刷新比较低就是卡了PPT的感觉
  • 动画想要丝滑流畅,需要卡住时间点进行代码操作(代码语句赋值、浏览器重绘)
  • 所以只需要每隔1000毫秒的60分之一(60HZ)即约为17毫秒,进行一次动画操作即可
  • 只要卡住这个17毫秒,每隔17毫秒进行操作,就能确保动画丝滑
  • 但是定时器的回调函数,会受到js的事件队列宏任务、微任务影响,可能设定的是17毫秒执行一次,但是实际上这次是17毫秒、下次21毫秒、再下次13毫秒执行,所以并不是严格的卡住了这个60HZ的时间
  • 没有在合适的时间点操作,就会出现:类似这样的情况:不变不变不变...
  • 于是就出现了,绘制不及时的情况,就会有抖动的出现(以上述案例,位置和时间没有线性对应更新变化导致看起来抖动)

2. 为何requestAnimationFrame不会卡

setTimeoutsetInterval的问题是,它们都不精确。它们的内在运行机制决定了时间间隔,参数实际上只是指定了把动画代码添加到浏览器UI线程队列中以等待执行的时间。如果队列前面已经加入了其他任务,那动画代码就要等前面的任务完成后再执行。

window.requestAnimationFrame(callback)能够做到,精准严格的卡住显示器刷新的时间,比如普通显示器60HZ它会自动对应17ms执行一次,高级显示器120HZ,它会自动对应9ms执行一次。callback 函数的执行频率会与屏幕刷新同步。

requestAnimationFrame只会执行一次,想要使其多次执行,要写成递归的形式。

所以,这就是requestAnimationFrame的好处,window.requestAnimationFrame这个api就是解决了定时器不精准的问题的。

示例:用 requestAnimationFrame() 实现的小球左右移动动画

html 复制代码
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8" />
<title>requestAnimationFrame动画示例</title>
</head>
<body>
<canvas id="myCanvas" width="500" height="200" style="border:1px solid #000;"></canvas>

<script>
  const canvas = document.getElementById('myCanvas');
  const ctx = canvas.getContext('2d');

  let x = 50;          // 小球横坐标
  const y = 100;       // 小球纵坐标
  const radius = 20;   // 小球半径
  let speed = 2;       // 每帧移动距离

  function draw() {
    ctx.clearRect(0, 0, canvas.width, canvas.height); // 清空画布

    // 绘制小球
    ctx.beginPath();
    ctx.arc(x, y, radius, 0, Math.PI * 2);
    ctx.fillStyle = 'blue';
    ctx.fill();

    // 更新位置
    x += speed;

    // 反弹边界检测
    if (x + radius > canvas.width || x - radius < 0) {
      speed = -speed; // 改变方向
    }

    // 下一帧动画请求
    requestAnimationFrame(draw);
  }

  // 启动动画
  requestAnimationFrame(draw);
</script>
</body>
</html>

参考链接:juejin.cn/post/720278...

相关推荐
行走在顶尖3 小时前
JS场景应用
前端
isyangli_blog3 小时前
(3-1) Html
前端·html
Yan-英杰3 小时前
Amazon SES + NestJS 实战:零成本打造高送达率邮箱验证方案
java·服务器·前端·网络·数据库·ai
智慧的牛3 小时前
React简单例子
javascript·react.js
weixin_456904274 小时前
Vue电商数据分析大屏开发
前端·vue.js·数据分析
玖笙&4 小时前
✨Vue 静态路由详解:构建应用的导航骨架(4)
前端·javascript·vue.js
这是个栗子4 小时前
(二)Vue.js 指令、事件与计算属性
前端·javascript·vue
黑客飓风4 小时前
Chrome性能优化指南
前端·chrome·性能优化
pc大老4 小时前
如何修复 Google Chrome 上的白屏问题
前端·网络·chrome·浏览器·谷歌