本文简介

点赞 + 关注 + 珍藏 = 学会了


在前端畛域,如果只是懂 Vue 或者 React ,将来退职场的竞争力可能会比拟弱。

依据我多年在家待业教训来看,前端将来在 数据可视化AI 这两个畛域会比拟香,而 Canvas 是数据可视化在前端方面的根底技术。

本文就用光的速度将 canvas 给入门了。

要入门一个技术,后期最重要是快!所以本文只讲入门内容,能应酬简略我的项目。深刻的知识点会在其余文章解说。



Canvas 是什么?

  • Canvas 中文名叫 “画布”,是 HTML5 新增的一个标签。
  • Canvas 容许开发者通过 JS在这个标签上绘制各种图案。
  • Canvas 领有多种绘制门路、矩形、圆形、字符以及图片的办法。
  • Canvas 在某些状况下能够 “代替” 图片。
  • Canvas 可用于动画、游戏、数据可视化、图片编辑器、实时视频解决等畛域。



Canvas 和 SVG 的区别

CanvasSVG
用JS动静生成元素(一个HTML元素)用XML形容元素(相似HTML元素那样,可用多个元素来形容一个图形)
位图(受屏幕分辨率影响)矢量图(不受屏幕分辨率影响)
不反对事件反对事件
数据发生变化须要重绘不须要重绘

就下面的形容而言可能有点难懂,你能够关上 AntV 旗下的图形编辑引擎做比照。G6 是应用 canvas 开发的,X6 是应用 svg 开发的。


我的倡议是:如果要展现的数据量比拟大,比方一条数据就是一个元素节点,那应用 canvas 会比拟适合;如果用户操作的交互比拟多,而且对清晰度有要求(矢量图),那么应用 svg 会比拟适合。



起步

学习前端肯定要入手敲代码,而后看成果展现。

起步阶段会用几句代码阐明 canvas 如何应用,本例会画一条直线。


画条直线

  1. HTML 中创立 canvas 元素
  2. 通过 js 获取 canvas 标签
  3. canvas 标签中获取到绘图工具
  4. 通过绘图工具,在 canvas 标签上绘制图形


<!-- 1、创立 canvas 元素 --><canvas  id="c"  width="300"  height="200"  style="border: 1px solid #ccc;"></canvas><script>  // 2、获取 canvas 对象  const cnv = document.getElementById('c')  // 3、获取 canvas 上下文环境对象  const cxt = cnv.getContext('2d')  // 4、绘制图形  cxt.moveTo(100, 100) // 终点坐标 (x, y)  cxt.lineTo(200, 100) // 起点坐标 (x, y)  cxt.stroke() // 将终点和起点连接起来</script>

moveTolineTostroke 办法临时能够不必管,它们的作用是绘制图形,这些办法在前面会讲到~


留神点

### 1、默认宽高

canvas默认的 宽度(300px) 和 高度(150px)

如果不在 canvas 上设置宽高,那 canvas 元素的默认宽度是300px,默认高度是150px。


2、设置 canvas 宽高

canvas 元素提供了 widthheight 两个属性,可设置它的宽高。

须要留神的是,这两个属性只需传入数值,不须要传入单位(比方 px 等)。

<canvas width="600" height="400"></canvas>


3、不能通过 CSS 设置画布的宽高

应用 css 设置 canvas 的宽高,会呈现 内容被拉伸 的结果!!!

<style>  #c {    width: 400px;    height: 400px;    border: 1px solid #ccc;  }</style><canvas id="c"></canvas><script>  // 1、获取canvas对象  const cnv = document.getElementById('c')  // 2、获取canvas上下文环境对象  const cxt = cnv.getContext('2d')  // 3、绘制图形  cxt.moveTo(100, 100) // 终点  cxt.lineTo(200, 100) // 起点  cxt.stroke() // 将终点和起点连接起来  console.log(cnv.width) // 获取 canvas 的宽度,输入:300  console.log(cnv.height) // 获取 canvas 的高度,输入:150</script>

canvas 的默认宽度是300px,默认高度是150px。

  1. 如果应用 css 批改 canvas 的宽高(比方本例变成 400px * 400px),那宽度就由 300px 拉伸到 400px,高度由 150px 拉伸到 400px。
  2. 应用 js 获取 canvas 的宽高,此时返回的是 canvas 的默认值。

最初呈现的成果如上图所示。


4、线条默认宽度和色彩

线条的默认宽度是 1px ,默认色彩是彩色。

但因为默认状况下 canvas 会将线条的中心点和像素的底部对齐,所以会导致显示成果是 2px 和非纯彩色问题。


5、IE兼容性高

临时只有 IE 9 以上才反对 canvas 。但好消息是 IE 曾经有本人的墓碑了。

如需兼容 IE 7 和 8 ,能够应用 ExplorerCanvas 。但即便是应用了 ExplorerCanvas 依然会有所限度,比方无奈应用 fillText() 办法等。



根底图形

坐标系

在绘制根底图形之前,须要先搞革除 Canvas 应用的坐标系。

Canvas 应用的是 W3C 坐标系 ,也就是遵循咱们屏幕、报纸的浏览习惯,从上往下,从左往右。

W3C 坐标系数学直角坐标系X轴 是一样的,只是 Y轴 的反向相同。

W3C 坐标系Y轴 正方向向下。


直线

一条直线

最简略的起步形式是画一条直线。这里所说的 “直线” 是几何学里的 “线段” 的意思。

须要用到这3个办法:

  1. moveTo(x1, y1):终点坐标 (x, y)
  2. lineTo(x2, y2):下一个点的坐标 (x, y)
  3. stroke():将所有坐标用一条线连起来


起步阶段能够先这样了解。

<canvas id="c" style="border: 1px solid #ccc;"></canvas><script>  const cnv = document.getElementById('c')  const cxt = cnv.getContext('2d')  // 绘制直线  cxt.moveTo(50, 100) // 终点坐标  cxt.lineTo(200, 50) // 下一个点的坐标  cxt.stroke() // 将下面的坐标用一条线连接起来</script>

下面的代码所出现的成果,能够看下图解释(手不太聪慧,画得不是很规范,心愿能看懂)


多条直线

如需画多条直线,能够用会下面那几个办法。

<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>  const cnv = document.getElementById('c')  const cxt = 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 和非纯彩色问题。

上图每个格子代表 1px

线的中心点会和画布像素点的底部对齐,所以会线两头是彩色的,但因为一个像素就不能再切割了,所以会有半个像素被染色,就变成了浅灰色。

所以如果你设置的 Y轴 值是一个整数,就会呈现下面那种状况。


设置款式

  • lineWidth:线的粗细
  • strokeStyle:线的色彩
  • lineCap:线帽:默认: butt; 圆形: round; 方形: square


<canvas id="c" style="border: 1px solid #ccc;"></canvas><script>  const cnv = document.getElementById('c')  const cxt = cnv.getContext('2d')  // 绘制直线  cxt.moveTo(50, 50)  cxt.lineTo(200, 50)  // 批改直线的宽度  cxt.lineWidth = 20  // 批改直线的色彩  cxt.strokeStyle = 'pink'  // 批改直线两端款式  cxt.lineCap = 'round' // 默认: butt; 圆形: round; 方形: square  cxt.stroke()</script>


新开门路

开拓新门路的办法:

  • beginPath()


在绘制多条线段的同时,还要设置线段款式,通常须要开拓新门路。

要不然款式之间会互相净化。

比方这样

<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>  const cnv = document.getElementById('c')  const cxt = cnv.getContext('2d')  // 第一条线  cxt.moveTo(20, 100)  cxt.lineTo(200, 100)  cxt.lineWidth = 10  cxt.strokeStyle = 'pink'  cxt.stroke()  // 第二条线  cxt.moveTo(20, 120.5)  cxt.lineTo(200, 120.5)  cxt.stroke()</script>


如果不想互相净化,须要做2件事:

  1. 应用 beginPath() 办法,从新开一个门路
  2. 设置新线段的款式(必须项)


如果下面2步却了其中1步都会有影响。

只应用 beginPath()

<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>  const cnv = document.getElementById('c')  const cxt = cnv.getContext('2d')  // 第一条线  cxt.moveTo(20, 100)  cxt.lineTo(200, 100)  cxt.lineWidth = 10  cxt.strokeStyle = 'pink'  cxt.stroke()  // 第二条线  cxt.beginPath() // 从新开启一个门路  cxt.moveTo(20, 120.5)  cxt.lineTo(200, 120.5)  cxt.stroke()</script>

第一条线的款式会影响之后的线。

但如果应用了 beginPath() ,前面的线段不会影响后面的线段。

<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>  const cnv = document.getElementById('c')  const cxt = 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 = 4  cxt.strokeStyle = 'red'  cxt.stroke()</script>


设置新线段的款式,没应用 beginPath() 的状况

这个状况会反过来,前面的线能影响后面的线。

<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>  const cnv = document.getElementById('c')  const cxt = cnv.getContext('2d')  // 第一条线  cxt.moveTo(20, 100)  cxt.lineTo(200, 100)  cxt.lineWidth = 10  cxt.strokeStyle = 'pink'  cxt.stroke()  // 第二条线  cxt.moveTo(20, 120.5)  cxt.lineTo(200, 120.5)  cxt.lineWidth = 4  cxt.strokeStyle = 'red'  cxt.stroke()</script>


正确的做法

在设置 beginPath() 的同时,也各自设置款式。这样就能做到互相不影响了。

<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>  const cnv = document.getElementById('c')  const cxt = cnv.getContext('2d')  cxt.moveTo(20, 100)  cxt.lineTo(200, 100)  cxt.lineWidth = 10  cxt.strokeStyle = 'pink'  cxt.stroke()  cxt.beginPath() // 从新开启一个门路  cxt.moveTo(20, 120.5)  cxt.lineTo(200, 120.5)  cxt.lineWidth = 4  cxt.strokeStyle = 'red'  cxt.stroke()</script>


折线

直线 差不多,都是应用 moveTo()lineTo()stroke() 办法能够绘制折线。

<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>  const cnv = document.getElementById('c')  const cxt = 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() 等办法能够间接生成矩形。


应用线段描述矩形

能够应用后面画线段的办法来绘制矩形

canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script> const cnv = document.getElementById('c') const cxt = 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>


下面的代码几个点别离对应下图。


应用 strokeRect() 描边矩形

  • strokeStyle:设置描边的属性(色彩、突变、图案)
  • strokeRect(x, y, width, height):描边矩形(x和y是矩形左上角终点;width 和 height 是矩形的宽高)
  • strokeStyle 必须写在 strokeRect() 后面,不然款式不失效。


<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>  const cnv = document.getElementById('c')  const cxt = cnv.getContext('2d')  // strokeStyle 属性  // strokeRect(x, y, width, height) 办法  cxt.strokeStyle = 'pink'  cxt.strokeRect(50, 50, 200, 100)</script>

下面的代码能够这样了解


应用 fillRect() 填充矩形

fillRect()strokeRect() 办法差不多,但 fillRect() 的作用是填充。

须要留神的是,fillStyle 必须写在 fillRect() 之前,不然款式不失效。

<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>  const cnv = document.getElementById('c')  const cxt = cnv.getContext('2d')  // fillStyle 属性  // fillRect(x, y, width, height) 办法  cxt.fillStyle = 'pink'  cxt.fillRect(50, 50, 200, 100) // fillRect(x, y, width, height)</script>


同时应用 strokeRect()fillRect()

同时应用 strokeRect()fillRect() 会产生描边和填充的成果

<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>  const cnv = document.getElementById('c')  const cxt = cnv.getContext('2d')  cxt.strokeStyle = 'red'  cxt.strokeRect(50, 50, 200, 100) // strokeRect(x, y, width, height)  cxt.fillStyle = 'yellow'  cxt.fillRect(50, 50, 200, 100) // fillRect(x, y, width, height)</script>


应用 rect() 生成矩形

rect()fillRect() 、strokeRect() 的用法差不多,惟一的区别是:

strokeRect()fillRect() 这两个办法调用后会立刻绘制;rect() 办法被调用后,不会立即绘制矩形,而是须要调用 stroke()fill() 辅助渲染。


<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>  const cnv = document.getElementById('c')  const cxt = cnv.getContext('2d')  cxt.strokeStyle = 'red'  cxt.fillStyle = 'pink'  cxt.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 = 'red'cxt.strokerect(50, 50, 200, 100)// -----------------------------cxt.fillStyle = 'hotpink'cxt.rect(50, 50, 200, 100)cxt.fill()// 等价于cxt.fillStyle = 'yellowgreen'cxt.fillRect(50, 50, 200, 100)


应用 clearRect() 清空矩形

应用 clearRect() 办法能够清空指定区域。

clearRect(x, y, width, height)

其语法和创立 cxt.rect() 差不多。


<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>  const cnv = document.getElementById('c')  const cxt = 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() 办法生成三角形。


<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>  const cnv = document.getElementById('c')  const cxt = 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 就会有问题,比方:

// 省略局部代码cxt.lineWidth = 20

当线段变粗后,起始点和完结点的链接处,拐角就呈现“不失常”景象。


如果须要真正闭合,能够应用 closePath() 办法。

<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>  const cnv = document.getElementById('c')  const cxt = 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 = 20  cxt.stroke()</script>

应用 cxt.closePath() 能够主动将起点和起始点连接起来,此时看上去就失常多了。


菱形

有一组邻边相等的平行四边形是菱形

<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>  const cnv = document.getElementById('c')  const cxt = 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)
  • xy: 圆心坐标
  • r: 半径
  • sAngle: 开始角度
  • eAngle: 完结角度
  • counterclockwise: 绘制方向(true: 逆时针; false: 顺时针),默认 false


开始角度和完结角度,都是以弧度为单位。例如 180°就写成 Math.PI ,360°写成 Math.PI * 2 ,以此类推。

在理论开发中,为了让本人或者别的开发者更容易看懂弧度的数值,1°应该写成 Math.PI / 180

  • 100°: 100 * Math.PI / 180
  • 110°: 110 * Math.PI / 180
  • 241°: 241 * Math.PI / 180


留神:绘制圆形之前,必须先调用 beginPath() 办法!!! 在绘制实现之后,还须要调用 closePath() 办法!!!


<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>  const cnv = document.getElementById('c')  const cxt = cnv.getContext('2d')  cxt.beginPath()  cxt.arc(150, 150, 80, 0, 360)  cxt.closePath()  cxt.stroke()</script>


半圆

如果应用 arc() 办法画圆时,没做到刚好绕完一周(360°)就间接闭合门路,就会呈现半圆的状态。

<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>  const cnv = document.getElementById('c')  const cxt = cnv.getContext('2d')  cxt.beginPath()  cxt.arc(150, 150, 100, 0, 180 * Math.PI / 180) // 顺时针  cxt.closePath()  cxt.stroke()</script>

下面的代码中,cxt.arc 最初一个参数没传,默认是 false ,所以是顺时针绘制。


如果心愿半圆的弧面在上方,能够将 cxt.arc 最初一个参数设置成 true

<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>  const cnv = document.getElementById('c')  const cxt = 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° 的弧线,能够这样写

<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>  const cnv = document.getElementById('c')  const cxt = cnv.getContext('2d')  cxt.beginPath()  cxt.arc(150, 150, 100, 0, 30 * Math.PI / 180)  cxt.stroke()</script>


原理如下图所示,红线代表画进去的那条弧线。


arcTo() 画弧线

arcTo() 的应用办法会更加简单,如果初学看不太懂的话能够先跳过,看完前面的再回来补补。

语法:

arcTo(cx, cy, x2, y2, radius)
  • cx: 两切线交点的横坐标
  • cy: 两切线交点的纵坐标
  • x2: 完结点的横坐标
  • y2: 完结点的纵坐标
  • radius: 半径

其中,(cx, cy) 也叫控制点,(x2, y2) 也叫完结点。

是不是有点奇怪,为什么没有 x1y1

(x1, y1) 是开始点,通常是由 moveTo() 或者 lineTo() 提供。


arcTo() 办法利用 开始点、控制点和完结点造成的家教,绘制一段与家教的两边相切并且半径为 radius 的圆弧


举个例子

<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>  const cnv = document.getElementById('c')  const cxt = cnv.getContext('2d')  cxt.moveTo(40, 40)  cxt.arcTo(120, 40, 120, 80, 80)  cxt.stroke()</script>



根底款式

后面学完根底图形,接下来能够开始理解一下如何设置元素的根底款式。


描边 stroke()

后面的案例中,其实曾经晓得应用 stroke() 办法进行描边了。这里就不再多讲这个办法。


线条宽度 lineWidth

lineWidth 默认值是 1 ,默认单位是 px

语法:

lineWidth = 线宽


<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>  const cnv = document.getElementById('c')  const cxt = cnv.getContext('2d')  // 线宽 10  cxt.beginPath()  cxt.moveTo(50, 50)  cxt.lineTo(250, 50)  cxt.lineWidth = 10 // 设置线宽  cxt.stroke()  // 线宽 20  cxt.beginPath()  cxt.moveTo(50, 150)  cxt.lineTo(250, 150)  cxt.lineWidth = 20 // 设置线宽  cxt.stroke()  // 线宽 30  cxt.beginPath()  cxt.moveTo(50, 250)  cxt.lineTo(250, 250)  cxt.lineWidth = 30 // 设置线宽  cxt.stroke()</script>


线条色彩 strokeStyle

应用 strokeStyle 能够设置线条色彩

语法:

strokeStyle = 色彩值


<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>  const cnv = document.getElementById('c')  const cxt = cnv.getContext('2d')  cxt.moveTo(50, 50)  cxt.lineTo(250, 50)  cxt.lineWidth = 20  cxt.strokeStyle = 'pink' // 设置色彩  cxt.stroke()</script>

为了展现不便,我将 lineWidth 设为 20。


线帽 lineCap

线帽指的是线段的开始和结尾处的款式,应用 lineCap 能够设置

语法:

lineCap = '属性值'


属性值包含:

  • butt: 默认值,无线帽
  • square: 方形线帽
  • round: 圆形线帽


<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>  const cnv = document.getElementById('c')  const cxt = cnv.getContext('2d')  // 设置线宽,不便演示  cxt.lineWidth = 16  // 默认线帽 butt  cxt.beginPath()  cxt.moveTo(50, 60)  cxt.lineTo(250, 60)  cxt.stroke()  // 方形线帽 square  cxt.beginPath()  cxt.lineCap = 'square'  cxt.moveTo(50, 150)  cxt.lineTo(250, 150)  cxt.stroke()  // 圆形线帽 round  cxt.beginPath()  cxt.lineCap = 'round'  cxt.moveTo(50, 250)  cxt.lineTo(250, 250)  cxt.stroke()</script>


应用 squareround 的话,会使线条变得略微长一点点,这是给线条减少线帽的局部,这个长度在日常开发中须要留神。

线帽只对线条的开始和结尾处产生作用,对拐角不会产生任何作用。


拐角款式 lineJoin

如果须要设置拐角款式,能够应用 lineJoin

语法:

lineJoin = '属性值'


属性值包含:

  • miter: 默认值,尖角
  • round: 圆角
  • bevel: 斜角


<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>  const cnv = document.getElementById('c')  const cxt = cnv.getContext('2d')    cxt.lineWidth = 20  // 默认,尖角  cxt.moveTo(50, 40)  cxt.lineTo(200, 40)  cxt.lineTo(200, 90)  cxt.stroke()  // 斜角 bevel  cxt.beginPath()  cxt.moveTo(50, 140)  cxt.lineTo(200, 140)  cxt.lineTo(200, 190)  cxt.lineJoin = 'bevel'  cxt.stroke()  // 圆角 round  cxt.beginPath()  cxt.moveTo(50, 240)  cxt.lineTo(200, 240)  cxt.lineTo(200, 290)  cxt.lineJoin = 'round'  cxt.stroke()</script>


虚线 setLineDash()

应用 setLineDash() 办法能够将描边设置成虚线。

语法:

setLineDash([])

须要传入一个数组,且元素是数值型。


虚线分3种状况

  1. 只传1个值
  2. 有2个值
  3. 有3个以上的值


<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>  const cnv = document.getElementById('c')  const cxt = cnv.getContext('2d')  cxt.lineWidth = 20  cxt.strokeStyle = 'pink'  cxt.moveTo(50, 50)  cxt.lineTo(200, 50)  cxt.setLineDash([10]) // 只传1个参数,实线与空白都是 10px  cxt.stroke()  cxt.beginPath()  cxt.moveTo(50, 100)  cxt.lineTo(200, 100)  cxt.setLineDash([10, 20]) // 2个参数,此时,实线是 10px, 空白 20px  cxt.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()


<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>  const cnv = document.getElementById('c')  const cxt = cnv.getContext('2d')  cxt.fillStyle = 'pink'  cxt.rect(50, 50, 200, 100)  cxt.fill()</script>


能够应用 fillStyle 设置填充色彩,默认是彩色。


非零盘绕填充

在应用 fill() 办法填充时,须要留神一个规定:非零盘绕填充

在应用 moveTolineTo 形容图形时,如果是按顺时针绘制,计数器会加1;如果是逆时针,计数器会减1。

当图形所处的地位,计数器的后果为0时,它就不会被填充。


这样说有点简单,先看看例子

<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>  const cnv = document.getElementById('c')  const cxt = 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() 办法开拓新门路。

内层矩形是逆时针绘制的,所以内层的值是 -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 设置的值,文本会被压缩。

<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>  const cnv = document.getElementById('c')  const cxt = cnv.getContext('2d')  cxt.font = '60px Arial' // 将字号设置成 60px,不便察看  cxt.strokeText('雷猴', 30, 90)</script>


设置描边色彩 strokeStyle

应用 strokeStyle 设置描边色彩。


<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>  const cnv = document.getElementById('c')  const cxt = cnv.getContext('2d')  cxt.font = '60px Arial' // 将字号设置成 60px,不便察看  cxt.strokeStyle = 'pink' // 设置文本描边色彩  cxt.strokeText('雷猴', 30, 90)</script>


填充 fillText

应用 fillText() 可填充文本。

语法和 strokeText() 一样。

fillText(text, x, y, maxWidth)


<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>  const cnv = document.getElementById('c')  const cxt = cnv.getContext('2d')  cxt.font = '60px Arial'  cxt.fillText('雷猴', 30, 90)</script>


设置填充色彩 fillStyle

应用 fillStyle 能够设置文本填充色彩。


<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>  const cnv = document.getElementById('c')  const cxt = cnv.getContext('2d')  cxt.font = '60px Arial'  cxt.fillStyle = 'pink'  cxt.fillText('雷猴', 30, 90)</script>


获取文本长度 measureText()

measureText().width 办法能够获取文本的长度,单位是 px

<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>  const cnv = document.getElementById('c')  const cxt = cnv.getContext('2d')  let text = '雷猴'  cxt.font = 'bold 40px Arial'  cxt.fillText(text, 40, 80)  console.log(cxt.measureText(text).width) // 80</script>


程度对齐形式 textAlign

应用 textAlign 属性能够设置文字的程度对齐形式,一共有5个值可选

  • start: 默认。在指定地位的横坐标开始。
  • end: 在指定坐标的横坐标完结。
  • left: 左对齐。
  • right: 右对齐。
  • center: 居中对齐。


红线是辅助参考线。

<canvas id="c" width="400" height="400" style="border: 1px solid #ccc;"></canvas><script>  const cnv = document.getElementById('c')  const cxt = cnv.getContext('2d')  // 竖向的辅助线(参考线,在画布两头)  cxt.moveTo(200, 0)  cxt.lineTo(200, 400)  cxt.strokeStyle = 'red'  cxt.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>


从下面的例子看,startleft 的成果如同是一样的,endright 也如同是一样的。

在大多数状况下,它们确实一样。但在某些国家或者某些场合,浏览文字的习惯是 从右往左 时,start 就和 right 一样了,endleft 也一样。这是须要留神的中央。


垂直对齐形式 textBaseline

应用 textBaseline 属性能够设置文字的垂直对齐形式。

在应用 textBaseline 前,须要自行理解 css 的文本基线。

用一张网图解释一下基线


textBaseline 可选属性:

  • alphabetic: 默认。文本基线是一般的字母基线。
  • top: 文本基线是 em 方框的顶端。
  • bottom: 文本基线是 em 方框的底端。
  • middle: 文本基线是 em 方框的正中。
  • hanging: 文本基线是悬挂基线。


红线是辅助参考线。

<canvas id="c" width="800" height="300" style="border: 1px solid #ccc;"></canvas><script>  const cnv = document.getElementById('c')  const cxt = cnv.getContext('2d')  // 横向的辅助线(参考线,在画布两头)  cxt.moveTo(0, 150)  cxt.lineTo(800, 150)  cxt.strokeStyle = 'red'  cxt.stroke()  cxt.font = '20px Arial'  // 默认 alphabetic  cxt.textBaseline = 'alphabetic'  cxt.fillText('雷猴 alphabetic', 10, 150)  // 默认 top  cxt.textBaseline = 'top'  cxt.fillText('雷猴 top', 200, 150)  // 默认 bottom  cxt.textBaseline = 'bottom'  cxt.fillText('雷猴 bottom', 320, 150)  // 默认 middle  cxt.textBaseline = 'middle'  cxt.fillText('雷猴 middle', 480, 150)  // 默认 hanging  cxt.textBaseline = 'hanging'  cxt.fillText('雷猴 hanging', 640, 150)</script>


留神:在绘制文字的时候,默认是以文字的左下角作为参考点进行绘制



图片

Canvas 中能够应用 drawImage() 办法绘制图片。


渲染图片

渲染图片的形式有2中,一种是在JS里加载图片再渲染,另一种是把DOM里的图片拿到 canvas 里渲染

渲染的语法:

drawImage(image, dx, dy)
  • image: 要渲染的图片对象。
  • dx: 图片左上角的横坐标地位。
  • dy: 图片左上角的纵坐标地位。


JS版

JS 里加载图片并渲染,有以下几个步骤:

  1. 创立 Image 对象
  2. 引入图片
  3. 期待图片加载实现
  4. 应用 drawImage() 办法渲染图片


<canvas id="c" width="500" height="500" style="border: 1px solid #ccc;"></canvas><script>  const cnv = document.getElementById('c')  const cxt = cnv.getContext('2d')  // 1 创立 Image 对象  const image = new Image()  // 2 引入图片  image.src = './images/dog.jpg'  // 3 期待图片加载实现  image.onload = () => {    // 4 应用 drawImage() 办法渲染图片    cxt.drawImage(image, 30, 30)  }</script>


DOM版

<style>  #dogImg {    display: none;  }</style><img src="./images/dog.jpg" id="dogImg"/><canvas id="c" width="500" height="500" style="border: 1px solid #ccc;"></canvas><script>  const cnv = document.getElementById('c')  const cxt = cnv.getContext('2d')  const image = 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 定义图片的高度。


<canvas id="c" width="500" height="500" style="border: 1px solid #ccc;"></canvas><script>  const cnv = document.getElementById('c')  const cxt = cnv.getContext('2d')  const image = new Image()  image.src = './images/dog.jpg'  image.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: 图片高度


<canvas id="c" width="500" height="500" style="border: 1px solid #ccc;"></canvas><script>  const cnv = document.getElementById('c')  const cxt = cnv.getContext('2d')  const image = new Image()  image.src = './images/dog.jpg'  image.onload = () => {    cxt.drawImage(image, 0, 0, 100, 100, 30, 30, 200, 200)  }</script>



总结

本文次要解说了在 Canvas 中绘制一些根底图形,还有一些根底款式设置。

还有更多高级的玩法会在之后的文章中讲到,比方突变、投影、滤镜等等。



代码仓库

⭐雷猴 Canvas



举荐浏览

《Fabric.js 从入门到收缩》

《『Three.js』腾飞!》

《console.log也能插图!!!》

《纯css实现117个Loading成果》

《视差特效的原理和实现办法》

《这18个网站能让你的页面背景炫酷起来》


点赞 + 关注 + 珍藏 = 学会了
点赞 + 关注 + 珍藏 = 学会了