Canvas 入门篇

136 阅读6分钟

引言

Canvas 本身只是 h5 的一个容器标签,本身并没有直接提供绘图功能,而是提供了一个空白的矩形画布,需要使用 JavaScript 来操作 Canvas API 进行图形绘制

Canvas API 提供了一组用于绘制图形、文本、图像等的方法和属性。通过 JavaScript 可以获取到 <canvas> 元素的 2D 或 3D 上下文(context),然后使用上下文来进行绘制操作

Canvas API

在了解和使用 Canvas API 之前,需要先创建 canvas 画布,然后在画布上通过 Canvas API 进行图形绘制,所以下面先给出如何创建画布的案例

<canvas id="myCanvas" width="" height=""></canvas>

除了设置画布宽高,还可以给画布本身添加一些 style 样式

画布创建好之后就可以通过 API 获取画布并进行绘制了,在这之前先科普一下:canvas 是一个二维网格,左上角坐标为(0, 0)

获取画布上下文

创建好画布之后需要获取到画布才能开始绘制,canvas 提供了 getContext API 实现此功能

const canvas = document.getElementById("myCanvas");
let context = canvas.getContext("2d");

绘制基本形状

绘制基本形状,只需要掌握绘制矩形和圆形这两个 Canvas API 即可,其他形状可以通过这个两个 API 变形或者组合而成

绘制矩形

API:fillRect(x, y, width, height)、strokeRect(x, y, width, height)

  • fillRect(x, y, width, height):绘制实心矩形
    • x:矩形的 x 坐标
    • y:矩形的 y 坐标
    • width:矩形的宽度
    • height:矩形的高度
  • strokeRect(x, y, width, height):绘制空心矩形
    • x:矩形的 x 坐标
    • y:矩形的 y 坐标
    • width:矩形的宽度
    • height:矩形的高度
context.fillStyle = 'red'
context.fillRect(8, 8, 100, 75);
context.stroke();

context.strokeStyle = 'blue';
context.strokeRect(124, 8, 100, 75);

绘制圆形

API:arc(x, y, r, startAngle, endAngle, anticlockwise)

  • x:圆心的 x 坐标
  • y:圆心的 y 坐标
  • radius:圆的半径
  • startAngle:起始角度,以弧度表示
  • endAngle:结束角度,以弧度表示
  • anticlockwise:可选参数,指定绘制方向是顺时针还是逆时针,默认是 false(顺时针)
context.arc(58, 141, 50, 0, Math.PI * 2);
context.stroke();

绘制路径

绘制路径,用于进行复杂图形的绘制

API:beginPath()、closePath()、moveTo(x, y)、lineTo(x, y)

  • beginPath():用于起始一条新的路径,清空当前的所有路径以及子路径
  • closePath():用于闭合路径,即将路径的起点和终点连接起来,形成一个封闭的形状
  • moveTo(x, y):用于将笔触移动到指定的坐标(x, y),不画线
    • x:画笔的 x 坐标
    • y:画笔的 y 坐标
  • lineTo(x, y):用于将笔触从当前点连接到指定的点(x, y),画一条直线
    • x:画笔的 x 坐标
    • y:画笔的 y 坐标
context.beginPath();
context.moveTo(58, 199);
context.lineTo(8, 249);
context.lineTo(108, 249);
context.closePath();
context.stroke();

beginPath

最初对于beginPath用来清空当前的所有路径以及子路径这一描述感到很迷惑,不太清楚它到底有什么作用?如果是单纯的从一个点开始新的绘图,那 moveTo() + closePath() 不就可以实现这个效果?

后来通过一个例子,我便明白了:

// 绘制圆形
context.arc(58, 141, 50, 0, Math.PI * 2);
context.stroke();

// 绘制路径
context.beginPath();
context.moveTo(58, 199);
context.lineTo(8, 249);
context.lineTo(108, 249);
context.closePath();
context.stroke();

上面绘制了两个图形:圆形 + 路径。在绘制圆形时,把圆的形状描绘后便立即调用stroke()进行图形绘制,如果此时去掉这一行代码,会发现页面不会绘制出圆形,但如果再去掉beginPath(),你会神奇的发现,圆形出现了,因为在最后一行调用stroke()时进行了整体绘制

此时,对于beginPath清空当前的所有路径以及子路径应该就有所领悟了~

绘制文本

API:fillText(text, x, y)、strokeText(text, x, y)

  • fillText(text, x, y):绘制实心文本
    • text:文本内容
    • x:文本的 x 坐标
    • y:文本的 y 坐标
  • strokeText(text, x, y):绘制空心文本
    • text:文本内容
    • x:文本的 x 坐标
    • y:文本的 y 坐标
const text = "Hello, Canvas!";
context.font = "30px Arial";
const textMetrics = context.measureText(text);
const totalHeight = textMetrics.actualBoundingBoxAscent + textMetrics.actualBoundingBoxDescent;

context.fillStyle = 'green';
context.fillText(text, 8, 257 + totalHeight);
context.stroke();

context.strokeStyle = 'purple';
context.font = "30px Arial";
context.strokeText(text, 16 + textMetrics.width, 257 + totalHeight);

绘制图像

API:drawImage(image,x,y)

  • image:Image 对象
  • x:图片的 x 坐标
  • y:图片的 y 坐标
context.fillStyle = "black";
context.fillRect(8, 265 + totalHeight, 120, 25);

const image = new Image();
image.src = "https://lf-cdn-tos.bytescm.com/obj/static/xitu_extension/static/brand-dark.3111cff6.svg";
image.onload = () => {
    context.drawImage(image, 8, 265 + totalHeight)
}

变换

API:translate(x, y)、rotate(deg)、scale(scaleX, scaleY)

  • translate(x, y):用于平移绘图环境的原点
  • rotate(deg):用于旋转绘图环境
  • scale(scaleX, scaleY):用于缩放绘图环境
// 绘制一个矩形
context.fillStyle = "blue";
context.fillRect(50, 400, 100, 50);

// 平移、旋转、缩放
context.translate(100, 425); // 平移原点到矩形中心
context.rotate(Math.PI / 4); // 旋转 45 度
context.scale(2, 1); // 沿 x 轴缩放两倍,沿 y 轴不变

// 绘制一个变换后的矩形
context.fillStyle = "red";
context.fillRect(-50, -25, 100, 50);

颜色和样式

  • fillStyle:用于设置绘制形状时的填充颜色
  • strokeStyle:用于设置绘制形状的轮廓颜色
  • lineWidth:用于设置绘制形状的线条宽度

fillStyle、strokeStyle

fillStylestrokeStyle是用于设置形状的填充和轮廓颜色的,这里需要说明一下的是:fillRect()strokeRect()fillText()strokeText()这 4 个 API 关于设置颜色的这 2 个属性的使用

  • fillRect方法用于绘制填充的矩形
  • strokeRect方法用于绘制矩形的轮廓
  • fillText方法用于绘制填充的文本
  • strokeText方法用于绘制文本的轮廓

绘制的填充形状,只能使用fillStyle,因为它会影响填充颜色;而对于绘制的轮廓形状,也只能使用strokeStyle,因为它会影响轮廓颜色;而对于arcpath绘制的形状,fillStylestrokeStyle就都适用,但是也需要结合fill()stroke()来实现图形绘制

  • fill()绘制填充图形,可以通过fillStyle改变填充色
  • stroke()绘制图形描边,可以通过strokeStyle改变描边色

动画

API:requestAnimationFrame(callback)

  • requestAnimationFrame(callback):用于在下一次浏览器重绘前执行指定函数
    • callback:要在每一帧中调用的函数

requestAnimationFrame返回一个整数值,通常存储在变量animationId中。这个值可以用于取消动画帧请求,使用cancelAnimationFrame(animationId)

let x = 8;
let color = 0;

function draw() {
    // 清除画布
    context.clearRect(8, 550, canvas.width, canvas.height);

    // 绘制矩形
    context.fillStyle = `hsl(${color}, 100%, 50%)`;
    context.fillRect(x, 550, 100, 50);

    // 更新位置
    x += 2;

    // 更新颜色
    color = (color + 1) % 360;

    // 使用 requestAnimationFrame 递归调用 draw 函数
    requestAnimationFrame(draw);
}
draw()

清除和保存状态

API:clearRect(x, y, width, height)、save()、restore()

  • clearRect(x, y, width, height):清除矩形区域,如:clearRect(0, 0, canvas.width, canvas.height) 清除整个画布
  • save():保存当前状态
  • restore():恢复之前保存的状态

最终效果图

本部分是汇总了之前的代码示例后的最终效果图

小结

本篇文章是对 Canvas API 的综合概述,主要是了解 Canvas 提供了哪些功能、可以绘制什么形状以及如何去绘制这些形状,然后在下一篇文章Canvas 新手进阶篇将会通过一个实例来进一步使用和理解 Canvas

  • 11.16
    • Canvas 新手进阶篇 敬请期待