关于前端:Canvas-从入门到劝朋友放弃图解版✨

3次阅读

共计 21094 个字符,预计需要花费 53 分钟才能阅读完成。

本文简介

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

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

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

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

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

Canvas 是什么?

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

Canvas 和 SVG 的区别

Canvas SVG
用 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 个网站能让你的页面背景炫酷起来》

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

正文完
 0