Maison interface Web tutoriel HTML 讲一讲 Canvas 究竟是个啥

讲一讲 Canvas 究竟是个啥

Oct 14, 2016 am 09:46 AM

HTML5 的标准已经出来好久了,但是似乎其中的 Canvas 现在并没有在太多的地方用到。一个很重要的原因是,Canvas 的标准还没有完全确定,不适合大规模用在生产环境。但是,Canvas 的优点也是很明显的,例如在绘制含有大量元素的图表的时候,SVG 往往因为性能问题而无法胜任,例如我见过的一次技术分享会的抽奖环节,虽然效果比较炫,但因为每个头像都是 DOM,利用 CSS3 控制的动画,导致了性能非常低下。此外,随着硬件性能的提高,视频截图、图像处理等功能也逐渐可以在网页上实现了,大多数网站用的是 Flash,但是 Flash 在 Mac 电脑上性能不高,还需要学一些额外的知识。Canvas 则是直接使用 JavaScript 来进行绘图,对 Mac 友好,所以不失为 Flash 的一个继承者。

使用 Canvas

说了这么多,Canvas 究竟是个啥?

英文中 Canvas 的意思是“画布”,不过这里说的 Canvas 是 HTML5 中新出的一个元素,开发者可以在上面绘制一系列图形。Canvas 在 HTML 文件中的写法很简单:


其中 id 属性是所有 HTML 元素都可以用的,Canvas 自带的属性只有后面两个(分别控制宽度、高度),没有其它的了。至于兼容性,CanIUse 上面写了,基础的功能目前用户使用的 90% 的浏览器都支持,所以大部分情况下还是可以放心使用的。

1.png

注意,一定要使用 Canvas 自带的 width 和 height 属性,不要使用 CSS 来控制,因为 CSS 控制会导致 Canvas 变形。可以试着与 PhptpShop 对比一下,后者是改变“图像大小”,前者才是正确的改变“画布大小”。例如下图是三张图片的横向拼接:最左边的黑框中是大小为 50px * 50px 的原图;中间是改变了图像大小为 100px * 100px 的效果,图像变得模糊,但是对于图像本身来说坐标范围并没有变大;最右边才是正确的 100px * 100px 的 Canvas。

1.png

Canvas 绝大部分的绘图方法都与  标签无关,需要使用 JavaScript 对其进行操作,这就是所谓的 Canvas API。

我们首先获取到这个元素:

var canvas = document.getElementById('canvas');
Copier après la connexion

然后通过一个方法来获取可以调用一切 Canvas API 的入口:

var ctx = canvas.getContext('2d');
Copier après la connexion

看到 2d 是不是很激动地联想到有没有 3d 呢?没有 3d 的写法,不过如果想要开启 3D 世界的大门,则可以写 canvas.getContext('webgl')。然而 WebGL 是基于 OpenGL ES 2.0 的一套标准,与本文是彻彻底底的两条路,因此这里就不讨论了。

Canvas 中的基本概念

坐标

与数学上常见的笛卡尔坐标系不太相同,Canvas 的坐标系是计算机中常见的坐标系,它长这样:

1.png

画布的最左上角是 (0,0),往右 x 增大,往下 y 增大,而且 x 和 y 都是整数(就算在计算过程中不是整数,在绘制的时候也会当作整数处理),单位是像素。

绘图

带大家怀旧一下。不知道有多少同学小时候玩过 logo 语言,在里面你可以控制一只小海龟在一块板子上行走、画画、提笔、落笔。Canvas 中也一样,你需要控制一只画笔的移动和绘制。然而 Canvas 更高级一些,你可以直接利用一些函数来画图,不用去控制那只画笔的位置。

Canvas 中的基本图形

通过上文定义的 ctx 变量可以干许多有意思的事情,我们先看看如何绘制一些基本图形。

线条

我们指定画笔移动到某一点,然后告诉画笔需要从当前这一点画到另一点。我们可以让画笔多次移动、绘制,最后统一输出到屏幕上。例子如下:

ctx.moveTo(10, 10);
ctx.lineTo(150, 50);
ctx.lineTo(10, 50);
ctx.moveTo(10, 20);
ctx.lineTo(40, 70);
ctx.stroke();
Copier après la connexion

上面的代码中,lineTo 是产生线条用的函数,执行完之后画笔就移到了线条的终点。需要注意的是,线条此时并没有显示在屏幕上,必须调用 stroke 才会显示。这样设计是有道理的,因为向屏幕上输出内容需要耗费大量的资源,我们完全可以先攒够一波 lineTo,最后用 stroke 放一个大的。

路径

绘制路径非常简单,只需要先告诉 ctx 一声“我要开始画路径了”,然后通过各种方法(例如 lineTo)绘制路径。如果需要画一个封闭路径,那就最后告诉 ctx一声:“我画完了,你把它封闭起来吧。”当然,不要忘记利用 stroke 输出到屏幕上。

一个简单的例子:

ctx.beginPath();
ctx.moveTo(10, 10);
ctx.lineTo(150, 50);
ctx.lineTo(10, 50);
ctx.closePath();
ctx.stroke();
Copier après la connexion

如果我不想只描绘路径线条,而是想填充整个路径呢?可以将最后一行的 stroke 改成 fill,这样就跟使用了画图中的油漆桶一样,封闭路径里面的内容就都被填充上颜色了:

ctx.fill();
Copier après la connexion

弧 / 圆形

绘制弧的函数参数比较多:

ctx.arc(圆心 x 坐标, 圆心 y 坐标, 半径, 起始角度, 终止角度, 是否为逆时针);
Copier après la connexion

注意,在 Canvas 的坐标系中,角的一边是以圆心为中心的水平向右的直线。角度单位均为弧度。例如下图,确定了圆心、起始角度(图中标明的锐角)和终止角度(图中标明的钝角),方向为逆时针,于是就有了这么一个弧。如果方向为顺时针,那么就会是一个跟它互补的、非常非常大的弧……

1.png

所以如果转了 2π 圈之后,弧就成了圆形,因此也可以使用绘制弧的方式来绘制圆形:

ctx.beginPath();
ctx.arc(圆心 x 坐标, 圆心 y 坐标, 半径, 0, Math.PI * 2, true);
ctx.closePath();
Copier après la connexion

最后一个参数随便填(当然也可以不填),因为不管是顺时针还是逆时针,转了 2π 圈之后都是一个圆。

矩形

如果只是想绘制一个横平竖直的矩形,可以使用下面的两个方法:

// 只描边
ctx.strokeRect(左上角 x 坐标, 左上角 y 坐标, 宽度, 高度);
// 只填充
ctx.fillRect(左上角 x 坐标, 左上角 y 坐标, 宽度, 高度);
Copier après la connexion

线条样式 / 填充样式

之前绘制的所有图形都是黑色的,但是 Canvas 肯定不止这么一种颜色(不然标准的制定者会被喷的很惨)。事实上,Canvas 可以单独设置线条样式和填充样式,分别使用的是 strokeStyle 和 fillStyle。可能的值有三种:纯色、渐变、图像。既然线条样式与填充样式的使用方法相同,那么下面统一以填充样式为例。如果想设置线条样式,直接将所有的 fillStyle 改成 strokeStyle 即可,里面的参数都不变。

/* 纯色填充 */
// 普通的颜色
ctx.fillStyle = '#0000ff';
// 带有透明度的颜色
ctx.fillStyle = 'rgba(64, 0, 127, 0.5)';

/* 渐变填充 */
// 设置渐变的尺寸(参数分别为起始点的 x 和 y、终止点的 x 和 y)
var gradient = ctx.createLinearGradient(0, 0, 170, 0);
// 设置过渡色,第一个参数是渐变的位置,第二个参数是颜色
gradient.addColorStop(0, 'magenta');
gradient.addColorStop(0.5, 'blue');
gradient.addColorStop(1.0, 'red');
// 设置填充样式
ctx.fillStyle = gradient;

/* 图片填充 */
// 创建图片
var image = new Image;
image.src = '/path/to/image.png';
// 创建图片笔触,可以指定图片的平铺方式,这里是横向平铺
var pattern = ctx.createPattern(image, 'repeat-x');
// 设置笔触填充
ctx.fillStyle = pattern;
Copier après la connexion

关于渐变,除了代码中提到的线性渐变以外,还有 createRadialGradient,也就是径向渐变。

设置完填充样式之后,就可以使用 fill 来填充啦!如果设置的是线条样式,那么就可以使用 stroke 来描边。

当然,对于线条样式,还有个额外的方法叫 lineWidth 可以用来控制线条的宽度。

文字

要想在画布上画文字,首先需要知道所使用的字体和字号:

ctx.font = '30px Verdana';
Copier après la connexion

然后就可以通过 strokeText 或者 fillText 来对字体描边或者填充字体。

ctx.strokeText("Hello Coding!", 23, 33);
ctx.fillText("Hello Coding!", 23, 66);
Copier après la connexion

图片

在 Canvas 中绘制图片有三种方法:

// 指定绘制位置
ctx.drawImage(image, x, y);
// 指定绘制位置和图像宽高
ctx.drawImage(image, x, y, width, height);
// 指定剪裁区域、绘制位置和图像宽高
ctx.drawImage(image, sx, sy, swidth, sheight, x, y, width, height);
Copier après la connexion

参数的含义依次如下:

image:   要使用的 Image、Canvas 或 Video
sx:      可选,开始剪切的 x 坐标
sy:      可选,开始剪切的 y 坐标
swidth:  可选,被剪切图像的宽度
sheight: 可选,被剪切图像的高度
x:       在画布上放置图像的 x 坐标
y:       在画布上放置图像的 y 坐标
width:   可选,要使用的图像的宽度
height:  可选,要使用的图像的高度
Copier après la connexion

画布设置

细心的同学可能会发现,刚才有些属性是直接对 ctx 变量做设置,例如 ctx.lineWidth,只要设置了它,那么后续画出来的线条全都是这么个宽度。

其实,Canvas 的设置项还有许多,例如我们可以直接移动画布、旋转画布、设置全局的绘制透明度等等。这些设置还可以随时保存和恢复。

要注意的一点是,所有已经画在画布上的东西,是已经定死了的,不管之后再次进行任何设置都不会再改变。这个很像 Windows 下的画图程序。

废话不多说,直接上代码:

// 移动画布,其实就是移动坐标系
ctx.translate(往右移动的量, 往下移动的量);
// 旋转画布,旋转中心为坐标系原点
ctx.rotate(顺时针旋转的角度);
// 以坐标系原点为中心缩放画布
ctx.scale(横向放大倍数, 纵向放大倍数);
// 设置绘制透明度,如果 fillStyle 等属性设置了透明度则会叠加
ctx.globalAlpha(零到一的小数);
// 设置全局组合操作
ctx.globalCompositeOperation = 'lighter';
// 保存当前设置
ctx.save();
// 恢复上次保存的设置
ctx.restore();
Copier après la connexion

移动、旋转、缩放其实就是在控制绘图的坐标系,如果你在调用这三个方法的时候,脑子里时刻有一个带刻度的坐标系,效果会非常好。

事实上,Canvas 的坐标变换遵循计算机图形学的知识:变换矩阵。简单来说,一个坐标可以看成是一个矩阵,坐标所对应的矩阵乘上变换矩阵就可以实现对坐标的变换。为了提升计算的效率,可以先计算出几种变换复合之后的变换矩阵,然后直接通过 transform 函数对当前坐标系进行变换,或者通过 setTransform 函数将坐标系重置为初始状态后再进行变换。至于变换矩阵的内容,对于本文来说就有些超纲了。

全局组合操作有点像 PhotoShop 里面的“混合选项”,具体的实现方式还没有完全确定,目前常见浏览器都统一了的实现方式有:source-over、source-atop、destination-over、destination-out、lighter、xor。具体的行为可以看 Mozilla 官方文档,但是由于标准还未完全确定,因此其它浏览器不保证所有的行为都跟 Mozilla 的标准一致。一般来说,比较常见的是 source-over 和 lighter 两种,这两种的标准在浏览器界也算是无可争议的。

至于保存和恢复设置就有点好玩了,首先需要了解一个叫“栈”的东西。

栈是一个一维数组,规定只能从一个方向操作。栈一开始是空的,我们可以从这个方向往数组 push 元素,也只能从这个方向把最后一个元素(栈顶元素)pop 出来,除此以外没有任何多余的操作。当然,pop 的次数不能多于 push 的次数,因为 pop 到栈底的时候栈里就已经没有元素了,此时再 pop 是没有意义的。栈的用处有很多,例如括号匹配、表达式求值、深度优先搜索,甚至绝大部分语言的函数调用都要用到栈。

每次我们调用 save 函数,实际上是将当前的全局设置 push 到了一个专门栈上,每次调用 restore 函数的时候将最后一次保存的内容 pop 出来并用它覆盖当前的全局设置,这样栈顶就是最近一次保存的内容了。保存和恢复在某些情况下很好用,例如我需要画一个歪着的图形,然后继续画正着的图形,这样就可以先调用 save,然后调用 rotate,画完图形之后再restore 回来,继续画其它的图形。

其实 Canvas 还有许多方法,例如 toDataURL 直接将当前画布上的内容转换为十六进制的 data-url,getImageData 直接将图像转换为 RGBA 数组以供图像处理算法使用,putImageData 将 RGBA 数组转换为图片显示在画布上等等。如果配上 JavaScript 的定时更新(最好用 requestAnimationFrame 而不是 setInterval),则可以产生动画效果。网上还有许多 Canvas 的库,可以让程序员更简便地基于 Canvas 编写属于自己的特效或功能。在这儿我想说一句话:大家的脑洞有多大,Canvas 的能力就有多强~


Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn

Outils d'IA chauds

Undresser.AI Undress

Undresser.AI Undress

Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover

AI Clothes Remover

Outil d'IA en ligne pour supprimer les vêtements des photos.

Undress AI Tool

Undress AI Tool

Images de déshabillage gratuites

Clothoff.io

Clothoff.io

Dissolvant de vêtements AI

AI Hentai Generator

AI Hentai Generator

Générez AI Hentai gratuitement.

Article chaud

R.E.P.O. Crystals d'énergie expliqués et ce qu'ils font (cristal jaune)
2 Il y a quelques semaines By 尊渡假赌尊渡假赌尊渡假赌
Repo: Comment relancer ses coéquipiers
4 Il y a quelques semaines By 尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island Adventure: Comment obtenir des graines géantes
4 Il y a quelques semaines By 尊渡假赌尊渡假赌尊渡假赌
Combien de temps faut-il pour battre Split Fiction?
3 Il y a quelques semaines By DDD

Outils chauds

Bloc-notes++7.3.1

Bloc-notes++7.3.1

Éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise

SublimeText3 version chinoise

Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1

Envoyer Studio 13.0.1

Puissant environnement de développement intégré PHP

Dreamweaver CS6

Dreamweaver CS6

Outils de développement Web visuel

SublimeText3 version Mac

SublimeText3 version Mac

Logiciel d'édition de code au niveau de Dieu (SublimeText3)

Difficulté à mettre à jour la mise en cache des pages Web officielles du compte: comment éviter l'ancien cache affectant l'expérience utilisateur après la mise à jour de la version? Difficulté à mettre à jour la mise en cache des pages Web officielles du compte: comment éviter l'ancien cache affectant l'expérience utilisateur après la mise à jour de la version? Mar 04, 2025 pm 12:32 PM

Le cache de mise à jour de la page Web du compte officiel, cette chose est simple et simple, et elle est suffisamment compliquée pour en boire un pot. Vous avez travaillé dur pour mettre à jour l'article officiel du compte, mais l'utilisateur a toujours ouvert l'ancienne version. Dans cet article, jetons un coup d'œil aux rebondissements derrière cela et comment résoudre ce problème gracieusement. Après l'avoir lu, vous pouvez facilement faire face à divers problèmes de mise en cache, permettant à vos utilisateurs de toujours ressentir le contenu le plus frais. Parlons d'abord des bases. Pour le dire franchement, afin d'améliorer la vitesse d'accès, le navigateur ou le serveur stocke des ressources statiques (telles que des images, CSS, JS) ou du contenu de la page. La prochaine fois que vous y accédez, vous pouvez le récupérer directement à partir du cache sans avoir à le télécharger à nouveau, et il est naturellement rapide. Mais cette chose est aussi une épée à double tranchant. La nouvelle version est en ligne,

Comment utiliser les attributs de validation du formulaire HTML5 pour valider l'entrée utilisateur? Comment utiliser les attributs de validation du formulaire HTML5 pour valider l'entrée utilisateur? Mar 17, 2025 pm 12:27 PM

L'article discute de l'utilisation des attributs de validation de formulaire HTML5 comme les limites requises, motifs, min, max et longueurs pour valider la saisie de l'utilisateur directement dans le navigateur.

Quelles sont les meilleures pratiques pour la compatibilité entre les navigateurs dans HTML5? Quelles sont les meilleures pratiques pour la compatibilité entre les navigateurs dans HTML5? Mar 17, 2025 pm 12:20 PM

L'article examine les meilleures pratiques pour assurer la compatibilité des navigateurs de HTML5, en se concentrant sur la détection des fonctionnalités, l'amélioration progressive et les méthodes de test.

Comment ajouter efficacement les effets de course aux images PNG sur les pages Web? Comment ajouter efficacement les effets de course aux images PNG sur les pages Web? Mar 04, 2025 pm 02:39 PM

Cet article démontre un ajout de bordure PNG efficace aux pages Web à l'aide de CSS. Il soutient que CSS offre des performances supérieures par rapport à JavaScript ou à des bibliothèques, détaillant comment ajuster la largeur, le style et la couleur des bordures pour un effet subtil ou proéminent

Quel est le but du & lt; datalist & gt; élément? Quel est le but du & lt; datalist & gt; élément? Mar 21, 2025 pm 12:33 PM

L'article traite du HTML & lt; Datalist & GT; élément, qui améliore les formulaires en fournissant des suggestions de saisie semi-automatique, en améliorant l'expérience utilisateur et en réduisant les erreurs. COMMANDE COMPRES: 159

Quel est le but du & lt; Progress & gt; élément? Quel est le but du & lt; Progress & gt; élément? Mar 21, 2025 pm 12:34 PM

L'article traite du HTML & lt; Progress & GT; élément, son but, son style et ses différences par rapport au & lt; mètre & gt; élément. L'objectif principal est de l'utiliser & lt; Progress & gt; pour l'achèvement des tâches et & lt; mètre & gt; pour stati

Comment utiliser le html5 & lt; time & gt; élément pour représenter les dates et les temps sémantiquement? Comment utiliser le html5 & lt; time & gt; élément pour représenter les dates et les temps sémantiquement? Mar 12, 2025 pm 04:05 PM

Cet article explique le html5 & lt; time & gt; élément de représentation sémantique de date / heure. Il souligne l'importance de l'attribut DateTime pour la lisibilité à la machine (format ISO 8601) à côté du texte lisible par l'homme, stimulant AccessIbilit

Quel est le but du & lt; mètre & gt; élément? Quel est le but du & lt; mètre & gt; élément? Mar 21, 2025 pm 12:35 PM

L'article traite du HTML & lt; mètre & gt; élément, utilisé pour afficher des valeurs scalaires ou fractionnaires dans une plage, et ses applications courantes dans le développement Web. Il différencie & lt; mètre & gt; De & lt; Progress & gt; et ex

See all articles