CanvasAPI

HTML5中的CanvasAPI提供了使用JavaScript和<canvas>标签自由绘制2D图形的接口。CanvasAPI在网页特效、数据可视化、游戏等领域都有广泛应用。这里值得一提的是Canvas的API都是围绕2D绘图设计的,因此并不适合显示3D内容(尽管也可以实现,但并不方便且性能低下),如果需要显示3D内容可以选择WebGL等方式。这篇笔记我们简单学习CanvasAPI的使用。

Canvas基础使用例子

下面例子中,我们使用CanvasAPI绘制了两个不同颜色叠在一起的矩形。

<canvas id="canvas" width="640" height="480"></canvas>

HTML中我们使用<canvas>标签创建了绘制区域,这里注意Canvas的大小需要在HTML标签上用widthheight指定,在<canvas>标签内部绘制时应该完全遵循这两个参数指定的画布大小。使用CSS指定的Canvas大小和绘制是完全无关的,CSS对Canvas产生的缩放和拉伸效果与绘制无关。

window.addEventListener('load', () => {
    const canvas = document.querySelector('#canvas');
    const ctx = canvas.getContext('2d');

    ctx.fillStyle = 'rgba(255,0,0,0.7)';
    ctx.fillRect(20, 20, 50, 50);
    ctx.fillStyle = 'rgba(0,255,0,0.7)';
    ctx.fillRect(30, 30, 50, 50);
});

JavaScript代码中,我们首先获取了Canvas的DOM节点,然后使用canvas.getContext('2d')获取绘制上下文对象,最后我们使用该对象绘制图像,ctx.fillRect(x, y, width, height)函数用于在指定位置绘制指定大小的矩形。

这段程序的运行结果如下:

Canvas图像绘制

Canvas绘制图像有两个基本概念我们需要知道:

  1. Canvas区域我们可以将其理解为一个画布,其原点是左上角
  2. 图形绘制其实只有两种:矩形和路径,所有其它图形都是通过组合这两种基本图形实现的

基于这两个基本概念,我们大概已经可以想象出如何用Canvas提供的API绘制图形了。

基本图形绘制

绘制矩形

CanvasAPI提供了3中方法绘制矩形:

fillRect(x, y, width, height)
strokeRect(x, y, width, height)
clearRect(x, y, width, height)

这3个方法分别用于绘制填充矩形、绘制矩形边框、清除指定矩形区域(让清除的部分完全透明)。它们的参数都是一样的,分别是绘制的位置坐标(矩形左上角)和矩形大小。实际上,前面例子我们已经演示如何绘制一个矩形了。

绘制路径

Canvas中路径是线段或曲线相连形成的点的集合,它能够封闭构成一个自定义图形或是描边产生一些线段和弧线,路径的使用很像PhotoShop中的钢笔工具。绘制路径的步骤如下:

  1. 创建路径起始点
  2. 绘制路径
  3. 使用填充绘制则需要将路径封闭
  4. 使用描边或填充渲染图形

路径操作一般都会用到如下几个函数:

beginPath()
stroke()
fill()

它们分别用于创建路径、路径描边、路径填充。下面是例子中,我们使用上面提到的这些函数绘制了一个闭合三角形。

window.addEventListener('load', () => {
    const canvas = document.querySelector('#canvas');
    const ctx = canvas.getContext('2d');

    ctx.beginPath();
    ctx.moveTo(75, 50);
    ctx.lineTo(100, 75);
    ctx.lineTo(100, 25);
    ctx.fill();
});

上面代码用到了moveTo()lineTo()函数,它们用于在路径绘制中移动画笔位置和绘制线段,我们通过这些函数和路径操作最终绘制了一个闭合三角形。运行上述代码结果如下图:

这里注意一个小细节,路径操作其实还有个closePath()函数,它用于让路径闭合(即连接一条最后位置到路径起始点的线段让图形闭合),不过实际上fill()会自动让图形闭合,因此我们一般都省略不写,而stroke()不会。下面例子我们使用了stroke()进行路径描边。

window.addEventListener('load', () => {
    const canvas = document.querySelector('#canvas');
    const ctx = canvas.getContext('2d');

    ctx.beginPath();
    ctx.moveTo(75, 50);
    ctx.lineTo(100, 75);
    ctx.lineTo(100, 25);
    ctx.stroke();
});

除了直线,CanvasAPI也支持绘制弧线和贝塞尔曲线,这里就不多介绍了,具体可以参考文档。

颜色

Canvas中图形绘制的颜色由fillStylestrokeStyle属性控制,它们分别指定了填充颜色和描边颜色。颜色的值可以用如下几种方式来指定:

ctx.fillStyle = "orange";
ctx.fillStyle = "#FFA500";
ctx.fillStyle = "rgb(255,165,0)";
ctx.fillStyle = "rgba(255,165,0,1)";

下面例子代码中,我们将之前绘制的三角形填充为橙色:

window.addEventListener('load', () => {
    const canvas = document.querySelector('#canvas');
    const ctx = canvas.getContext('2d');

    ctx.fillStyle = 'orange';
    ctx.beginPath();
    ctx.moveTo(75, 50);
    ctx.lineTo(100, 75);
    ctx.lineTo(100, 25);
    ctx.fill();
});

文字绘制

Canvas中绘制文本有如下两种方式:

fillText(text, x, y [, maxWidth])
strokeText(text, x, y [, maxWidth])

它们分别绘制填充文本和文本边框,参数为文本内容、位置,以及可选的最大长度。

下面例子代码中,我们在Canvas中绘制了一段文本。

window.addEventListener('load', () => {
    const canvas = document.querySelector('#canvas');
    const ctx = canvas.getContext('2d');

    ctx.font = '48px serif';
    ctx.fillText('Hello, world!', 50, 50);
});

代码运行效果如下:

注意,文本和矩形不同,文本的位置点是文本的左下角,而矩形则是左上角。

图片绘制

Canvas中绘制图片比较简单,首先我们需要有一个图片对象,它可以来自Image对象或是<img>标签,然后我们调用ctx.drawImage()将其绘制到Canvas中即可。下面是一个例子:

window.addEventListener('load', () => {
    const canvas = document.querySelector('#canvas');
    const ctx = canvas.getContext('2d');

    const img = new Image();
    img.onload = () => {
        ctx.drawImage(img, 10, 10);
    };
    img.src='./1.jpg';
});

注意我们这里Image对象是从URL地址加载的,因此它是异步的,我们的绘制逻辑需要编写在img.onload内部,避免图片还未加载就开始绘制。当然,这种写法比较简陋,在实际开发中资源的加载是比较重要的一个环节,通常需要根据实际需求来编写。

绘制上下文save/restore

在继续学习更加复杂的图形绘制方法之前,我们需要了解CanvasAPI中的两个必不可少的函数:

save()
restore()

这两个函数用于保存和恢复绘制上下文(context)的状态,它们可以处理的绘制属性包括前面我们用到过的fillStylestrokeStylefont等。它们的行为类似一个栈,保存一个状态后属性集就会入栈,恢复就会将属性集弹出。

Transformation 变形

这里所说的变形不是指变形动画,而是指一种绘制方式,变形包括移动、旋转、缩放。此外还要注意的是,这些变形操作的对象是Canvas绘制上下文而不是某个图像(这可能让这些API有些难用),它们会移动Canvas的原点、旋转Canvas的坐标系统,以及增减图形在Canvas中的像素数目。

translate(x, y)

x是左右偏移量,y是上下偏移量。

rotate(angle)

angle是旋转弧度。

scale(x, y)

xy分别是沿坐标轴的缩放比例,如果设置为1则不缩放,-1则是完全的镜像翻转。

下面例子中,我们使用移动变形绘制了两个矩形。

window.addEventListener('load', () => {
    const canvas = document.querySelector('#canvas');
    const ctx = canvas.getContext('2d');

    ctx.fillRect(50, 50, 30, 30);
    ctx.translate(50, 50);
    ctx.fillRect(50, 50, 30, 30);
});

绘制结果如图:

使用变形相关的API时,经常需要将绘制上下文恢复到初始状态,因此通常需要搭配save/restore来使用。

Canvas动画

前面我们绘制的都是静态图像,用Canvas渲染动画也并不复杂,我们将画布定时清空重绘,就可以实现动画了。JavaScript中和定时相关的函数有setInterval()setTimeout(),不过这里更推荐的是requestAnimationFrame()这个函数,它提供了更加平缓并更加有效率的方式来执行动画,专用于实现动画、游戏的帧循环,默认情况下每秒会执行60次(根据绘制函数的复杂程度和硬件性能帧数也可能降低)。

下面例子代码中,我们绘制了一个不停旋转的方块。

window.addEventListener('load', () => {
    const canvas = document.querySelector('#canvas');
    const ctx = canvas.getContext('2d');

    let currentAngle = 0;

    const update = () => {
        ctx.clearRect(0, 0, 640, 480);
        ctx.save();
        ctx.translate(50, 50);
        ctx.rotate(currentAngle);
        ctx.strokeRect(-15, -15, 30, 30);
        currentAngle += 3 * (Math.PI / 180);
        ctx.restore();
        window.requestAnimationFrame(update);
    };

    window.requestAnimationFrame(update);
});

Canvas像素数据操作

ImageData对象存储了Canvas中绘制内容的真实像素数据,我们可以直接操作这些像素来改变Canvas中的图像,也可以将Canvas输出为图片,以便于用户将其保存到磁盘上。

获取像素数据:

var myImageData = ctx.getImageData(left, top, width, height);

参数其实也是一个矩形,它指定了获取像素数据的范围。

作者:Gacfox
版权声明:本网站为非盈利性质,文章如非特殊说明均为原创,版权遵循知识共享协议CC BY-NC-ND 4.0进行授权,转载必须署名,禁止用于商业目的或演绎修改后转载。
Copyright © 2017-2024 Gacfox All Rights Reserved.
Build with NextJS | Sitemap