Canvas 101

2023年09月06上次更新于 3 个月前
编程

Canvas

通过HTMLcanvas元素和JavaScript,我们可以在浏览器环境下绘制各种图形、创建动画、数据可视化,照片处理和实时视频处理等功能。

Basic Example

HTML canvas element exmaple:

<canvas id="canvas"></canvas>

JavaScript:

const canvas = document.querySelector('#canvas') // 获取元素
const ctx = canvas.getContext('2d') // 获取 2D 上下文

Canvas.getContext(): 返回一个canvas 元素上的绘制上下文对象(如果不支持此功能则返回null)

HTMLCanvasElement: getContext() method - Web APIs | MDN

不同的获取context的参数,决定了绘图上下文的能力来源。我们通常会使用浏览器的2d上下文,亦或是选择基于webgl的增强上下文,具体选择什么取决于你的需求。

不同的基础能力也有不同的调优配置,具体可以参考文档。

Basic usage of canvas

默认的canvas元素高300px,宽150px,我们可以通过元素属性和CSS来修改其宽高样式,所有绘制的内容都会在这个元素内部渲染,并且这些内部渲染的图形不会受到Canvas上的CSS影响。

绘制

看图如下:

Canvas grid with a blue square demonstrating coordinates and axes.

Canvas的坐标系,其原点为canvas元素的左上角,接下来我们开始绘制基础图形(复杂的图形都可以拆解为基础图形,我们在开发的时候可以多多思考如何将复杂问题拆解为简单的子问题,逐一解决)。

Canvas仅支持两种原始图形:

  • Rectangles 矩形
  • Paths 路径(多点成线)

其他的图形都是通过组合以上的原始图形形成的。

绘制矩形

绘制矩形的核心API如下:

  • fillRect(x, y, width, height) 填充矩形
  • strokeRect(x, y, width, height) 绘制矩形的外部轮廓
  • clearRect(x, y, width, height) 清除矩形区域数据,将其完全变得透明。

看看这个例子:

function draw() {
  const canvas = document.getElementById("canvas");
  if (canvas.getContext) {
    const ctx = canvas.getContext("2d");

    ctx.fillRect(25, 25, 100, 100);
    ctx.clearRect(45, 45, 60, 60);
    ctx.strokeRect(50, 50, 50, 50);
  }
}
draw()

这个函数将会绘制如下所示的图形:

我们可以清楚看到矩形填充区、清理出来的透明矩形区和内部的轮廓边。

绘制路径

路径就是一系列的的组合,不同的路径连接组合起来就是不同的图形。

绘制路径的时候,我们会使用到如下绘制函数:

  • beginPath() 创建一个路径,创建路径之后就可以在上面绘制了
  • closePath() 关闭路径,添加一条笔直的线到路径的起点上
  • stroke() 绘制图形的边框,绘制线时,仅绘制线的边框,需要手动移动到起始点或者调用closePath()来收拢图形绘制区域
  • fill() 绘制图形并且填充其内部区域,会自动根据点位进行收拢,例如起始点+两次lineTo()即可在fill()的时候绘制其内部区域,形成一个三角形

此外,开始一个路径之后即可调用以下函数进行绘制(就像手持画笔那样):

  • moveTo(x, y) 移动画笔到目标坐标(x, y)
  • lineTo(x, y) 绘制直线到目标坐标(x, y)

其中,beginPath函数是创建一条路径的开始。下面是一个简单的绘制三角形的函数示例:

function draw() {
  const canvas = document.getElementById("canvas");
  if (canvas.getContext) {
    const ctx = canvas.getContext("2d");

    ctx.beginPath(); // 开始路径
    ctx.moveTo(75, 50); // 移动画笔到坐标 75, 50
    ctx.lineTo(100, 75); // 绘制直线到坐标 100, 75
    ctx.lineTo(100, 25); // 再次绘制
    ctx.fill(); // 填充画布区(自动围拢成的三角形)
  }
}

效果如下所示:

绘制直线和矩形相对来说还算简单,接下来我们简单看看如何绘制曲线(弧线)和圆形。

绘制弧线和圆

绘制弧线和圆的主要API如下所示:

  • arc(x, y, radius, startAngle, endAngle, counterckickwise) 默认为顺时针绘制,绘制之后画笔就会停留在最后的坐标上,因此经常需要配合moveTo方法移动画笔
  • arcTo(x1, y1, x2, y2, radius) 绘制弧线以连接两个切线。简单来说我们首先定义起点坐标和终点坐标,接着需要绘制两个点之间的一条弧线。

绘制弧线的APIarc(x, y, radius, startAngle, endAngle, counterclockwise):关键属性即:坐标(决定了圆心位置)、半径、起始角度、是否逆时针绘制。

通过上述几个关键点,我们就可以绘制出想要的弧线。接下来,我们来看这个绘制弧线和圆形组成的微笑画面:

这个图形是由外圆和双眼圆形、笑脸半圆弧线的嘴巴组成的。我们一步步来绘制:

function draw() {
  const canvas = document.getElementById("canvas");
  if (canvas.getContext) {
    // 针对高设备像素比的屏幕,修正宽高以免画面模糊
    const {width, height} = canvas.getBoundingClientRect();
    const radio = window.devicePixelRatio || 1;
    canvas.width = width * radio;
    canvas.height = height * radio;
    const ctx = canvas.getContext("2d");
    // 开始路径
    ctx.beginPath();
    // 绘制外圆,设定圆心坐标 (75, 75)
    // 设定半径、起始角度和逆时针绘制
    ctx.arc(75, 75, 50, 0, Math.PI * 2, true); // Outer circle
    // 绘制结束之后,画笔会停在 (125, 75)
    ctx.moveTo(110, 75); // 将画笔移动到左边 10 像素的位置,继续准备绘制嘴巴
    ctx.arc(75, 75, 35, 0, Math.PI, false); // Mouth (clockwise) // 圆心保持一致,修改半径和起始角度,顺时针绘制好嘴巴
    ctx.moveTo(65, 65); // 移动到左边眼睛去绘制眼睛
    ctx.arc(60, 65, 5, 0, Math.PI * 2, true); // Left eye
    ctx.moveTo(95, 65); // 移动到右边眼睛坐标去绘制
    ctx.arc(90, 65, 5, 0, Math.PI * 2, true); // Right eye
    ctx.stroke();
  }
}
draw()

在绘制的时候想象成自己正在拿着画笔,逐一确定起始点的位置。

最后我们只需要绘制描边即可。

绘制圆形较为简单,下面通过CanvasRenderingContext2D: arcTo() method - Web APIs | MDN的例子,绘制弧线。

not-by-ainot-by-ai
文章推荐

Friends

Jimmy老胡SubmaraBruce SongScarsu