Skip to content

S07-02 数据可视化-Canvas

[TOC]

API-Canvas

canvas

  • <canvas>with height,被用来通过 JS(Canvas API 或 WebGL API)绘制图形及图形动画的元素。
  • el.getContext()(contextType, contextAttributes?),返回canvas的上下文,如果上下文没有定义则返回null
  • el.toDataURL()(type?, quality?),将 Canvas 转换为 Base64 编码的图像。

设置

  • ctx.fillStylecolor| gradient | pattern默认:#000,设置或获取 Canvas 填充颜色或样式
  • ctx.strokeStylecolor| gradient | pattern默认:#000,描边颜色
  • ctx.globalAlphanumber默认:1.0,0~1的数值,值越小透明度越高,设置或获取 Canvas 的全局透明度。
  • ctx.createLinearGradient()(x0, y0, x1, y1),创建一个沿着参数坐标指定的线的线性渐变
  • ctx.createRadialGradient()(x0, y0, r0, x1, y1, r1),创建一个沿着参数坐标指定的线的放射性渐变
  • gradient.addColorStop()(offset, color),用于在渐变中设置颜色停靠点。gradient是通过上述2个方法创建出来的CanvasGradient对象。
  • ctx.createPattern()(image, repetition),用于创建一个图案。
  • ctx.lineWidthnumber默认:1,设置线条宽度。不带px单位
  • ctx.lineCapbutt | round | square默认:butt,用于设置路径的端点样式。
  • ctx.lineJoinround | bevel | miter默认:bevel,控制路径连接处的形状。

路径

  • ctx.beginPath()(),开始一条新的路径的方法。每次调用 beginPath() 都会清除当前路径,使接下来的绘图操作不会连接到先前的路径上。
  • ctx.closePath()(),用于关闭当前路径的方法。将路径的最后一部分连接回到起始点,形成一个闭合的路径。
  • ctx.stroke()(),通过线条来绘制图形轮廓/描边(针对当前路径)。
  • ctx.fill()(),通过填充路径的内容区域生成实心的图形(针对当前路径)。
  • ctx.moveTo()(x, y),将当前路径的起始点移动到指定的起始点坐标点
  • ctx.lineTo()(x, y),通过连接当前路径的末端点和指定的 (x, y) 坐标来绘制一条直线。
  • ctx.rect()(x, y, width, height),创建一个矩形路径。
  • ctx.arc()(x, y, radius, startAngle, endAngle, anticlockwise?),绘制一段圆弧或圆的方法。0表示3点钟方向。
  • ctx.ellipsis()(x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise?),添加一个椭圆路径。
  • ctx.bezierCurveTo()(cp1x, cp1y, cp2x, cp2y, x, y),添加一个 3 次贝赛尔曲线路径。起始点是当前路径的最后一个点,绘制贝赛尔曲线前,可以通过调用 moveTo() 进行修改。

绘制矩形

  • ctx.fillRect()(x, y, width, height),绘制填充矩形
  • ctx.strokeRect()(x, y, width, height),绘制一个描边矩形
  • ctx.clearRect()(x, y, width, height), 清除指定矩形区域,让清除部分完全透明。

绘制文本

  • ctx.fontfont-style? font-variant? font-weight? font-size line-height? font-family默认:10px sans-serif,设置文本的字体样式。
  • ctx.textAlignstart | end | left | right | center默认:start,文本对齐选项。
  • ctx.textBaselinetop | hanging | middle | alphabetic | ideographic | bottom默认:alphabetic,基线对齐选项。
  • ctx.fillText()(text, x, y, maxWidth?),绘制填充文本。
  • ctx.strokeText()(text, x, y, maxWidth?),绘制描边文本。

绘制图片

  • ctx.drawImage()(image, x, y)版本 1: 绘制图像到画布(不缩放、不裁剪)
  • ctx.drawImage()(image, x, y, width, height)版本 2: 绘制图像并缩放
  • ctx.drawImage()(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)版本 3: 从图像中裁剪出指定的区域,并将其绘制到画布。

形变

  • 保存和恢复绘画状态
  • ctx.save()(),将当前的绘图状态保存到状态栈中。
  • ctx.restore()(),从状态栈中恢复canvas的当前绘画状态。
  • ctx.translate()(x, y),平移整个画布上的所有内容。
  • ctx.rotate()(angle),用于旋转绘图上下文的坐标系。所有后续绘制的内容都将基于旋转后的坐标系进行。
  • ctx.scale()(x, y),用于缩放绘图上下文的整个坐标系。用来增减图形在canvas中的像素数目。会影响到所有后续绘制的图形。

动画

  • setInterval()(callback, interval?, arg1?, arg2?, ...),按照指定的时间间隔重复执行一个函数,直到被清除或页面关闭。
  • setTimeout()(callback, delay, arg1, arg2, ...),在指定的延迟时间后执行一个函数,只执行一次。
  • requestAnimationFrame()(callback),一种浏览器提供的机制,用于在浏览器的下一次重绘之前执行一个函数,它被广泛应用于动画的实现。

概述

介绍

什么是Canvas

Canvas 最初由 Apple 于 2004 年引入,用于 Mac OS X WebKit 组件,为仪表板小部件和 Safari 浏览器等应用程序提供支持。后来,它被 Gecko 内核的浏览器(尤其是 Mozilla Firefox),Opera 和 Chrome 实现,并被网页超文本应用技术工作小组提议为下一代的网络技术的标准元素(HTML5 新增元素)。

Canvas 提供了非常多的 JavaScript 绘图 API(比如:绘制路径、矩形、圆、文本和图像等方法),结合<canvas>元素可以绘制2D图形

Canvas API 主要聚焦于 2D 图形。当然也可以使用<canvas>元素对象的 WebGL API 来绘制3D图形

应用场景:

  • 可以用于动画游戏画面数据可视化图片编辑以及实时视频处理等方面。

浏览器兼容性:

image-20241029101315432

Canvas 优缺点

Canvas优点:

  • Canvas 提供的功能更原始,适合像素处理,动态渲染和数据量大的绘制,如:图片编辑、热力图、炫光尾迹特效等。

  • Canvas 非常适合图像密集型的游戏开发,适合频繁重绘许多的对象。

  • Canvas 能够以 .png 或 .jpg 格式保存结果图像,适合对图片进行像素级的处理

Canvas缺点:

  • 在移动端可以能会因为 Canvas 数量多,而导致内存占用超出了手机的承受能力,导致浏览器崩溃。

  • Canvas 绘图只能通过 JavaScript 脚本操作(all in js)。

  • Canvas 是由一个个像素点构成的图形,放大会使图形变得颗粒状和像素化,导致模糊

基础

初体验 Canvas

使用 Canvas 的注意事项:

  • <canvas><img> 元素很相像,唯一的不同就是它并没有 src 和 alt 属性。

  • <canvas> 标签只有两个属性——width 和 height( 单位默认为 px )。当没有设置宽度和高度时,canvas 会初始化宽为300px 和高为 150px。

  • <img> 元素不同,<canvas> 元素必须需要结束标签 </canvas>。如结束标签不存在,则文档其余部分会被认为是替代内容,将不会显示出来。

  • 测试 canvas.getContext() 方法的存在,可以检查浏览器是否支持 Canvas。


▸ 初体验 Canvas

1、Canvas 通用模板

image-20241115172920525

2、Canvas 绘制矩形

image-20241115173159664

Canvas坐标系统

在开始画图之前,我们需要了解一下 Canvas 网格(canvas grid)和 坐标系。

Canvas Grid 或 坐标空间

  • 假如,HTML 模板中有个宽 150px, 高 150px 的 <canvas> 元素。<canvas>元素默认被网格所覆盖。

  • 通常来说网格中的一个单元相当于 canvas 元素中的一像素。

  • 该网格的原点位于坐标 (0,0) 的左上角。所有元素都相对于该原点放置。

  • 网格也可以理解为坐标空间(坐标系),坐标原点位于 canvas 元素的左上角,被称为初始坐标系

    • 如右图中蓝色正方形,左上角的坐标为距离左边 x 像素,距离上边 y 像素,坐标为(x, y)
  • 网格或坐标空间是可以变换的,后面会讲如何将原点转换到不同的位置,旋转网格甚至缩放它。

注意: 移动、旋转、缩放坐标系后,默认所有后续变换都将基于新坐标系的变换

image-20241115173612819

填充规则

<canvas> 中,路径可以通过一系列的线段(或弯曲)来定义,可以形成封闭的区域。而非零环绕规则是用来决定这些封闭区域哪些会被填充的标准。

非零环绕规则: 通过计算从目标点出发的射线与路径相交的次数来判断该点是否在路径内。具体计算如下:

  • 从目标点出发,沿着某个方向绘制一条射线,计算它与路径相交的次数。
  • 每次射线与路径交点的方向性(顺时针或逆时针)会影响相交次数的正负。
    • 如果路径是顺时针方向(通常表示外部),则相交次数加1。
    • 如果路径是逆时针方向(通常表示内部),则相交次数减1。
  • 最终,路径内部是那些相交次数的绝对值不为零的区域。如果最终的值是零,说明该点在路径外部;如果值是非零的,说明该点在路径内部。

绘制矩形 Rectangle

Canvas支持两种方式来绘制矩形:

  • 矩形方法
  • 路径方法

路径的概念:

路径是通过不同颜色和宽度的线段或曲线相连形成的不同形状的点的集合。

除了矩形,其他的图形都是通过一条或者多条路径组合而成的。通常我们会通过众多的路径来绘制复杂的图形。

Canvas 绘图的矩形方法:

ctx.fillRect()(x, y, width, height),绘制填充矩形

ctx.strokeRect()(x, y, width, height),绘制一个描边矩形

ctx.clearRect()(x, y, width, height), 清除指定矩形区域,让清除部分完全透明。

方法参数:

  • 上面的方法都包含了相同的参数。

  • x 与 y 指定了在 canvas 画布上所绘制矩形的左上角(相对于原点)的坐标(不支持 undefined )。

  • width 和 height 设置矩形的尺寸。


▸ 基本使用:绘制矩形

image-20241115175550055

路径

介绍

什么是路径?

  • 图形的基本元素是路径。

  • 路径是通过不同颜色和宽度的线段或曲线相连形成的不同形状的点的集合

  • 路径是可由很多子路径构成,这些子路径都是在一个列表中,列表中所有子路径(线、弧形等)将构成图形。

  • 一个路径,甚至一个子路径,通常都是闭合的

使用路径绘制图形的步骤:

  • 1.首先需要创建路径起始点(beginPath)。

  • 2.然后使用画图命令去画出路径( arc 、lineTo )。

  • 3.之后把路径闭合( closePath , 不是必须)。

  • 4.一旦路径生成,就能通过描边(stroke)或填充路径区域(fill)来渲染图形。

image-20241115181003186

以下是绘制路径时,所要用到的函数

ctx.beginPath()(),开始一条新的路径的方法。每次调用 beginPath() 都会清除当前路径,使接下来的绘图操作不会连接到先前的路径上。

ctx.closePath()(),用于关闭当前路径的方法。将路径的最后一部分连接回到起始点,形成一个闭合的路径。

ctx.stroke()(),通过线条来绘制图形轮廓/描边(针对当前路径)。

ctx.fill()(),通过填充路径的内容区域生成实心的图形(针对当前路径)。

绘制直线

ctx.moveTo()(x, y),将当前路径的起始点移动到指定的起始点坐标点

ctx.lineTo()(x, y),通过连接当前路径的末端点和指定的 (x, y) 坐标来绘制一条直线。


移动画笔moveTo(x, y)方法

  • moveTo 方法是不能画出任何东西,但是它也是路径列表的一部分

  • moveTo 可以想象为在纸上作业,一支钢笔或者铅笔的笔尖从一个点到另一个点的移动过程。

  • moveTo(x, y): 将笔移动到指定的坐标 x 、 y 上。

  • 当 canvas 初始化或者 beginPath()调用后,我们通常会使用 moveTo(x, y)函数设置起点

  • 使用 moveTo 函数能够绘制一些不连续的路径。

绘制直线lineTo(x, y)方法

  • lineTo(x, y): 绘制一条从当前位置到指定 (x ,y)位置的直线。

    • 该方法有两个参数(x , y)代表坐标系中直线结束的点

    • 开始点和之前的绘制路径有关,之前路径的结束点就是接下来的开始点

    • 当然开始点也可以通过moveTo(x, y)函数改变。


▸ 基本使用:绘制一条直线

  • 第一步:调用 beginPath() 来生成路径。本质上,路径是由很多子路径(线、弧形、等)构成。

  • 第二步:调用 moveTo、lineTo 函数来绘制路径(路径可以是连续也可以不连续)。

  • 第三步:闭合路径 closePath(),虽然不是必需的,但是通常都是要闭合路径。

  • 第四步:调用 stroke()函数来给直线描边。

image-20241117115418704

绘制三角形 Triangle

▸ 基本使用:绘制一个三角形

  • 第一步:调用 beginPath() 来生成路径。

  • 第二步:调用 moveTo()、lineTo()函数来绘制路径。

  • 第三步:闭合路径 closePath(),不是必需的。

    • closePath() 方法会通过绘制一条从当前点到开始点的直线来闭合图形。

    • 如果图形是已经闭合了的,即当前点为开始点,该函数什么也不做。

  • 第四步:调用 stroke()函数来给线描边,或者调用 fill()函数来填充(使用填充 fill 时,路径会自动闭合,而 stroke 不会)。

image-20241117120537385

绘制圆弧 Arc、圆 Circle

绘制圆弧或者圆,使用 arc()方法。

ctx.arc()(x, y, radius, startAngle, endAngle, anticlockwise?),绘制一段圆弧或圆的方法。0表示3点钟方向。

弧度:

弧度(radian)是平面角的单位。1radian为当圆弧长度等于半径时的圆心角度。一整个圆的弧度为2π。

弧度VS角度:

  • 1角度 = Math.PI / 180
  • n角度对应的弧度 = (Math.PI / 180) * n角度

▸ 基本使用:绘制一个圆弧

  • 第一步:调用 beginPath() 来生成路径。

  • 第二步:调用 arc()函数来绘制圆弧。

  • 第三步:闭合路径 closePath(),不是必需的。

  • 第四步:调用 stroke()函数来描边,或者调用 fill()函数来填充(使用填充 fill 时,路径会自动闭合)。

image-20241117124138720


▸ 不重新开始路径,之前路径的结束点会作为新路径的开始点

image-20241117123922348


▸ 在一个路径中绘制多个图形

通过使用 ctx.moveTo() 方法修改起始点位置,实现2个圆不单独绘制

缺点: 需要计算起始点的坐标

image-20241117124609911

绘制矩形 Rectangle

ctx.rect()(x, y, width, height),创建一个矩形路径。

注意: 当该方法执行的时候,moveTo(x, y) 方法自动设置坐标参数(0,0)。也就是说,当前笔触自动重置回默认坐标。


▸ 基本使用:绘制一个矩形

image-20241117205624850

设置样式

色彩 Colors

前面已经学过了很多绘制图形的方法。如果我们想要给图形上色,有两个重要的属性可以做到:

ctx.fillStylecolor| gradient | pattern默认:#000,设置或获取 Canvas 填充颜色或样式

ctx.strokeStylecolor| gradient | pattern默认:#000,描边颜色

image-20241029101611816

color:

  • color 可以是表示 CSS 颜色值的字符串,支持:关键字十六进制rgbrgba 格式。

  • 默认情况下,线条和填充颜色都是黑色(CSS 颜色值 #000000)。

注意:

  • 一旦设置了 strokeStyle 或者 fillStyle 的值,那么这个新值就会成为新绘制的图形的默认值

  • 如果你要给图形上不同的颜色,你需要重新设置 fillStyle 或 strokeStyle 的值。

额外补充:

  • fill() 函数是图形填充,fillStyle 属性是设置填充色

  • stroke() 函数是图形描边,strokeStyle 属性是设置描边色


▸ 基本使用:填充颜色 fillStyle

image-20241117210646976


▸ 基本使用:描边颜色 strokeStyle

image-20241117210910937

透明度 Transparent

除了可以绘制实色图形,我们还可以用 canvas 来绘制半透明的图形。

▸ 方式一:strokeStyle 和 fillStyle 属性结合 RGBA

image-20241029101632505

▸ 方式二:globalAlpha 属性

ctx.globalAlphanumber默认:1.0,0~1的数值,值越小透明度越高,设置或获取 Canvas 的全局透明度。

  • 这个属性影响到 canvas 里所有图形的透明度

image-20241117212119677

线型 Line styles

调用 lineTo()函数绘制的线条,是可以通过一系列属性来设置线的样式。

ctx.lineWidthnumber默认:1,设置线条宽度。不带px单位

ctx.lineCapbutt | round | square默认:butt,用于设置路径的端点样式。

ctx.lineJoinround | bevel | miter默认:bevel,控制路径连接处的形状。

lineWidth

  • lineWidth设置线条宽度的属性值必须为正数。默认值是 1px,不需单位。( 零、负数、InfinityNaN值将被忽略)

  • 线宽是指给定路径的中心到两边的粗细。换句话说就是在路径的两边各绘制线宽的一半。

  • 如果你想要绘制一条从 (3,1) 到 (3,5),宽度是 1.0 的线条,你会得到像第二幅图一样的结果。

    • 路径的两边个各延伸半个像素填充并渲染出 1 像素的线条(深蓝色部分)

    • 两边剩下的半个像素又会以实际画笔颜色一半色调来填充(浅蓝部分)

    • 实际画出线条的区域为(浅蓝和深蓝的部分),填充色大于 1 像素了,这就是为何宽度为 1.0 的线经常并不准确的原因。

  • 要解决这个问题,必须对路径精确的控制。如,1px 的线条会在路径两边各延伸半像素,那么像第三幅图那样绘制从 (3.5 ,1) 到 (3.5,5) 的线条,其边缘正好落在像素边界,填充出来就是准确的宽为 1.0 的线条。

image-20241029101654952


▸ 基本使用:绘制精准的1px宽度线条

image-20241117214355799

lineCap

lineCap属性的值决定了线段端点样式。它可以为下面的三种的其中之一:

  • butt 截断,默认是 butt。

  • round 圆形

  • square 正方形

image-20241029101733278


▸ 基本使用

image-20241117214849372

lineJoin

lineJoin属性的值决定了图形中线段连接处样式。它可以是这三种之一:

  • round 圆形

  • bevel 斜角

  • miter 斜槽规,默认是 miter。

image-20241029101740957

绘制其他

绘制文本

canvas 提供了两种方法来渲染文本:

ctx.fillText()(text, x, y, maxWidth?),绘制填充文本。

ctx.strokeText()(text, x, y, maxWidth?),绘制描边文本。

image-20241029101751059

文本的样式: 需在绘制文本前调用

ctx.fontfont-style? font-variant? font-weight? font-size line-height? font-family默认:10px sans-serif,设置文本的字体样式。

ctx.textAlignstart | end | left | right | center默认:start,文本对齐选项。

ctx.textBaselinetop | hanging | middle | alphabetic | ideographic | bottom默认:alphabetic,基线对齐选项。


▸ 基本使用:绘制文本

image-20241117221357181

image-20241117221341322

绘制图片

绘制图片,可以使用 drawImage 方法将它渲染到 canvas 里。drawImage 方法有三种形态:

ctx.drawImage()(image, x, y)版本 1: 绘制图像到画布(不缩放、不裁剪)

ctx.drawImage()(image, x, y, width, height)版本 2: 绘制图像并缩放

ctx.drawImage()(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)版本 3: 从图像中裁剪出指定的区域,并将其绘制到画布。

图片的来源:

canvas 的 API 可以使用下面这些类型中的一种作为图片的源:

  • HTMLImageElement:这些图片是由 new Image()函数构造出来的,或者任何的<img>元素
  • HTMLVideoElement:用一个 HTML 的 <video>元素作为你的图片源,可以从视频中抓取当前帧作为一个图像。
  • HTMLCanvasElement:可以使用另一个 <canvas> 元素作为你的图片源。

▸ 基本使用:绘制图片

drawImage(image, x, y)

image-20241117222345362

drawImage(image, x, y, width, height)

image-20241117222558873

drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)

image-20250102172450647


▸ 后面绘制的图形会在前面绘制图形的图层上面

image-20241117223005642

形变

Canvas 和 CSS3 一样也是支持变形,形变是一种更强大的方法,可以将坐标原点移动到另一点、形变可以对网格(坐标系)进行旋转和缩放。

Canvas 的形变有 4 种方法实现:

ctx.translate()(x, y),平移整个画布上的所有内容。

ctx.rotate()(angle),用于旋转绘图上下文的坐标系。所有后续绘制的内容都将基于旋转后的坐标系进行。

ctx.scale()(x, y),用于缩放绘图上下文的整个坐标系。会影响到所有后续绘制的图形。

ctx.transform()(a, b, c, d, e, f),允许对变形矩阵直接修改。这个方法是将当前的变形矩阵乘上一个基于自身参数的矩阵。

注意:

  • 做变形之前先调用 save 方法保存状态是一个良好的习惯。大多数情况下,调用 restore 方法比手动恢复原先的状态要简单得多。
  • 如果在一个循环中做位移但没有保存和恢复 canvas 状态,很可能到最后会发现有些东西不见了,因为它很可能已超出 canvas 画布以外了。
  • 形变需要在绘制图形前调用

image-20241029101838544

保存和恢复绘画状态

Canvas 绘画状态 是当前绘画时所产生的样式和变形的一个快照。

  • Canvas 在绘画时,会产生相应的绘画状态,其实我们是可以将某些绘画的状态存储在栈中来为以后复用。

  • Canvas 绘画状态的可以调用 save 和 restore 方法是用来保存和恢复,这两个方法都没有参数,并且它们是成对存在的

保存和恢复(Canvas)绘画状态:

ctx.save()(),将当前的绘图状态保存到状态栈中。

ctx.restore()(),从状态栈中恢复canvas的当前绘画状态。

Canvas 绘画状态包括:

  • 当前应用的变形:即移动旋转缩放

  • 以下属性:strokeStyle, fillStyle, globalAlpha, lineWidth, lineCap, lineJoin, miterLimit,shadowOffsetX, shadowOffsetY, shadowBlur, shadowColor, font, textAlign, textBaseline ...

  • 当前的裁切路径:clipping path


▸ 基本使用:保存和恢复绘画状态

注意: ctx.save()ctx.restore()是成对存在的。

image-20241118174900928

移动 translate()

ctx.translate()(x, y),平移整个画布上的所有内容。

移动canvas原点的好处:

  • 如不使用 translate 方法,那么所有矩形默认都将被绘制在相同的(0,0)坐标原点。

  • translate 方法可让我们任意放置图形,而不需要手工一个个调整坐标值。


▸ 基本使用:移动矩形

调用translate()会修改canvas的坐标原点(红点位置)

注意: 第二次调用translate()计算坐标是在第一次位置的基础上计算的。

image-20241118180842880


▸ 移动前使用ctx.save()保存绘画状态

  • 第一步:先保存一下 canvas 当前的状态

  • 第二步:在绘制图形前 translate 移动画布

  • 第三步:开始绘制图形,并填充颜色

image-20241118220928544

旋转 rotate()

ctx.rotate()(angle),用于旋转绘图上下文的坐标系。所有后续绘制的内容都将基于旋转后的坐标系进行。

image-20241029101900300


▸ 基本使用:旋转矩形

坐标原点有1变到2再变到3,x、y轴顺时针旋转了45度

image-20241118221714065


▸ 旋转前使用ctx.save()保存绘画状态

  • 第一步:先保存一下 Canvas 当前的状态,并确定旋转原点

  • 第二步:在绘制图形前旋转画布(坐标系会跟着旋转了)

  • 第三步:开始绘制图形,并填充颜色

image-20241118222300693

缩放 scale()

ctx.scale()(x, y),用于缩放绘图上下文的整个坐标系。用来增减图形在canvas中的像素数目。会影响到所有后续绘制的图形。

注意:

  • 画布初始情况下,是以左上角坐标为原点。如果参数为负实数,相当于以 x 或 y 轴作为对称轴镜像反转。

    • 例如,使用 translate(0, canvas.height); scale(1,-1); 以 y 轴作为对称轴镜像反转。
  • 默认情况下,canvas 的 1 个单位为 1 个像素。如果我们设置缩放因子是 0.5,1 个单位就变成对应 0.5 个像素,这样绘制出来的形状就会是原先的一半。同理,设置为 2.0 时,1 个单位就对应变成了 2 像素,绘制的结果就是图形放大了 2 倍。


▸ 基本使用:缩放矩形

  • 第一步:先保存一下 Canvas 当前的状态,并确定缩放原点

  • 第二步:在绘制图形前缩放画布

  • 第三步:开始绘制图形,并填充颜色

注意: 缩放是对x和y轴的像素数进行放大,原先移动10px缩放后会移动20px,原先50px的宽度缩放后会变成100px的宽度

image-20241118222721827

Canvas动画

Canvas 绘图都是通过 JavaScript 去操控的,如要实现一些交互性动画是相当容易的。那 Canvas 是如何做一些基本动画的?

  • canvas 可能最大的限制就是图像一旦绘制出来,它就是一直保持那样了。

  • 如需要执行动画,不得不对画布上所有图形进行一帧一帧的重绘比如在 1 秒绘 60 帧就可绘出流畅的动画了)。

  • 为了实现动画,我们需要一些可以定时执行重绘的方法。然而在 Canvas 中有三种方法可以实现:

    • 分别为 setInterval()setTimeout()requestAnimationFrame() 三种方法来定期执行指定函数进行重绘。

画一帧动画: 如要画出流畅动画,1s 需绘 60 帧

  • 第一步:用 clearRect() 方法清空 canvas ,除非接下来要画的内容会完全充满 canvas(例如背景图),否则你需要清空所有。

  • 第二步:保存 canvas 状态,如果加了 canvas 状态的设置(样式,变形之类的),又想在每画一帧之时都是原始状态的话,你需要先保存一下,后面再恢复原始状态。

  • 第三步:绘制动画图形(animated shapes) ,即绘制动画中的一帧。

  • 第四步:恢复 canvas 状态,如果已经保存了 canvas 的状态,可以先恢复它,然后重绘下一帧。

绘制秒针 setInterval()

setInterval()(callback, interval?, arg1?, arg2?, ...),按照指定的时间间隔重复执行一个函数,直到被清除或页面关闭。

基本使用:使用setInterval()绘制一帧秒针动画

  • 第一步:用 clearRect(x,y, w,h)方法,清空 canvas 。

  • 第二步:保存 canvas 状态 。

  • 第三步:修改 canvas 状态 (样式、移动坐标、旋转等)。

  • 第四步:绘制秒针图形(即绘制动画中的一帧)。

  • 第五步:恢复 canvas 状态 ,准备重绘下一帧。

image-20241118230326467


setInterval() 定时器的缺陷:

  • setInterval 定时器不是非常精准的,因为 setInterval 的回调函数是放到了宏任务中等待执行

  • 如果微任务中一直有未处理完成的任务,那么 setInterval 的回调函数就有可能不会在指定时间内触发回调。

  • 如果想要更加平稳和更加精准的定时执行某个任务的话,可以使用 requestAnimationFrame() 函数。

绘制秒针 requestAnimationFrame()

requestAnimationFrame()(callback),一种浏览器提供的机制,用于在浏览器的下一次重绘之前执行一个函数,它被广泛应用于动画的实现。

  • 告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用该函数的回调函数来更新动画

  • 该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行

  • 若想在浏览器下次重绘之前继续更新下一帧动画,那么在回调函数自身内必须再次调用 requestAnimationFrame()

  • 通常每秒钟回调函数执行 60 次左右,也有可能会被降低。


基本使用:使用requestAnimationFrame()绘制一帧秒针动画

  • 第一步:用 clearRect(x,y, w,h)方法,清空 canvas 。

  • 第二步:保存 canvas 状态 。

  • 第三步:修改 canvas 状态 (样式、移动坐标、旋转等)。

  • 第四步:绘制秒针图形(即绘制动画中的一帧) 。

  • 第五步:恢复 canvas 状态 ,准备重绘下一帧。

image-20241118232107796

案例:太阳系旋转

基本使用:太阳系旋转动画

  • 第一步:用 clearRect(x,y, w,h)方法,清空 canvas ,并初始化全局样式。

  • 第二步:保存 canvas 状态 。

  • 第三步:绘制背景、绘制地球(绘制月球)、绘制阴影效果。

  • 第五步:恢复 canvas 状态 ,准备重绘下一帧。

image-20241029102036521

案例:时钟

▸ 绘制时钟-背景

1、模版

image-20241123162319853

2、绘制白色圆

image-20241123162547354


▸ 绘制时钟-数字

求圆上 x, y 的坐标:

  • 圆上 x, y 轴坐标实际上就是右图的 ( AB, BC ),AC 为时钟半径

  • x= AB = cosa * AC => x = Math.cos(弧度) * R

  • y= BC = sina * AC => y = Math.sin(弧度) * R

  • 角度与弧度的 JS 表达式:弧度=( Math.PI / 180 ) * 角度 => 弧度= 1 角度对应的弧度 * 角度 。

  • 比如:旋转 90°:弧度为 Math.PI / 2; 旋转 180°:为 Math.PI ; 旋转 360°:为 Math.PI * 2; 旋转-90°:为-Math.PI / 2;

  • 第 i 小时的坐标:

    • x = Math.cos( Math.PI * 2 / 12 * i ) * R

    • y = Math.sin( Math.PI * 2 / 12 * i ) * R

image-20241123172635354

image-20241123164410429


▸ 绘制时钟-时针、分针、秒针

1、绘制静态时针

image-20241123164910544

2、绘制时针动画

image-20241123165314288

3、绘制分针

image-20241123165733373

4、绘制秒针

image-20241123165941036


▸ 绘制时钟-圆心

image-20241123170416976


▸ 绘制时钟-刻度

1、绘制一个时针刻度

image-20241123171217076

2、绘制多个时针刻度

image-20241123171815474

3、绘制分针刻度

image-20241123172033389


▸ 绘制时钟-封装

image-20241123172544275

案例:黑板【