颜色和样式
通过刚才的例子,我们学会了绘制图形。
但是我们看到,不管是填充还是描边,画出来的都是简单的黑白图形。如果想要指定描绘的内容,画出更丰富的效果应该如何操作呢?
有两个重要的属性可以做到,fillStyle
和 strokeStyle
。顾名思义,分别是为填充和描边指定样式。
颜色
在本章节最初的例子里,其实已经看到上色的基本方法,就是直接用颜色作为指定样式。
ctx.fillStyle = 'rgb(200,0,0)'
ctx.fillRect(20, 20, 200, 200)
一旦设置了 fillStyle
或者 strokeStyle
的值,新值就会成为新绘制的图形的默认值。如果你要给每个图形上不同的颜色,需要画完一种样式的图形后,重新设置 fillStyle
或 strokeStyle
的值。
//填充绘制一个矩形,颜色为暗红色
ctx.fillStyle = 'rgb(200,0,0)'
ctx.fillRect(20, 20, 200, 200)
//描边绘制另一个矩形,边框颜色为半透明蓝色
ctx.strokeStyle = 'rgba(0, 0, 200, 0.5)'
ctx.strokeRect(80, 80, 200, 200)
canvas 的颜色支持各种 CSS 色彩值。
// 以下值均为 '红色'
ctx.fillStyle = 'red' //色彩名称
ctx.fillStyle = '#ff0000' //十六进制色值
ctx.fillStyle = 'rgb(255,0,0)' //rgb色值
ctx.fillStyle = 'rgba(255,0,0,1)' //rgba色值
渐变色
除了使用纯色,还支持使用渐变色。先创建渐变色对象,并将渐变色对象作为样式进行绘图,就能绘制出渐变色的图形。
渐变色对象可以使用 createLinearGradient
创建线性渐变,然后使用 addColorStop
上色。
这里要注意的是,渐变色对象的坐标尺寸都是相对画布的。应用了渐变色的图形实际起到的是类似“蒙版”的效果。
//填充绘制一个矩形,填充颜色为深红到深蓝的线性渐变色
const linGrad1 = ctx.createLinearGradient(0, 0, 300, 300)
linGrad1.addColorStop(0, 'rgb(200, 0, 0)')
linGrad1.addColorStop(1, 'rgb(0, 0, 200)')
ctx.fillStyle = linGrad1
ctx.fillRect(20, 20, 200, 200)
//描边绘制另一个矩形,边框颜色为深蓝到深红的线性渐变色
const linGrad2 = ctx.createLinearGradient(0, 0, 300, 300)
linGrad2.addColorStop(0, 'rgb(0, 0, 200)')
linGrad2.addColorStop(1, 'rgb(200, 0, 0)')
ctx.strokeStyle = linGrad2
ctx.strokeRect(80, 80, 200, 200)
线型
除了颜色,还可以在描边绘制图形的时候,为描边的线条增加线型。
线型可设置的项目包括:
线宽(lineWidth)
顾名思义,线宽就是描边线条的宽度,单位是像素。
这里要注意两点:
线条的宽度会向图形的内部及外部同时延伸,会侵占图形的内部空间。在使用较宽线条时特别需要注意图形内部填充部分是否被过度挤压。常用解决方法可以尝试先描边后填充。
可能会出现的半渲染像素点。例如,绘制一条 (1, 1) 到 (1, 3),线宽为 1px 的线段,是在 x = 1 的位置,向左右各延伸 0.5px 进行绘制。但是由于实际最小绘制单位是一个像素点,那么最终绘制出来的效果将是线宽 2px,但是颜色减半的线段,视觉上看就会模糊。常用解决方法,一种是改用偶数的线宽绘制;另一种可以将线段绘制的起始点做适当偏移,例如偏移至 (1.5, 1) 到 (1.5, 3),左右各延伸 0.5px 后,正好布满一个像素点,不会出现半像素渲染了。
端点样式(lineCap)
端点样式决定了线段端点显示的样子。从上至下依次为 butt
,round
和 square
,其中 butt
为默认值。
这里要注意的是,round
和 square
会使得线段描绘出来的视觉长度,两端各多出半个线宽,可参考蓝色辅助线。
交点样式(lineJoin)
交点样式决定了图形中两线段连接处所显示的样子。从上至下依次为 miter
, bevel
和 round
,miter
为默认值。
交点最大斜接长度(miterLimit)
在上图交点样式为 miter
的展示中,线段的外侧边缘会延伸交汇于一点上。线段直接夹角比较大的,交点不会太远,但当夹角减少时,交点距离会呈指数级增大。
miterLimit
属性就是用来设定外延交点与连接点的最大距离,如果交点距离大于此值,交点样式会自动变成了 bevel
。
示例
ctx.lineWidth = 20
ctx.lineCap = 'round'
ctx.lineJoin = 'bevel'
ctx.strokeRect(80, 80, 200, 200)
使用虚线
用 setLineDash
方法和 lineDashOffset
属性来制定虚线样式。 setLineDash
方法接受一个数组,来指定线段与间隙的交替;lineDashOffset
属性设置起始偏移量。
示例
drawLineDashCanvas () {
const canvas = this.$element('linedash-canvas')
const ctx = canvas.getContext('2d')
let offset = 0
// 绘制蚂蚁线
setInterval(() => {
offset++
if (offset > 16) {
offset = 0
}
ctx.clearRect(0, 0, 300, 300)
// 设置虚线线段和间隙长度 分别为 4px 2px
ctx.setLineDash([4, 2])
// 设置虚线的起始偏移量
ctx.lineDashOffset = -offset
ctx.strokeRect(10, 10, 200, 200)
}, 20)
}
运行效果如下
组合使用
通过学习,我们为刚才绘制的快应用 logo 添加颜色和样式。
drawCanvas () {
const r = 20
const h = 380
const p = Math.PI
const linGrad1 = ctx.createLinearGradient(h, h, 0, 0)
linGrad1.addColorStop(0, '#FFFAFA')
linGrad1.addColorStop(0.8, '#E4C700')
linGrad1.addColorStop(1, 'rgba(228,199,0,0)')
ctx.fillStyle = linGrad1
ctx.fillRect(0, 0, h, h)
const linGrad2 = ctx.createLinearGradient(0, 0, h, h)
linGrad2.addColorStop(0, '#C1FFC1')
linGrad2.addColorStop(0.5, '#ffffff')
linGrad2.addColorStop(1, '#00BFFF')
ctx.beginPath()
ctx.moveTo(r * 2, r)
ctx.arc(r * 2, r * 2, r, -p / 2, -p, true)
ctx.lineTo(r, h - r * 2)
ctx.arc(r * 2, h - r * 2, r, p, p / 2, true)
ctx.lineTo(h - r * 2, h - r)
ctx.arc(h - r * 2, h - r * 2, r, p / 2, 0, true)
ctx.lineTo(h - r, r * 2)
ctx.arc(h - r * 2, r * 2, r, 0, -p / 2, true)
ctx.closePath()
ctx.lineWidth = 10
ctx.strokeStyle = linGrad2
ctx.stroke()
const s = 60
ctx.beginPath()
ctx.moveTo(h / 2 + s, h / 2)
ctx.arc(h / 2, h / 2, s, 0, -p / 2 * 3, true)
ctx.arc(h / 2, h / 2 + s + s / 2, s / 2, -p / 2, p / 2, false)
ctx.arc(h / 2, h / 2, s * 2, -p / 2 * 3, 0, false)
ctx.arc(h / 2 + s + s / 2, h / 2, s / 2, 0, p, false)
ctx.fillStyle = '#4286f5'
ctx.fill()
ctx.beginPath()
ctx.moveTo(h / 2 + s * 2, h / 2 + s + s / 2)
ctx.arc(h / 2 + s + s / 2, h / 2 + s + s / 2, s / 2, 0, p * 2, false)
ctx.fillStyle = 'rgb(234, 67, 53)'
ctx.fill()
ctx.beginPath()
ctx.moveTo(h / 2 + s / 4 * 3, h / 2 + s / 2)
ctx.arc(h / 2 + s / 2, h / 2 + s / 2, s / 4, 0, p * 2, false)
ctx.fillStyle = 'rgba(250, 188, 5, 1)'
ctx.fill()
}
实现效果如下