什么是 canvas
- HTML5
<canvas>
元素用于图形的绘制,通过脚本 (通常是JavaScript)来完成; - 你可以通过多种方法使用 canvas 绘制路径、矩形、圆、字符以及添加图像
使用 canvas
创建画布
1 | // 第一步:创建 canvas 元素 |
上下文的属性和方法
canvas 是一个二维网格。左上角坐标为 (0,0)
填充和描边
填充和描边是两个基本操作。填充,就是用指定的样式(颜色、渐变、图像)填充图形;描边,就是只在图形的边缘描线。这两个属性的值可以是字符串,渐变对象或模式对象,默认值为#000
。
ctx.fillStyle = ""
填充ctx.strokeStyle = ""
描边ctx.lineWidth = 2
描边线条的宽度ctx.lineCap = ""
描边线条末端的形状,平头(butt)、圆头(round)、方头(square)ctx.lineJoin = ""
控制线条交接的方式,圆交(round)、斜交(bevel)、斜接(miter)
阴影
2D 上下文可以用以下几个值,为形状和路径绘制出阴影:
ctx.shadowColor = ""
用css颜色格式表示的阴影颜色,默认黑色。ctx.shadowOffsetX = ""
形状或路径 x 轴方向的阴影偏移量,默认 0ctx.shadowOffsetY = ""
形状或路径 y 轴方向的阴影偏移量,默认 0ctx.shadowBlur = ""
模糊的像素数,默认 0
渐变
渐变由CanvasGradient
实例表示。
ctx.createLinearGradient(startX,startY,endX,endY)
创建一个线性渐变ctx.createRadialGradient(startX,startY,radius1,endX,endY,radius2)
创建一个径向渐变。前三个参数是起点圆的圆心和半径;后三个参数是终点圆的圆心和半径gradient.addColorStop(pos,color)
添加色标。色标位置pos
,色标位置是一个 0-1 之间的数字。css颜色值color
1 | // 1. 首先,我们要创建一个线性渐变。 |
值得注意的是,如果要让渐变覆盖到整个矩形,矩形和渐变对象的
坐标必须匹配。否则可能只显示部分效果。因此我们可以用矩形的宽高去反推渐变坐标
模式
模式其实就是重复的图像,可以用来填充或者描边图形。与渐变类似,我们需要用createPattern()
方法去创建一个新模式
ctx.createPattern(img, pattern)
创建一个新模式- 可以使一个
<img>
元素、<video>
元素或者<canvas>
元素 - 和一个表示如何重复图像的字符串,与
background-repeat
属性值相同:repeat
repeat-x
repeat-y
no-repeat
- 可以使一个
1 | var image = document.images[0], |
模式和渐变一样,都是从画布的原点开始的。然后将填充样式(fillStyle)设置为模式对象,只表示在某个特定的区域内显示重复的图像,而不是从某个位置开始绘制重复的图像
绘制路径
要绘制路径时, 首先必须调用ctx.beginPath()
方法,表示要开始绘制新路径。然后再调用下面方法。
ctx.moveTo(x,y)
将绘图游标移动到(x,y)
,不划线ctx.lineTo(x,y)
从上一点开始绘制一条直线,到(x,y)
为止ctx.arc(x,y,radius,startAngle,endAngle,counterclockwise)
以(x,y)
为圆心绘制一条弧线,弧线半径为radius
,起始和结束角度(弧度)为startAngle
和endAngle
,最后一个参数表示是否按逆时针方向计算,值为 false。ctx.arcTo(x1,y1,x2,y2,radius)
从上一点开始绘制一条弧线,到(x2,y2)
为止,并且以给定的半径radius
穿过(x1,y1)
ctx.bezierCurveTo(c1x,c1y,c2x,c2y,x,y)
从上一点开始绘制一条曲线(贝塞尔曲线),到(x,y)
为止,并且以(c1x,c1y)
,c2x,c2y
为控制点ctx.quadraticCurveTo(cx,cy,x,y)
从上一点开始绘制一条二次曲线,到(x,y)
为止,并且以(cx,cy)
作为控制点ctx.rect(x, y, width, height)
从点(x,y)
开始绘制一个矩形。这个方法绘制的是矩形路径,而不是strokeRect()
和fillRect()
所绘制的独立的形状ctx.isPointInPath(x,y)
用于在路径被关闭前确定画布上的某一点(x,y)
是否位于路径上
创建路径后, 接下来可能有以下几种选择:
- 如果想绘制一条连接到起点的线条(闭环),可以调用
ctx.closePath()
- 如果路径已经完成,你想用
fillStyle
填充它,可以调用ctx.fill()
;当然你想用strokeStyle
进行描边,可以调用ctx.stroke()
- 还可以调用
ctx.clip()
,可以在路径上创建一个剪切区域
绘制文本
绘制文本的方法:
ctx.fillText(text,x,y,width)
使用fillStyle
属性绘制文本ctx.strokeText(text,x,y,width)
使用strokeStyle
属性绘制文本ctx.measureText(text)
该方法返回一个TextMetrics
对象,用以计算文本大小。目前只有width
属性。
其中
fillText
和strokeText
的第四个参数代表文本的最大像素宽度,提供这个参数后,如果传的字符的宽度超出最大像素宽,则绘制的字体在高度上正确,但宽度会收缩至最大像素宽以内。
绘制文本的基础属性:
ctx.font = ""
表示文本样式、大小和字体,与css
中font
一致ctx.textAlign = ""
表示文本对齐方法。值有start
,end
,center
,left
,right
ctx.textBaseline = ""
表示文本的基线。可能的值有top
,hanging
,middle
,alphabetic
,ideographic
,bottom
绘制矩形
矩形是唯一一种可以直接在 2D 上下文中绘制的形状。
这几个方法都能接受四个参数:x 左边,y 坐标,矩形 width,矩形 height。单位都是像素。
ctx.fillRect()
绘制的矩形会填充指定颜色,颜色通过fillStyle
属性指定ctx.strokeRect()
绘制的矩形会使用指定的颜色描边,颜色通过strokeStyle
属性指定ctx.clearRect()
用于清除画布上的矩形区域,即变透明
变换
ctx.rotate(angle)
围绕原点旋转图像angle
弧度ctx.scale(scaleX,scaleY)
缩放图像,在 x 方向乘以scaleX
,在 y 方向上乘以scaleY
,默认都是 1ctx.translate(x,y)
将坐标原点移动到(x,y)
。执行这个变换后,坐标(0,0)
会变成由之前(x,y)
表示的点。ctx.transform(m1_1, m1_2, m2_1, m2_2, dx, dy)
直接修改变换矩阵,方式是如下矩阵ctx.setTransform(m1_1, m1_2, m2_1, m2_2, dx, dy)
将变换矩阵重置为默认状态,然后再调用transform()
对于上面的变换,以及fillStyle
strokeStyle
等属性,都会在当前上下文中一直有效,除非修改。虽然没有方法把上下文一切都重置回默认值,但有另个方法可以追踪变化。
ctx.save()
会把当前的所有设置保存进入一个栈结构。需要注意的是,保存的是对绘图上下文的设置和变换,不保存上下文内容ctx.restore()
在保存设置的栈结构中向前返回一级,回复之前的状态
绘制图像
2D 绘图上下文内置了对图像的支持。我们可以使用drawImage()
方法在画布上绘制图像、画布或视频。一共可以传入 9 个参数,使用这个方法,有三种不同的参数组合方式。
ctx.drawImage(img,oX,oY)
在画布上绘制图像并定位(原始宽高)ctx.drawImage(img,oX,oY,oW,oH)
在画布上绘制图像并定位,并且设置图像的宽高ctx.drawImage(img,oX,oY,oW,oH,tX,tY,tW,tH)
裁剪图像后定位至目标位置并设置宽高。- 要绘制的图像
img
, - 开始剪切的 x 坐标位置
oX
,开始剪切的 y 坐标位置oY
, - 被剪切图像的宽度
oW
,被剪切图像的高度oH
, - 在画布上放置图像的 x 坐标位置
tX
,在画布上放置图像的 y 坐标位置tY
, - 要使用的图像的宽度
tW
,要使用的图像的高度tH
- 要绘制的图像
导出图像
canvas.toDataURL('image/png')
导出图像,接受一个参数,即图像的 MIME 类型。这个方法是canvas
元素上的方法。
1 | var drawing = document.getElementById("drawing"); |
使用图像数据
2D 上下文有一个很好的用法,就是可以通过getImageData()
取得原始图像数据。
ctx.createImageData()
创建一个ImageData
对象createImageData(width,height)
创建了一个新的具体特定尺寸的ImageData对象。所有像素被预设为透明黑createImageData(anotherImageData)
创建一个被anotherImageData
对象指定的相同像素的ImageData
对象,这个新的对象像素全部被预设为透明黑(并非复制)
ctx.getImageData(x,y,width,height)
该方法返回的对象是一个ImageData
的实例。- 每个对象都有三个属性:
width
height
data
。其中data
是一个数组,保存着每一个像素的数据。 - 在
data
数组中,一个像素用四个元素保存,分别表示红data[0]
、绿data[1]
、蓝data[2]
、透明值data[3]
- 每个对象都有三个属性:
ctx.putImageData(myImageData, dx, dy)
在场景中写入像素数据
能够直接访问到原始图形数据,就能以各种方式来操作这些数据。比如,像下面这样创建一个简单的灰阶过滤器。
1 | var drawing = document.getElementById("drawing"); |
合成
2D 上下文中还有两个会应用到所有绘制操作的属性:globalAlpha
和 globalCompositionOperation
ctx.globalAlpha = ""
用于指定所有绘制的透明度(0-1),默认 0- 如果后续所有操作都要基于相同的透明度,就可以把
globalAlpha
设置为合适值,然后绘制。最后再把它设置回默认 0
- 如果后续所有操作都要基于相同的透明度,就可以把
ctx.globalCompositionOperation = ""
表示后绘制的图形怎么与先绘制的图形结合。这个属性的值是字符串source-over
:默认值。后绘制的图形位于先绘制图形上方。source-in
:后绘制的图形与先绘制的图形重叠部分可见,其他地方完全透明source-out
:后绘制的图形与先绘制的图形不重叠的部分可见,先绘制的图形完全透明。source-atop
:后绘制的图形与先绘制的图形重叠部分可见,先绘制的图形不受影响destination-over
:后绘制的图形位于先绘制的图形下方destination-in
:后绘制的图形位于先绘制的图形下方,不重叠的地方完全透明destination-out
:后绘制的图形擦除与先绘制的的图形重叠的部分destination-atop
:后绘制的图形位于先绘制的图形下方,在两者不重叠的地方,先绘制的图形会变透明。lighter
:后绘制的图形与先绘制的图形重叠部分的值相加,使该部分变亮copy
:后绘制的图形完全替代与之重叠的先绘制的图形xor
:后绘制的图形与先绘制的图形重叠的部分执行“异或”操作
WebGL
对于 webgl 暂时不做讨论