一切福田,不離方寸,從心而覓,感無不通。

canvas详细入门教程(1W字 吐血分享)

大家好,我是潘潘

今天为大家带来的是我已经写了很久了的canvas详细教程,对入门canvas很有帮助。

点击跳转原文:
canvas详细教程原文

canvas是什么?

沙拉查词

简单来说,<canvas> 是HTML5中的标签,它是一个容器,可以使用JS在其中绘制图形或文字。

MDN:<canvas>是一个可以使用脚本 (通常为JavaScript) 来绘制图形的 HTML 元素。例如,它可以用于绘制图表、制作图片构图或者制作简单的动画。主要聚焦于2D图形。

预备知识

canvas标签本身

canvas标签是一张画布,如果你写了一个canvas标签,打开live server,在支持canvas的浏览器上显示是这样的:

你可能会问怎么啥都没有呢?因为canvas标签只是一个默认长300像素,宽150像素的白色画布,你可以给它加上页面居中和box-shadow的css样式:

如果你觉得这个画布太小了,施展不开,那么你可以通过标签属性来自定义宽高:

 

设置后:

这里注意,设置画布的宽高只能在html标签里通过height和width属性来设置(canvas标签有且只有这两个属性),如果你在css中设置:

 

上边的这种方式只是将画布拉伸变形了,就好像拿放大镜看一样,会导致失真,其实它本质上的大小并没有变化,比如:

所以canvas的宽高还是要在html中使用标签属性设置。

支持的浏览器

IE已经有自己的墓碑了。。

那么如果你的浏览器不支持canvas呢?那么你canvas标签中的文字就派上用场了,浏览器上会显示出你canvas标签里的文字,比如:

 

(为了演示方便,以下教程全部默认浏览器支持canvas,且给画布加上了居中和阴影效果。)

详细教程

基本步骤

我们以现实生活中画水墨画为例:如果我们要画一幅画,要有以下的准备步骤:

①首先要有一用来画画的纸;

②找到这张纸;

③决定是画二维还是三维的画

类比于canvas,也是这样的几个步骤:

①首先要有一用来画画的纸:

 

②找到这张纸:

 

③决定是画二维还是三维的画:

 

坐标系

Canvas 使用的是 W3C 坐标系 ,也就是遵循我们屏幕、报纸的阅读习惯,从上往下,从左往右。

已经做好了准备工作,接下来就可以开始画画了!

一、绘制基础图形

1. 画线段

画线之前你需要知道canvas上下文的以下几个api:

moveTo(x,y):定义画线的起始点;

lineTo(x,y):定义画线的折点;

stroke():通过线条来绘制图形轮廓

完整代码:

 

当然,你也可以同时画多条折线:

 

给线段设置样式

strokeStyle = '颜色':设置线的颜色;

lineWidth = 数字:设置线的宽度;

lineCap = 'round/butt/square':设置线帽为圆型/默认/方形;

lineJoin = 'miter/round/bevel':设置线段连接处为默认/圆形/平直形式;

globalAlpha = 数字:设置图案的透明度

一个简单的示例,设置一条线的线宽:

 

显示:

image-20221006104445983

这里有一个地方需要注意,就是样式的设置必须写在绘制图形轮廓(stroke())方法之前!否则会不生效!(因为stroke()方法是进行绘制,如果已经绘制了再设置线段的样式,自然会不生效)

同时画多条线并分别设置样式

如果现在你画了两条宽20 像素圆角的线,并且想一条设置为红色,一条设置为天蓝色,那么你可能会这样写:

 

但是显示的会是这样:

两条线都显示的是红色,这是因为第二条线的颜色“覆盖”了第一条线的颜色。如果我们想分别设置每条线的样式,就需要用到下面两个方法:

beginPath():开启一条新路径,生成之后,图形绘制命令会被指向到新路径上;

closePath():关闭上一条路径的绘制

在每条路径开始和结束的时候加上这两个方法即可分别设置两条线的样式:

 

显示:

综合示例:

 

显示:

线宽、线帽、连接处、颜色、透明度

p.s.:其实在画第二条线的时候只需要开启(beginPath())新路径即可,两条线仍然可以分别设置样式,但是为了规范起见,还是建议写上closePath()

2. 画三角形

画三角形其实也是用画线的思路,只需要注意首尾点连接起来即可:

 

显示:

如果要设置样式也和画线是一样的:

 

显示:

可能有人会想把三角形左上角的闭合处显示的更衔接一点,那么我们可以使用closePath()方法,它会把线段的终点和起点连接起来,这样看上去就更更衔接了:

 

显示:

3. 画矩形

(1)空心矩形

绘制矩形有三种方法,第一种和画三角形的思路一样,比如:

 

显示:

第二种方法是直接使用ctx身上的strokeRect()方法:

例如:

 

显示:

第三种方法和第二种类似,只是把strokeRect()方法拆成了stroke()rect()方法,好处就是使用rect()暂时生成了矩形,但是必须调用stroke()方法才会绘制出来,比如可以使用下面的方式延迟两秒钟再绘制矩形:

 

两秒钟后会显示矩形:

(2)填充矩形

绘制填充矩形有两种方式。

第一种方式:只需要将strokeRect()方法和strokeStyle()方法的"stroke"改成"fill"即可。

fillStyle = '颜色':选择填充色

 

显示:

第二种方式:

这种方式和绘制空心矩形的第三种方法很像,只需要用fill()方法替代stroke()方法即可:

 

两秒钟后绘制填充矩形:

这里使用的fill()方法和stroke()方法都是用来绘制出来形状,只不过前者是填充绘制,后者是用线轮廓。后边我们也会经常使用发到fill()方法。

4. 画⌒/⚪

绘制圆弧通常有两种方式。

第一种方法:

使用arc(x, y, radius, startAngle, endAngle, anticlockwise)方法生成⚪/⌒,再使用stroke()方法绘制圆。

例如:

 

显示:

逆时针绘制一段圆弧:

 

显示:

圆弧也可以和点连接起来,比如:

 

显示:

进而,我们可以用这段代码画出一个“贪吃豆”:

 

显示:

在上面的例子中我们使用到了beginPath()closePath()方法来绘制多个形状。

绘制圆弧的第二种方法:

还可以用arcTo()方法来绘制圆弧,它接收四个参数(如下图),在它前边会有一个开始点坐标,一般由moveTo()lineTo()方法提供。

这个方法有点难以理解,解释一下:arcTo()方法就是利用“开始点”、“控制点”和“结束点”这三个点所形成的夹角,然后绘制一段与夹角的两边相切并且半径为radius的圆弧。其中,弧线的起点是“开始点所在边与圆的切点”,而弧线的终点是“结束点所在边与圆的切点”。arcTo()方法绘制的弧线是两个切点之间长度最短的那个圆弧。此外,如果开始点不是弧线起点,arcTo()方法还将添加一条当前端点到弧线起点的直线线段。也就是说,开始点坐标不一定是弧线起点坐标

例如我们绘制一个这样的弧线:

 

显示:

image-20221005181257611

解释:点A是给出的开始点,弧线是∠ABC与半径为100像素的圆相切的部分,如图:

蓝色线为辅助理解的线

5. 画椭圆

使用ellipse()方法来绘制椭圆

例如:

 

显示:

添加填充并设置颜色:

 

显示:

6. 画贝塞尔曲线

二次贝塞尔曲线:

使用quadraticCurveTo()方法来绘制二次贝塞尔曲线:

arcTo()方法一样,在它前边也会有一个开始点坐标,一般由moveTo()lineTo()方法提供,例如:

 

显示:

解释:二次贝塞尔曲线由起点、控制点和重点来控制:

在这推荐给大家一个在线调试二次贝塞尔曲线的小工具,大家可以在这里调节好之后,复制代码到IDE里即可:

链接:http://blogs.sitepointstatic.com/examples/tech/canvas-curves/quadratic-curve.html

image-20221005201800547

三次贝塞尔曲线

二次贝塞尔曲线由一个控制点控制,而三次贝塞尔曲线由两个控制点来控制。

使用bezierCurveTo()来绘制三次贝塞尔曲线:

例如:

 

显示:

解释:

同样,三次贝塞尔曲线也有调试工具,可以手动调节到目标曲线,再复制右侧代码即可:

链接:http://blogs.sitepointstatic.com/examples/tech/canvas-curves/bezier-curve.html

7. 画虚线

使用setLineDash([])方法来绘制虚线,setLineDash([])方法可以接收若干个参数,例如:

 

显示:

解读:setLineDash([])里的数组参数会“铺开”,下标为偶数的项为实线,为奇数项为透明线段,数字的大小代表着线段的长度

你也可以结合arc()、ellipse()、arcTo()...方法,画一个虚线的图形,比如:

 

显示:

getLineDash()方法可以获得当前虚线设置的样式,它的长度为非负偶数。例如:

 

打印出:

如果虚线设置的样式是基数,那么getLineDash()方法得到的是两组样式的集合。

二、绘制文本

下面讲解的是用canvas绘制文本的方法,其实如果学会了绘制图形,在学绘制文本就会感觉很简单了。

Hello Canvas!

使用strokeText()方法接收四个参数绘制描边文本

示例:

 

strokeText()方法类似,使用fillStroke()方法绘制填充文本:

image-20221008195728011

示例:

 

设置文本样式

.font方法可以设置文本大小和字体;

.textAlign:设置文本的对齐方式,可选left/right/center/start/end,对齐方式是以文本对strokeText()方法中坐标参数为参考的水平方向对齐方式。这段代码可以帮助你理解textAlign方法(仔细看代码和注释部分):

 

textBaseline:决定了文本在垂直方向的对齐方式,可选alphabetic/top/hanging/middle/ideographic/bottom。仔细看下面这段代码和注释部分:

 

.direction:设置文本的绘制方向,可选ltr(left to right)和rtl(right to left),比如:

 

文本样式综合示例:

 

获取文本宽度

可以使用measureText()方法测量文本的宽度:

例如:

 

注意:这里不是必须显示出文本来才能计算文本的长度,测量结果也不受文本的最大宽度等外界因素的影响,文本长度的测量结果和文本的font参数相关。

样式补充

渐变色

(1)线性渐变

使用createLinearGradient()来创建线性渐变色:

使用addColorStop()添加渐变色:

看例子:

 

显示:

如果我们把上边代码中的第一个偏移值改为0.5,那么显示如下:

(2)径向渐变

同线性渐变的唯一区别就是,径向渐变是通过createRadialGradient()方法来创建渐变色:

示例:

 

显示:

添加指定元素

使用createPattern()方法接收两个参数来添加指定元素:

pattern可以是图片,视频,canvas对象;type可以选择repeat/no-repeat/repeat-x(沿x轴平铺)/repeat-y(沿y轴平铺)

添加image

 

显示:

当然,你也可以使用这种方式在canvas画布中添加图片:

 

这里设置为repeat,显示:

添加canvas

和添加image类似:

 

显示:

阴影样式

文本和图形都可以设置阴影样式,且方法一样:

shadowOffsetX = 数字:设置阴影在X轴上的延申距离,正值表示阴影向x轴正方向延申,负值表示阴影向x轴负方向延申;

shadowOffsetY = 数字:设置阴影在Y轴上的延申距离,正值表示阴影向y轴正方向延申,负值表示阴影向y轴负方向延申;

shadowBlur = 数字:设定阴影的模糊度,默认为0;

shadowColor = '颜色':设置阴影的颜色,默认是全透明色;

综合示例:

 

三、绘制图片

使用drawImage()方法绘制图形,该方法可以接收3-9个参数进行拉伸、裁剪等。比如基础的绘制图片接收3个参数即可:

 

如果要拉伸图片到指定宽和高,要在drawImage()方法上多加两个参数:

 

drawImage()方法再加4个参数可以绘制拉伸和裁剪之后的图片:

 

以上绘制的图片都是用js生成的image对象,我们也可以使用现有的DOM对象来绘制,比如:

 

绘制的效果和之前一样:

合成、保存和还原绘画状态、变形、裁剪

合成

当我们在绘制canvas画布的时候,不可避免地要考虑到绘制的顺序,如果我们希望一个图形一直置顶显示,那么就可以使用globalCompositeOperation = type这个方法,根据type的值,这个方法有以下几种作用:

source-over

默认值,在已有图像之上绘制新图像。

 

source-in

在已有图像中显示新绘制的图像。只有已有图像之内的新图像部分才会显示,已有图像是透明的。可以简单理解为,只会展示新图像与已有图像重叠的部分。

示例:

 

蓝色部分为为了便于大家理解我添加的辅助线,不属于图像部分:

source-out

在已有图像之外显示新图像,只有已有图像之外的新图像部分会显示,已有图像是透明的。

 

蓝色虚线为辅助线,不属于图像:

这里解释以下为什么图像呈现出来是这样的:我们在画天蓝色圆的时候,globaoCompositeOperation的参数是source-over,所以会全部画出来,但是又因为下一个绘制浅黄色圆的参数是source-out,所以浅黄色圆只会显示和浅蓝色圆重叠之外的部分,且浅蓝色圆会变透明,继续画第三个粉色圆,粉色圆只会与画出已有浅黄色圆重叠之外的部分,故得到如图所示结果。

source-atop

在已有图像顶部显示新绘制的图像。已有图像位于新绘制图像之外的部分是不可见的。

 

蓝色虚线为辅助线:

destination-over

source-over相反,在已有图像之后绘制新图像。

 

destination-in

source-in类似,但是显示的是最开始的已有图像

 

蓝色虚线为辅助线:

destination-out

在新绘制的图像之外显示已有图像。只有新图像之外的已有图像部分会被显示,新绘制的图像是透明的。

 

蓝色虚线为辅助线:

destination-atop

在新绘制图像顶部显示已有图像。已有图像位于新绘制图像之外的部分是不可见的。

 

蓝色虚线为辅助线:

lighter

折叠图像的颜色是有颜色值相加得来的

 

copy

显示新绘制的图像。忽略已有的图像。

 

蓝色虚线为辅助线:

xor

两个图像重叠之后,二者都变为透明的

 

解释一下:中间显示粉色是因为蓝色圆与黄色圆重叠之后为透明的了,所以中间部分粉色圆没有和其他图形重叠了。

保存和还原绘画状态

通过save()方法可以保存当前绘画的状态,并通过restore()方法还原之前保存的状态。保存和还原可以多次调用。

示例:

在之前讲过,如果我们这样画出来两个矩形,那么这两个矩形的颜色都将是天蓝色,透明度50%,

 

而如果在设置storeStyle = 'skyblue'之前保存状态,并在绘制第二个矩形之前还原状态,第二个矩形绘制的就会是黑色:

 

Canvas的状态是存储在栈中的,每次调用save()方法后,当前的状态都会被推送到栈中保存起来。绘画状态包含: strokeStyle fillStyle globalAlpha lineWidth lineCap lineJoin miterLimit lineDashOffset shadowOffsetX shadowOffsetY shadowBlur shadowColor globalCompositeOperation font textAlign textBaseline direction imageSmoothingEnabled

变形

我们现在有一个基础图形,要在其基础上进行移动原点、旋转、缩放的效果:

 

移动原点位置

使用translate(x, y)方法接收两个参数向x轴和y轴正方向分别移动x、y像素:

 

蓝色矩形的绘制坐标还是(0,0)点,但是在此之前移动了原点位置,所以视觉上矩形的位置是在(100,150)处绘制的。

旋转

使用rotate(angle)方法以此时的画布原点为中心,顺时针旋转angle度。继续以上边的矩形为例,以(0,0)点为中心旋转45°:

 

缩放

使用scale(x, y)方法可以缩放图形。

在基础矩形的上做缩放:

 

如果想要同时绘制这三种效果到一张画布上,那么就需要用到save()restore()方法:

 

说明:save()保存的状态是可以多次保存的,同时保存在栈中的元素遵循的是后进先出的顺序,验证:

 

在上边的代码中,我在两个save()中间加了一个修改填充颜色为pink的代码,观察下图中更改颜色的矩形方块是哪几个:

可以看到,更改颜色的是旋转和移动原点坐标的两个矩形,而缩放的矩形颜色并未修改,所以第二个save()保存的状态应用到了第一个restore()上,故验证了save()在栈中保存的元素遵循的是后进先出的顺序。

裁剪

使用clip()方法从原始画布中剪切任意形状和尺寸。注意:一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内(不能访问画布上的其他区域),你也可以在使用 clip() 方法前通过使用 save() 方法对当前画布区域进行保存,并在以后的任意时间对其进行恢复(通过 restore() 方法)

 

蓝色虚线为辅助线,不属于图像:

清除

使用clearRect()方法清空给定矩形内的指定像素:

例如:

 

动画

动画的绘制其实就是在上文基础图形绘制和对画布状态的保存与恢复,再加上一些js内置的方法(比如setInterval()setTimeout()requestAnimationFrame()…)的基础上,不断地进行画布的绘制和清除来实现的。因为本文的篇幅已经够长了(我是使用Markdown文档编辑的,到现在已经有了9700词)所以我打算下一期再介绍用canvas绘制动画。

在下一期我会绘制几个动画,并且详细介绍实现原理,敬请期待!

.

以上为潘潘自己总结的canvas入门教程的全部内容。欢迎转载~ 转载请注明出处~

from:https://blog.csdn.net/nvgis/article/details/127316552