canvas基础

最近在工作中,有个项目一直需要用到canvas相关知识,之前对这块不是很熟练,借着十一假期来进行学习学习

1. 基础

1.1 创建canvas

html


在 canvas 上属性 width和height直接指定大小,或用下面的方式

javascript

var canvas =  document.getElementById("canvas")
canvas.width = 1024
canvas.height = 768
var ctx = canvas.getContext("2d");

下面所有例子的代码省略了上面的穿件canvas代码

1.2 坐标

左上角为原点,向右为x轴正方向,向下为y轴正方向

image.png

这个和数学中的坐标系有点区别


image.png

2. 绘制线条

2.1 步骤

  1. 使用moveTo定义笔触开始的点坐标,坐标的表示见 1.2 图形表示
  2. 使用lineTo定义笔触将要到达的点
  3. 使用 stroke 进行绘制
ctx.moveTo(100, 100)  // 将笔尖移动到 100 100 的位置
ctx.lineTo(300, 100)  // 将要绘制到的位置
ctx.stroke() // 绘制

第1,2步是设置状态,第三步才是进行绘制操作。

此外,对于线的状态设置还有

ctx.lineWidth = 2
ctx.strokeStyle = '#f00'

3. 绘制图形

3.1 根据线->图形。

在初级数学中,我们都知道线可以组成面,线闭合起来就能形成一个图形,那么有了 2 中的知识,我们就能绘制出一个图形了。just do it

3.1.1绘制一个三角形

ctx.moveTo(100, 100)  // 将笔尖移动到 100 100 的位置
ctx.lineTo(300, 100)  // 将要绘制到的位置 1
ctx.lineTo(100, 300)  // 将要绘制到的位置 2
ctx.lineTo(100, 100)  // 将要绘制到的位置 3 回到笔触起点
ctx.stroke() // 绘制线条
image.png

3.1.2 填充

在上面我们在设置状态(确定顶点坐标)之后,用了canvas的 stroke() 这个api,它表示绘制线条,其实我们还有其他的api,填充

fill

  • stroke
  • fill
ctx.moveTo(100, 100)  // 将笔尖移动到 100 100 的位置
ctx.lineTo(300, 100)  // 将要绘制到的位置 1
ctx.lineTo(100, 300)  // 将要绘制到的位置 2
ctx.lineTo(100, 100)  // 将要绘制到的位置 3 回到笔触起点
ctx.fill() // 填充
image.png

这时候我们就得到一个实心的图形啦!

哎?怎么是黑色的?因为默认就是黑色的。线条能设置颜色,那么我们填充也能设置颜色,使用下面的api

  • fillStyle
ctx.moveTo(100, 100)  // 将笔尖移动到 100 100 的位置
ctx.lineTo(300, 100)  // 将要绘制到的位置 1
ctx.lineTo(100, 300)  // 将要绘制到的位置 2
ctx.lineTo(100, 100)  // 将要绘制到的位置 3 回到笔触起点
ctx.fillStyle = '#f00' // 设置填充样式
ctx.fill() // 填充
image.png

所以综上所述,只要我们知道图形的顶点坐标,我们再用线连接起来,就能绘制大部分的图形了!

3.2 绘制矩形

矩形,规规矩矩,所以canvas中有特定的api进行绘制,提供以下三个api

strokeRect(x, y, width, height) // 绘制一个矩形的边框
fillRect(x, y, width, height) // 绘制一个填充的矩形
clearRect(x, y, width, height) // 清除指定矩形区域,让清除部分完全透明

我们使用上面三个分别尝试

3.2.1 strokeRect

// ctx.strokeStyle = '#f00' // 设置矩形线条颜色
ctx.strokeRect(100, 100, 200, 200) // 左上角坐标为(100,100),宽高分别为 200, 200 的线状矩形
image.png

3.2.2 fillRect

// ctx.fillStyle = '#f00' // 设置矩形线条颜色
ctx.fillRect(100, 100, 200, 200) // 左上角坐标为(100,100),宽高分别为 200, 200 的填充矩形
image.png

3.2.3 clearRect

清除指定矩形区域,让清除部分完全透明

我们在 3.2.2的基础上写

// ctx.fillStyle = '#f00' // 设置矩形线条颜色
ctx.fillRect(100, 100, 200, 200) // 左上角坐标为(100,100),宽高分别为 200, 200 的填充矩形
ctx.clearRect(100, 100, 150, 150)
image.png

可以看到,在3.2.2基础上,某个区域被清除了。

3.2.4 绘制多个图形/线条,状态问题(重要!!!)

canvas是基于状态进行绘制,先设定状态,再进行绘制。在一个canvas画布上进行多个图形/线条绘制是正常操作,我们看下面代码

// 绘制一个边线宽为5, 颜色为红色的三角形
ctx.moveTo(100, 100)  // 将笔尖移动到 100 100 的位置
ctx.lineTo(300, 100)  // 将要绘制到的位置 1
ctx.lineTo(100, 300)  // 将要绘制到的位置 2
ctx.lineTo(100, 100)  // 将要绘制到的位置 3 回到笔触起点
ctx.strokeStyle = '#f00'
ctx.lineWidth = 5
ctx.stroke() // 绘制


// 绘制一条绿色的直线,线宽默认
ctx.moveTo(400, 100)
ctx.lineTo(100, 400)
ctx.strokeStyle = '#0f0'
ctx.stroke()

运行结果如下


image.png

哎?从上述代码中,我们明明是先绘制一个边线宽为5, 颜色为红色的三角形,然后再绘制一条绿色的,线宽默认的直线。

但是我们发现了啥,三角形的边颜色变成了绿色,直线线宽变成了5

原因如下:

  1. canvas是基于状态进行绘制,先设定状态,再进行绘制
  2. 绘制三角形的时候,我们设置了状态 1.线颜色为红色, 2. 线宽为5
  3. 绘制直线的时候,我们设置了状态 1. 线颜色为绿色
  4. 这时候,我们后面的状态就会将前面的状态进行覆盖!及 1. 线颜色为绿色, 2. 线宽为5

真相大白!这是新手经常会遇到的问题。这个就类似于作用域,是不是?

所以我们要对每个图形状态进行状态的限定。用到两个api

  • beginPath
  • closePath

将每一段状态用beginPath 和 closePath 进行括起来

直接上代码

// 绘制一个三角形
ctx.beginPath()
ctx.moveTo(100, 100)  // 将笔尖移动到 100 100 的位置
ctx.lineTo(300, 100)  // 将要绘制到的位置 1
ctx.lineTo(100, 300)  // 将要绘制到的位置 2
ctx.lineTo(100, 100)  // 将要绘制到的位置 3 回到笔触起点
ctx.strokeStyle = '#f00'
ctx.lineWidth = 5
ctx.closePath()
ctx.stroke() // 绘制


// 绘制一条直线
ctx.beginPath()
ctx.moveTo(400, 100)
ctx.lineTo(100, 400)
ctx.strokeStyle = '#0f0'
ctx.closePath()
ctx.stroke()

运行结果


上述代码,其实不需要closePath也能得到同样的效果,closePath会自动将一个不封闭的图形封闭。

// 绘制一个三角形
ctx.beginPath()
ctx.moveTo(100, 100)  // 将笔尖移动到 100 100 的位置
ctx.lineTo(300, 100)  // 将要绘制到的位置 1
ctx.lineTo(100, 300)  // 将要绘制到的位置 2
// ctx.lineTo(100, 100)  // 将要绘制到的位置 3 回到笔触起点  注意此时我注释了这个状态,不是一个封闭的三角形了
ctx.strokeStyle = '#f00'
ctx.lineWidth = 5
ctx.closePath() // 自动封闭图形
ctx.stroke() // 绘制


// 绘制一条直线
ctx.beginPath()
ctx.moveTo(400, 100)
ctx.lineTo(100, 400)
ctx.strokeStyle = '#0f0'
// ctx.closePath() // 可以不需要
ctx.stroke()

3.2.4 绘制圆/圆弧 arc

  1. 重要api
ctx.arc(centerx, centery, radius, startingAngle, endingAngle, anticlockwise = false)

上面的api, 参数分别表示:圆心x, 圆心y,半径r, 起始角度, 结束角度, 逆时针旋转 (默认false)

  1. 角度示意图
image.png
  1. 例子
  // 圆心x,圆心y,半径r, 起始角度, 结束角度, 逆时针旋转 (默认false)
  ctx.arc(300, 300, 100, 0, 2 * Math.PI, false)
  ctx.stroke()
image.png

4. 颜色覆盖、透明

css中使用 rgba来实现颜色及其透明度,在canvas同样适用

ctx.beginPath()
ctx.fillStyle = 'rgba(255,100,100, 0.5)'
ctx.fillRect(100, 100, 100, 100)
ctx.closePath()


ctx.beginPath()
ctx.fillStyle = 'rgba(100,100,100, 0.5)'
ctx.fillRect(150, 150, 100, 100)
ctx.closePath()

运行结果


image.png

5. 图形变换和状态保存

5.1 通过translate讲解 save和restore

canvas中translate(x,y)可以讲绘制的图形平移到(x,y) 的位置

ctx.fillStyle = 'rgba(255,100,100, 0.5)'
ctx.translate(100, 100) // 从0,0 平移到 100,100
ctx.fillRect(0, 0, 100, 100)

ctx.fillStyle = 'rgba(100,100,100, 0.5)'
ctx.translate(150, 150) // // 希望从0,0 平移 150,150, 但是和上面的状态叠加了 变成了 translate(250,250)
ctx.fillRect(0, 0, 100, 100)

结果

image.png

第二个灰色的矩形,我们希望从(0,0)平移(150,150)个横纵像素, 按理说和第一个粉色的矩形有重合,但是和上面的状态叠加了 变成了 translate(250,250)。

这时候我们就需要用到 save和restore这两个api,它们是成对出现的,利用它们更改上面的代码

ctx.save()
ctx.fillStyle = 'rgba(255,100,100, 0.5)'
ctx.translate(100, 100) // 从0,0 平移到 100,100
ctx.fillRect(0, 0, 100, 100)
ctx.restore()

ctx.save()
ctx.fillStyle = 'rgba(100,100,100, 0.5)'
ctx.translate(150, 150) // // 希望从0,0 平移到 150,150, 但是和上面的状态叠加了 变成了 translate(250,250)
ctx.fillRect(0, 0, 100, 100)
ctx.restore()

结果如下:

image.png

5.2 tranlate、rotate、scale

5.2.1 translate

对图形进行平移

5.1 章节已经演示过

5.2.2 rotate

旋转

rotate(度数)

ctx.save()
ctx.fillStyle = 'rgba(100,100,100, 0.5)'
ctx.fillRect(200, 200, 100, 100)
ctx.restore()

ctx.save()
ctx.fillStyle = 'rgba(255,100,100, 0.5)'
ctx.rotate(30 / 180 * Math.PI) // 以(0,0)的点为圆心, 以(0,0)到左上角的顶点坐标的距离为半径,顺时针旋转30度
ctx.fillRect(200, 200, 100, 100)
ctx.restore()

以(0,0)的点为圆心, 以(0,0)到左上角的顶点坐标的距离为半径,顺时针旋转30度。如图,粉色的图形即为灰色图形旋转后的位置

image.png

5.2.3 scale

缩放

特性

scale() 用于修改元素的大小。可以通过向量形式定义的缩放值来放大或缩小元素,同时可以在不同的方向设置不同的缩放值。该变换通过一个二维向量确定在一个方向缩放的多少。如果缩放向量的两个坐标是相等的,那么所讲是均等的,或者说是各向同的,同时元素的形状是被保持的。---- MDN

我们看下面的代码,蓝色部分可以看作是灰色部分放大两倍的。但是我们看看结果

ctx.fillStyle = '#98CCFB'
ctx.fillRect(50, 50, 200, 100)

ctx.fillStyle = '#CCCCCC'
ctx.scale(2,2)
ctx.fillRect(50, 50, 200, 100)

结果

image.png

我们发现,他们的顶点位置竟然也发生了变化,变成了(100,100),可是咱们代码中,顶点位置都是(50,50)。这就是scale值得注意的地方,它会对起始位置的坐标也会进行缩放操作。下面是示意图:

image.png

如图蓝色图形被放大2倍,它的顶点位置也会被相应缩放,当大了两倍。

副作用

从上面,我们可以看见scale的副作用之一就是会对起始坐标的位置进行缩放。我们接着上面的例子进行改造下,我们换成strokeRect进行绘制图形。

ctx.save()
ctx.fillStyle = '#98CCFB'
ctx.strokeStyle = '#000'
ctx.lineWidth = 5
ctx.strokeRect(50, 50, 200, 100)
ctx.restore()

ctx.save()
ctx.fillStyle = '#CCCCCC'
ctx.strokeStyle = '#000'
ctx.lineWidth = 5
ctx.scale(2,2)
ctx.strokeRect(50, 50, 200, 100)
ctx.restore()

运行结果如下:

image.png

我们会发现,进行放大的图形,它的lineWidth也放大了!这时候我们知道了它的另一个副作用,会缩放边框。

总结:

  • scale会缩放起始坐标
  • scale会缩放边的粗细

你可能感兴趣的:(canvas基础)