在前端领域,如果只是懂 Vue或者 React,未来在职场的竞争力可能会比较弱。
根据我多年在家待业经验来看,前端未来在 数据可视化和 AI这两个领域会比较香,而 Canvas是数据可视化在前端方面的基础技术。
本文就用光的速度将 canvas给入门了。
01.gif
要入门一个技术,前期最重要是快!所以本文只讲入门内容,能应付简单项目。深入的知识点会在其他文章讲解。
文章同时收录于小程序-
互联网小兵
,不止于前端,收各平台优质热门的技术文章(后端、移动端、算法、人工智能...),大家支持支持,点击前往体验!Canvas 是什么?Canvas中文名叫 “画布”,是 HTML5新增的一个标签。Canvas允许开发者通过 JS在这个标签上绘制各种图案。Canvas拥有多种绘制路径、矩形、圆形、字符以及图片的方法。Canvas在某些情况下可以 “代替” 图片。Canvas可用于动画、游戏、数据可视化、图片编辑器、实时视频处理等领域。Canvas 和 SVG 的区别
CanvasSVG
用JS动态生成元素(一个HTML元素)用XML描述元素(类似HTML元素那样,可用多个元素来描述一个图形)位图(受屏幕分辨率影响)矢量图(不受屏幕分辨率影响)不支持事件支持事件数据发生变化需要重绘不需要重绘就上面的描述而言可能有点难懂,你可以打开 AntV旗下的图形编辑引擎做对比。
G6[1]
是使用 canvas开发的,X6[2]
是使用 svg开发的。我的建议是:如果要展示的数据量比较大,比如一条数据就是一个元素节点,那使用 canvas会比较合适;如果用户操作的交互比较多,而且对清晰度有要求(矢量图),那么使用 svg会比较合适。
起步学习前端一定要动手敲代码,然后看效果展示。
起步阶段会用几句代码说明 canvas如何使用,本例会画一条直线。
画条直线在 HTML中创建 canvas元素通过 js获取 canvas标签从 canvas标签中获取到绘图工具通过绘图工具,在 canvas标签上绘制图形
02.png
<!-- 1、创建 canvas 元素 --><canvasid="c"width="300"height="200"></canvas><script>// 2、获取 canvas 对象constcnv = document.getElementById(c) // 3、获取 canvas 上下文环境对象constcxt = cnv.getContext(2d) // 4、绘制图形cxt.moveTo(100, 100) // 起点坐标 (x, y)cxt.lineTo(200, 100) // 终点坐标 (x, y)cxt.stroke() // 将起点和终点连接起来</script>复制代码
moveTo、 lineTo和 stroke方法暂时可以不用管,它们的作用是绘制图形,这些方法在后面会讲到~
注意点1、默认宽高
canvas有
默认的 宽度(300px) 和 高度(150px)
如果不在 canvas上设置宽高,那 canvas元素的默认宽度是300px,默认高度是150px。
2、设置 canvas 宽高canvas元素提供了 width和 height两个属性,可设置它的宽高。
需要注意的是,这两个属性只需传入数值,不需要传入单位(比如 px等)。
<canvaswidth="600"height="400"></canvas>复制代码3、不能通过 CSS 设置画布的宽高
使用 css设置 canvas的宽高,会出现
内容被拉伸
的后果!!!03.png
<style>#c{ width: 400px; height: 400px; border: 1pxsolid #ccc; } </style><canvasid="c"></canvas><script>// 1、获取canvas对象constcnv = document.getElementById(c) // 2、获取canvas上下文环境对象constcxt = cnv.getContext(2d) // 3、绘制图形cxt.moveTo(100, 100) // 起点cxt.lineTo(200, 100) // 终点cxt.stroke() // 将起点和终点连接起来console.log(cnv.width) // 获取 canvas 的宽度,输出:300console.log(cnv.height) // 获取 canvas 的高度,输出:150</script>复制代码
canvas的默认宽度是300px,默认高度是150px。
如果使用 css修改 canvas的宽高(比如本例变成 400px * 400px),那宽度就由 300px 拉伸到 400px,高度由 150px 拉伸到 400px。使用 js获取 canvas的宽高,此时返回的是 canvas的默认值。最后出现的效果如上图所示。
4、线条默认宽度和颜色线条的默认宽度是 1px,默认颜色是黑色。
但由于默认情况下 canvas会将线条的中心点和像素的底部对齐,所以会导致显示效果是 2px和非纯黑色问题。
5、IE兼容性高暂时只有 IE 9以上才支持 canvas。但好消息是 IE已经有自己的墓碑了。
如需兼容 IE 7 和 8,可以使用
ExplorerCanvas [3]
。但即使是使用了 ExplorerCanvas 仍然会有所限制,比如无法使用 fillText()方法等。基础图形坐标系
在绘制基础图形之前,需要先搞清除 Canvas使用的坐标系。
Canvas使用的是
W3C 坐标系
,也就是遵循我们屏幕、报纸的阅读习惯,从上往下,从左往右。04.jpg
W3C 坐标系
和数学直角坐标系
的 X轴是一样的,只是 Y轴的反向相反。W3C 坐标系
的 Y轴正方向向下。直线一条直线
最简单的起步方式是画一条直线。这里所说的 “直线” 是几何学里的 “线段” 的意思。
需要用到这3个方法:
moveTo(x1, y1):起点坐标 (x, y)lineTo(x2, y2):下一个点的坐标 (x, y)stroke():将所有坐标用一条线连起来起步阶段可以先这样理解。
05.png
<canvasid="c"></canvas><script>constcnv = document.getElementById(c) constcxt = cnv.getContext(2d) // 绘制直线cxt.moveTo(50, 100) // 起点坐标cxt.lineTo(200, 50) // 下一个点的坐标cxt.stroke() // 将上面的坐标用一条线连接起来</script>复制代码
上面的代码所呈现的效果,可以看下图解释(手不太聪明,画得不是很标准,希望能看懂)
06.jpg
多条直线如需画多条直线,可以用会上面那几个方法。
07.png
<canvasid="c"width="300"height="300"></canvas><script>constcnv = document.getElementById(c) constcxt = cnv.getContext(2d) cxt.moveTo(20, 100) cxt.lineTo(200, 100) cxt.stroke() cxt.moveTo(20, 120.5) cxt.lineTo(200, 120.5) cxt.stroke() </script>复制代码
仔细观察一下,为什么两条线的粗细不一样的?
明明使用的方法都是一样的,只是第二条直线的 Y轴的值是有小数点。
答:
默认情况下 canvas会将线条的中心点和像素的底部对齐,所以会导致显示效果是 2px和非纯黑色问题。
08.jpg
上图每个格子代表 1px。
线的中心点会和画布像素点的底部对齐
,所以会线中间是黑色的,但由于一个像素就不能再切割了,所以会有半个像素被染色,就变成了浅灰色。所以如果你设置的 Y轴值是一个整数,就会出现上面那种情况。
设置样式lineWidth:线的粗细strokeStyle:线的颜色lineCap:线帽:默认: butt; 圆形: round; 方形: square
09.png
<canvasid="c"></canvas><script>constcnv = document.getElementById(c) constcxt = cnv.getContext(2d) // 绘制直线cxt.moveTo(50, 50) cxt.lineTo(200, 50) // 修改直线的宽度cxt.lineWidth = 20// 修改直线的颜色cxt.strokeStyle = pink// 修改直线两端样式cxt.lineCap = round// 默认: butt; 圆形: round; 方形: squarecxt.stroke() </script>复制代码新开路径
开辟新路径的方法:
beginPath()在绘制多条线段的同时,还要设置线段样式,通常需要开辟新路径。
要不然样式之间会相互污染。
比如这样
10.png
<canvasid="c"width="300"height="300"></canvas><script>constcnv = document.getElementById(c) constcxt = cnv.getContext(2d) // 第一条线cxt.moveTo(20, 100) cxt.lineTo(200, 100) cxt.lineWidth = 10cxt.strokeStyle = pinkcxt.stroke() // 第二条线cxt.moveTo(20, 120.5) cxt.lineTo(200, 120.5) cxt.stroke() </script>复制代码
如果不想相互污染,需要做2件事:
使用 beginPath()方法,重新开一个路径设置新线段的样式(必须项)如果上面2步却了其中1步都会有影响。
只使用beginPath()11.png
<canvasid="c"width="300"height="300"></canvas><script>constcnv = document.getElementById(c) constcxt = cnv.getContext(2d) // 第一条线cxt.moveTo(20, 100) cxt.lineTo(200, 100) cxt.lineWidth = 10cxt.strokeStyle = pinkcxt.stroke() // 第二条线cxt.beginPath() // 重新开启一个路径cxt.moveTo(20, 120.5) cxt.lineTo(200, 120.5) cxt.stroke() </script>复制代码
第一条线的样式会影响之后的线。
但如果使用了 beginPath(),后面的线段不会影响前面的线段。
12.png
<canvasid="c"width="300"height="300"></canvas><script>constcnv = document.getElementById(c) constcxt = cnv.getContext(2d) // 第一条线cxt.moveTo(20, 100) cxt.lineTo(200, 100) cxt.stroke() // 第二条线cxt.beginPath() // 重新开启一个路径cxt.moveTo(20, 120.5) cxt.lineTo(200, 120.5) cxt.lineWidth = 4cxt.strokeStyle = redcxt.stroke() </script>复制代码设置新线段的样式,没使用beginPath()的情况
这个情况会反过来,后面的线能影响前面的线。
13.png
<canvasid="c"width="300"height="300"></canvas><script>constcnv = document.getElementById(c) constcxt = cnv.getContext(2d) // 第一条线cxt.moveTo(20, 100) cxt.lineTo(200, 100) cxt.lineWidth = 10cxt.strokeStyle = pinkcxt.stroke() // 第二条线cxt.moveTo(20, 120.5) cxt.lineTo(200, 120.5) cxt.lineWidth = 4cxt.strokeStyle = redcxt.stroke() </script>复制代码正确的做法
在设置 beginPath()的同时,也各自设置样式。这样就能做到相互不影响了。
14.png
<canvasid="c"width="300"height="300"></canvas><script>constcnv = document.getElementById(c) constcxt = cnv.getContext(2d) cxt.moveTo(20, 100) cxt.lineTo(200, 100) cxt.lineWidth = 10cxt.strokeStyle = pinkcxt.stroke() cxt.beginPath() // 重新开启一个路径cxt.moveTo(20, 120.5) cxt.lineTo(200, 120.5) cxt.lineWidth = 4cxt.strokeStyle = redcxt.stroke() </script>复制代码折线
和
直线
差不多,都是使用 moveTo()、lineTo()和 stroke()方法可以绘制折线。15.png
<canvasid="c"width="300"height="300"></canvas><script>constcnv = document.getElementById(c) constcxt = cnv.getContext(2d) cxt.moveTo(50, 200) cxt.lineTo(100, 50) cxt.lineTo(200, 200) cxt.lineTo(250, 50) cxt.stroke() </script>复制代码
画这种折线,最好在草稿纸上画一个坐标系,自己计算并描绘一下每个点大概在什么什么位置,最后在 canvas中看看效果。
矩形根据前面的基础,我们可以
使用线段来描绘矩形
,但 canvas也提供了 rect()等方法可以直接生成矩形。使用线段描绘矩形可以使用前面画线段的方法来绘制矩形
16.png
canvas ></canvas><script>constcnv = document.getElementById(c) constcxt = cnv.getContext(2d) // 绘制矩形cxt.moveTo(50, 50) cxt.lineTo(200, 50) cxt.lineTo(200, 120) cxt.lineTo(50, 120) cxt.lineTo(50, 50) // 需要闭合,又或者使用 closePath() 方法进行闭合,推荐使用 closePath()cxt.stroke() </script>复制代码
上面的代码几个点分别对应下图。
17.jpg
使用strokeRect()描边矩形strokeStyle:设置描边的属性(颜色、渐变、图案)strokeRect(x, y, width, height):描边矩形(x和y是矩形左上角起点;width 和 height 是矩形的宽高)strokeStyle必须写在 strokeRect()前面,不然样式不生效。
18.png
<canvasid="c"width="300"height="300"></canvas><script>constcnv = document.getElementById(c) constcxt = cnv.getContext(2d) // strokeStyle 属性// strokeRect(x, y, width, height) 方法cxt.strokeStyle = pinkcxt.strokeRect(50, 50, 200, 100) </script>复制代码
上面的代码可以这样理解
19.jpg
使用fillRect()填充矩形fillRect()和 strokeRect()方法差不多,但 fillRect()的作用是填充。
需要注意的是,fillStyle必须写在 fillRect()之前,不然样式不生效。
20.png
<canvasid="c"width="300"height="300"></canvas><script>constcnv = document.getElementById(c) constcxt = cnv.getContext(2d) // fillStyle 属性// fillRect(x, y, width, height) 方法cxt.fillStyle = pinkcxt.fillRect(50, 50, 200, 100) // fillRect(x, y, width, height)</script>复制代码同时使用strokeRect()和fillRect()
同时使用 strokeRect()和 fillRect()会产生描边和填充的效果
21.png
<canvasid="c"width="300"height="300"></canvas><script>constcnv = document.getElementById(c) constcxt = cnv.getContext(2d) cxt.strokeStyle = redcxt.strokeRect(50, 50, 200, 100) // strokeRect(x, y, width, height)cxt.fillStyle = yellowcxt.fillRect(50, 50, 200, 100) // fillRect(x, y, width, height)</script>复制代码使用rect()生成矩形
rect()和 fillRect() 、strokeRect()的用法差不多,唯一的区别是:
strokeRect()和 fillRect()这两个方法调用后会立即绘制;rect()方法被调用后,不会立刻绘制矩形,而是需要调用 stroke()或 fill()辅助渲染。
22.png
<canvasid="c"width="300"height="300"></canvas><script>constcnv = document.getElementById(c) constcxt = cnv.getContext(2d) cxt.strokeStyle = redcxt.fillStyle = pinkcxt.rect(50, 50, 200, 100) // rect(x, y, width, height)cxt.stroke() cxt.fill() </script>复制代码
等价公式:
cxt.strokeStyle=red,cxt.rect(50,50,200,100)cxt.stroke()//等价于cxt.strokeStyle=redcxt.strokerect(50,50,200,100)//-----------------------------cxt.fillStyle=hotpinkcxt.rect(50,50,200,100)cxt.fill()//等价于cxt.fillStyle=yellowgreencxt.fillRect(50,50,200,100)复制代码使用clearRect()清空矩形
使用 clearRect()方法可以清空指定区域。
clearRect(x, y, width, height) 复制代码
其语法和创建 cxt.rect()差不多。
23.png
<canvasid="c"width="300"height="300"></canvas><script>constcnv = document.getElementById(c) constcxt = cnv.getContext(2d) cxt.fillStyle = pink// 设置填充颜色cxt.fillRect(50, 50, 200, 200) // 填充矩形cxt.clearRect(60, 60, 180, 90) // 清空矩形</script>复制代码清空画布
canvas画布元素是矩形,所以可以通过下面的代码把整个画布清空掉。
// 省略部分代码cxt.clearRect(0, 0, cnv.width, cnv.height) 复制代码
要清空的区域:从画布左上角开始,直到画布的宽和画布的高为止。
多边形Canvas要画多边形,需要使用 moveTo()、 lineTo()和 closePath()。
三角形虽然三角形是常见图形,但 canvas并没有提供类似 rect()的方法来绘制三角形。
需要确定三角形3个点的坐标位置,然后使用 stroke()或者 fill()方法生成三角形。
24.png
<canvasid="c"width="300"height="300"></canvas><script>constcnv = document.getElementById(c) constcxt = cnv.getContext(2d) cxt.moveTo(50, 50) cxt.lineTo(200, 50) cxt.lineTo(200, 200) // 注意点:如果使用 lineTo 闭合图形,是不能很好闭合拐角位的。cxt.lineTo(50, 50) // 闭合cxt.stroke() </script>复制代码
注意,默认情况下不会自动从最后一个点连接到起点。最后一步需要设置一下 cxt.lineTo(50, 50),让它与 cxt.moveTo(50, 50)一样。这样可以让路径回到起点,形成一个闭合效果。
但这样做其实是有点问题的,而且也比较麻烦,要记住起始点坐标。
上面的闭合操作,如果遇到设置了 lineWidth或者 lineJoin就会有问题,比如:
25.png
// 省略部分代码cxt.lineWidth = 20复制代码
当线段变粗后,起始点和结束点的链接处,拐角就出现“不正常”现象。
如果需要真正闭合,可以使用 closePath()方法。
26.png
<canvasid="c"width="300"height="300"></canvas><script>constcnv = document.getElementById(c) constcxt = cnv.getContext(2d) cxt.moveTo(50, 50) cxt.lineTo(200, 50) cxt.lineTo(200, 200) // 手动闭合cxt.closePath() cxt.lineJoin = miter// 线条连接的样式。miter: 默认; bevel: 斜面; round: 圆角cxt.lineWidth = 20cxt.stroke() </script>复制代码
使用 cxt.closePath()可以自动将终点和起始点连接起来,此时看上去就正常多了。
菱形有一组邻边相等的平行四边形是菱形
27.png
<canvasid="c"width="300"height="300"></canvas><script>constcnv = document.getElementById(c) constcxt = cnv.getContext(2d) cxt.moveTo(150, 50) cxt.lineTo(250, 100) cxt.lineTo(150, 150) cxt.lineTo(50, 100) cxt.closePath() cxt.stroke() </script>复制代码
要绘制直线类型的图形,在草稿纸上标记出起始点和每个拐角的点,然后再连线即可。相对曲线图形来说,直线图形是比较容易的。
圆形绘制圆形的方法是 arc()。
语法:
arc(x, y, r, sAngle, eAngle,counterclockwise) 复制代码x和 y: 圆心坐标r: 半径sAngle: 开始角度eAngle: 结束角度counterclockwise: 绘制方向(true: 逆时针; false: 顺时针),默认 false
28.jpg
开始角度和结束角度,都是以
弧度
为单位。例如 180°就写成 Math.PI,360°写成 Math.PI * 2,以此类推。在实际开发中,为了让自己或者别的开发者更容易看懂
弧度的数值
,1°应该写成 Math.PI / 180。100°: 100 * Math.PI / 180110°: 110 * Math.PI / 180241°: 241 * Math.PI / 180注意:绘制圆形之前,必须先调用 beginPath()方法!!!在绘制完成之后,还需要调用 closePath()方法!!!
29.png
<canvasid="c"width="300"height="300"></canvas><script>constcnv = document.getElementById(c) constcxt = cnv.getContext(2d) cxt.beginPath() cxt.arc(150, 150, 80, 0, 360* Math.PI / 180) cxt.closePath() cxt.stroke() </script>复制代码半圆
如果使用 arc()方法画圆时,没做到刚好绕完一周(360°)就直接闭合路径,就会出现半圆的状态。
30.png
<canvasid="c"width="300"height="300"></canvas><script>constcnv = document.getElementById(c) constcxt = cnv.getContext(2d) cxt.beginPath() cxt.arc(150, 150, 100, 0, 180* Math.PI / 180) // 顺时针cxt.closePath() cxt.stroke() </script>复制代码
上面的代码中,cxt.arc最后一个参数没传,默认是 false,所以是顺时针绘制。
31.jpg
如果希望半圆的弧面在上方,可以将 cxt.arc最后一个参数设置成 true
32.png
<canvasid="c"width="300"height="300"></canvas><script>constcnv = document.getElementById(c) constcxt = cnv.getContext(2d) cxt.beginPath() cxt.arc(150, 150, 100, 0, 180* Math.PI / 180, true) cxt.closePath() cxt.stroke() </script>复制代码弧线
使用 arc()方法画半圆时,如果最后不调用 closePath()方法,就不会出现闭合路径。也就是说,那是一条弧线。
在 canvas中,画弧线有2中方法:arc()和 arcTo()。
arc() 画弧线如果想画一条 0° ~ 30°的弧线,可以这样写
33.png
<canvasid="c"width="300"height="300"></canvas><script>constcnv = document.getElementById(c) constcxt = cnv.getContext(2d) cxt.beginPath() cxt.arc(150, 150, 100, 0, 30* Math.PI / 180) cxt.stroke() </script>复制代码
原理如下图所示,红线代表画出来的那条弧线。
34.jpg
arcTo() 画弧线arcTo()的使用方法会更加复杂,如果初学看不太懂的话可以先跳过,看完后面的再回来补补。
语法:
arcTo(cx, cy, x2, y2, radius) 复制代码cx: 两切线交点的横坐标cy: 两切线交点的纵坐标x2: 结束点的横坐标y2: 结束点的纵坐标radius: 半径
其中,(cx, cy)也叫控制点,(x2, y2)也叫结束点。
是不是有点奇怪,为什么没有 x1和 y1?
(x1, y1)是开始点,通常是由 moveTo()或者 lineTo()提供。
arcTo()方法利用
开始点、控制点和结束点形成的家教,绘制一段与家教的两边相切并且半径为 radius的圆弧
。35.jpg
举个例子
36.png
<canvasid="c"width="300"height="300"></canvas><script>constcnv = document.getElementById(c) constcxt = cnv.getContext(2d) cxt.moveTo(40, 40) cxt.arcTo(120, 40, 120, 80, 80) cxt.stroke() </script>复制代码基础样式
前面学完基础图形,接下来可以开始了解一下如何设置元素的基础样式。
描边 stroke()前面的案例中,其实已经知道使用 stroke()方法进行描边了。这里就不再多讲这个方法。
线条宽度 lineWidthlineWidth默认值是 1,默认单位是 px。
语法:
lineWidth = 线宽 复制代码
37.png
<canvasid="c"width="300"height="300"></canvas><script>constcnv = document.getElementById(c) constcxt = cnv.getContext(2d) // 线宽 10cxt.beginPath() cxt.moveTo(50, 50) cxt.lineTo(250, 50) cxt.lineWidth = 10// 设置线宽cxt.stroke() // 线宽 20cxt.beginPath() cxt.moveTo(50, 150) cxt.lineTo(250, 150) cxt.lineWidth = 20// 设置线宽cxt.stroke() // 线宽 30cxt.beginPath() cxt.moveTo(50, 250) cxt.lineTo(250, 250) cxt.lineWidth = 30// 设置线宽cxt.stroke() </script>复制代码线条颜色 strokeStyle
使用 strokeStyle可以设置线条颜色
语法:
strokeStyle = 颜色值 复制代码
38.png
<canvasid="c"width="300"height="300"></canvas><script>constcnv = document.getElementById(c) constcxt = cnv.getContext(2d) cxt.moveTo(50, 50) cxt.lineTo(250, 50) cxt.lineWidth = 20cxt.strokeStyle = pink// 设置颜色cxt.stroke() </script>复制代码
为了展示方便,我将 lineWidth设为 20。
线帽 lineCap线帽指的是线段的开始和结尾处的样式,使用 lineCap可以设置
语法:
lineCap= 属性值复制代码
属性值包括:
butt: 默认值,无线帽square: 方形线帽round: 圆形线帽39.png
<canvasid="c"width="300"height="300"></canvas><script>constcnv = document.getElementById(c) constcxt = cnv.getContext(2d) // 设置线宽,方便演示cxt.lineWidth = 16// 默认线帽 buttcxt.beginPath() cxt.moveTo(50, 60) cxt.lineTo(250, 60) cxt.stroke() // 方形线帽 squarecxt.beginPath() cxt.lineCap = squarecxt.moveTo(50, 150) cxt.lineTo(250, 150) cxt.stroke() // 圆形线帽 roundcxt.beginPath() cxt.lineCap = roundcxt.moveTo(50, 250) cxt.lineTo(250, 250) cxt.stroke() </script>复制代码
使用 square和 round的话,会使线条变得稍微长一点点,这是给线条增加线帽的部分,这个长度在日常开发中需要注意。
线帽只对线条的开始和结尾处产生作用,对拐角不会产生任何作用。
拐角样式 lineJoin如果需要设置拐角样式,可以使用 lineJoin。
语法:
lineJoin= 属性值复制代码
属性值包括:
miter: 默认值,尖角round: 圆角bevel: 斜角40.png
<canvasid="c"width="300"height="300"></canvas><script>constcnv = document.getElementById(c) constcxt = cnv.getContext(2d) cxt.lineWidth = 20// 默认,尖角cxt.moveTo(50, 40) cxt.lineTo(200, 40) cxt.lineTo(200, 90) cxt.stroke() // 斜角 bevelcxt.beginPath() cxt.moveTo(50, 140) cxt.lineTo(200, 140) cxt.lineTo(200, 190) cxt.lineJoin = bevelcxt.stroke() // 圆角 roundcxt.beginPath() cxt.moveTo(50, 240) cxt.lineTo(200, 240) cxt.lineTo(200, 290) cxt.lineJoin = roundcxt.stroke() </script>复制代码虚线 setLineDash()
使用 setLineDash()方法可以将描边设置成虚线。
语法:
setLineDash([]) 复制代码
需要传入一个数组,且元素是数值型。
虚线分3种情况
只传1个值有2个值有3个以上的值41.png
<canvasid="c"width="300"height="300"></canvas><script>constcnv = document.getElementById(c) constcxt = cnv.getContext(2d) cxt.lineWidth = 20cxt.strokeStyle = pinkcxt.moveTo(50, 50) cxt.lineTo(200, 50) cxt.setLineDash([10]) // 只传1个参数,实线与空白都是 10pxcxt.stroke() cxt.beginPath() cxt.moveTo(50, 100) cxt.lineTo(200, 100) cxt.setLineDash([10, 20]) // 2个参数,此时,实线是 10px, 空白 20pxcxt.stroke() cxt.beginPath() cxt.moveTo(50, 150) cxt.lineTo(200, 150) cxt.setLineDash([10, 20, 5]) // 传3个以上的参数,此例:10px实线,20px空白,5px实线,10px空白,20px实线,5px空白 ……cxt.stroke() </script>复制代码
此外,还可以始终 cxt.getLineDash()获取虚线不重复的距离;
用 cxt.lineDashOffset设置虚线的偏移位。
填充使用 fill()可以填充图形,根据前面的例子应该掌握了如何使用 fill()
42.png
<canvasid="c"width="300"height="300"></canvas><script>constcnv = document.getElementById(c) constcxt = cnv.getContext(2d) cxt.fillStyle = pinkcxt.rect(50, 50, 200, 100) cxt.fill() </script>复制代码
可以使用 fillStyle设置填充颜色,默认是黑色。
非零环绕填充在使用 fill()方法填充时,需要注意一个规则:
非零环绕填充
。在使用 moveTo和 lineTo描述图形时,如果是按顺时针绘制,计数器会加1;如果是逆时针,计数器会减1。
当图形所处的位置,计数器的结果为0时,它就不会被填充。
这样说有点复杂,先看看例子
43.png
<canvasid="c"width="300"height="300"></canvas><script>constcnv = document.getElementById(c) constcxt = cnv.getContext(2d) // 外层矩形cxt.moveTo(50, 50) cxt.lineTo(250, 50) cxt.lineTo(250, 250) cxt.lineTo(50, 250) cxt.closePath() // 内层矩形cxt.moveTo(200, 100) cxt.lineTo(100, 100) cxt.lineTo(100, 200) cxt.lineTo(200, 200) cxt.closePath() cxt.fill() </script>复制代码
请看看上面的代码,我画了2个矩形,它们都没有用 beginPath()方法开辟新路径。
44.png
内层矩形是逆时针绘制的,所以内层的值是 -1,它又经过外层矩形,而外层矩形是顺时针绘制,所以经过外层时值 +1,最终内层的值为 0,所以不会被填充。
文本Canvas提供了一些操作文本的方法。
为了方便演示,我们先了解一下在 Canvas中如何给本文设置样式。
样式 font和 CSS设置 font差不多,Canvas也可以通过 font设置样式。
语法:
cxt.font = font-style font-variant font-weight font-size/line-height font-family复制代码
如果需要设置字号 font-size,需要同事设置 font-family。
cxt.font = 30px 宋体复制代码描边 strokeText()
使用 strokeText()方法进行文本描边
语法:
strokeText(text, x, y, maxWidth) 复制代码text: 字符串,要绘制的内容x: 横坐标,文本
左边
要对齐的坐标(默认左对齐)y: 纵坐标,文本底边
要对齐的坐标maxWidth: 可选参数,表示文本渲染的最大宽度(px),如果文本超出 maxWidth设置的值,文本会被压缩。45.png
<canvasid="c"width="300"height="300"></canvas><script>constcnv = document.getElementById(c) constcxt = cnv.getContext(2d) cxt.font = 60px Arial// 将字号设置成 60px,方便观察cxt.strokeText(雷猴, 30, 90) </script>复制代码设置描边颜色 strokeStyle
使用 strokeStyle设置描边颜色。
46.png
<canvasid="c"width="300"height="300"></canvas><script>constcnv = document.getElementById(c) constcxt = cnv.getContext(2d) cxt.font = 60px Arial// 将字号设置成 60px,方便观察cxt.strokeStyle = pink// 设置文本描边颜色cxt.strokeText(雷猴, 30, 90) </script>复制代码填充 fillText
使用 fillText()可填充文本。
语法和 strokeText()一样。
fillText(text, x, y, maxWidth) 复制代码
47.png
<canvasid="c"width="300"height="300"></canvas><script>constcnv = document.getElementById(c) constcxt = cnv.getContext(2d) cxt.font = 60px Arialcxt.fillText(雷猴, 30, 90) </script>复制代码设置填充颜色 fillStyle
使用 fillStyle可以设置文本填充颜色。
48.png
<canvasid="c"width="300"height="300"></canvas><script>constcnv = document.getElementById(c) constcxt = cnv.getContext(2d) cxt.font = 60px Arialcxt.fillStyle = pinkcxt.fillText(雷猴, 30, 90) </script>复制代码获取文本长度 measureText()
measureText().width方法可以获取文本的长度,单位是 px。
<canvasid="c"width="300"height="300"></canvas><script>constcnv = document.getElementById(c) constcxt = cnv.getContext(2d) lettext = 雷猴cxt.font = bold 40px Arialcxt.fillText(text, 40, 80) console.log(cxt.measureText(text).width) // 80</script>复制代码水平对齐方式 textAlign
使用 textAlign属性可以设置文字的水平对齐方式,一共有5个值可选
start: 默认。在指定位置的横坐标开始。end: 在指定坐标的横坐标结束。left: 左对齐。right: 右对齐。center: 居中对齐。49.png
红线是辅助参考线。
<canvasid="c"width="400"height="400"></canvas><script>constcnv = document.getElementById(c) constcxt = cnv.getContext(2d) // 竖向的辅助线(参考线,在画布中间)cxt.moveTo(200, 0) cxt.lineTo(200, 400) cxt.strokeStyle = redcxt.stroke() cxt.font = 30px Arial// 横坐标开始位对齐cxt.textAlign = start// 默认值,cxt.fillText(雷猴 start, 200, 40) // 横坐标结束位对齐cxt.textAlign = end// 结束对齐cxt.fillText(雷猴 end, 200, 100) // 左对齐cxt.textAlign = left// 左对齐cxt.fillText(雷猴 left, 200, 160) // 右对齐cxt.textAlign = right// 右对齐cxt.fillText(雷猴 right, 200, 220) // 居中对齐cxt.textAlign = center// 右对齐cxt.fillText(雷猴 center, 200, 280) </script>复制代码
从上面的例子看,start和 left的效果好像是一样的,end和 right也好像是一样的。
在大多数情况下,它们的确一样。但在某些国家或者某些场合,阅读文字的习惯是
从右往左
时,start就和 right一样了,end和 left也一样。这是需要注意的地方。垂直对齐方式 textBaseline使用 textBaseline属性可以设置文字的垂直对齐方式。
在使用 textBaseline前,需要自行了解 css的文本基线。
50.png
用一张网图解释一下基线
textBaseline可选属性:
alphabetic: 默认。文本基线是普通的字母基线。top: 文本基线是 em方框的顶端。bottom: 文本基线是 em方框的底端。middle: 文本基线是 em方框的正中。hanging: 文本基线是悬挂基线。51.png
红线是辅助参考线。
<canvasid="c"width="800"height="300"></canvas><script>constcnv = document.getElementById(c) constcxt = cnv.getContext(2d) // 横向的辅助线(参考线,在画布中间)cxt.moveTo(0, 150) cxt.lineTo(800, 150) cxt.strokeStyle = redcxt.stroke() cxt.font = 20px Arial// 默认 alphabeticcxt.textBaseline = alphabeticcxt.fillText(雷猴 alphabetic, 10, 150) // 默认 topcxt.textBaseline = topcxt.fillText(雷猴 top, 200, 150) // 默认 bottomcxt.textBaseline = bottomcxt.fillText(雷猴 bottom, 320, 150) // 默认 middlecxt.textBaseline = middlecxt.fillText(雷猴 middle, 480, 150) // 默认 hangingcxt.textBaseline = hangingcxt.fillText(雷猴 hanging, 640, 150) </script>复制代码
注意:在绘制文字的时候,默认是以文字的左下角作为参考点进行绘制
图片在 Canvas中可以使用 drawImage()方法绘制图片。
渲染图片渲染图片的方式有2中,一种是
在JS里加载图片再渲染
,另一种是把DOM里的图片拿到 canvas里渲染
。渲染的语法:
drawImage(image, dx, dy) 复制代码image: 要渲染的图片对象。dx: 图片左上角的横坐标位置。dy: 图片左上角的纵坐标位置。JS版
在 JS里加载图片并渲染,有以下几个步骤:
创建 Image对象引入图片等待图片加载完成使用 drawImage()方法渲染图片52.png
<canvasid="c"width="500"height="500"></canvas><script>constcnv = document.getElementById(c) constcxt = cnv.getContext(2d) // 1 创建 Image 对象constimage = newImage() // 2 引入图片image.src = ./images/dog.jpg// 3 等待图片加载完成image.onload = ()=>{ // 4 使用 drawImage() 方法渲染图片cxt.drawImage(image, 30, 30) } </script>复制代码DOM版
53.png
<style>#dogImg{ display: none; } </style><imgsrc="./images/dog.jpg"id="dogImg"/><canvasid="c"width="500"height="500"></canvas><script>constcnv = document.getElementById(c) constcxt = cnv.getContext(2d) constimage = document.getElementById(dogImg) cxt.drawImage(image, 70, 70) </script>复制代码
因为图片是从 DOM里获取到的,所以一般来说,只要在 window.onload这个生命周期内使用 drawImage都可以正常渲染图片。
本例使用了 css的方式,把图片的 display设置成 none。因为我不想被 <img>影响到本例讲解。
实际开发过程中按照实际情况设置即可。
设置图片宽高前面的例子都是直接加载图片,图片默认的宽高是多少就加载多少。
如果需要指定图片宽高,可以在前面的基础上再添加两个参数:
drawImage(image, dx, dy, dw, dh) 复制代码
image、 dx、 dy的用法和前面一样。
dw用来定义图片的宽度,dy定义图片的高度。
54.png
<canvasid="c"width="500"height="500"></canvas><script>constcnv = document.getElementById(c) constcxt = cnv.getContext(2d) constimage = newImage() image.src = ./images/dog.jpgimage.onload = ()=>{ cxt.drawImage(image, 30, 30, 100, 100) } </script>复制代码
我把图片的尺寸设为 100px * 100px,图片看上去比之前就小了很多。
截取图片截图图片同样使用drawImage()方法,只不过传入的参数数量比之前都多,而且顺序也有点不一样了。
drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh) 复制代码
以上参数缺一不可
image: 图片对象sx: 开始截取的横坐标sy: 开始截取的纵坐标sw: 截取的宽度sh: 截取的高度dx: 图片左上角的横坐标位置dy: 图片左上角的纵坐标位置dw: 图片宽度dh: 图片高度55.png
<canvasid="c"width="500"height="500"></canvas><script>constcnv = document.getElementById(c) constcxt = cnv.getContext(2d) constimage = newImage() image.src = ./images/dog.jpgimage.onload = ()=>{ cxt.drawImage(image, 0, 0, 100, 100, 30, 30, 200, 200) } </script>复制代码总结
本文主要讲解了在 Canvas中绘制一些基础图形,还有一些基础样式设置。