canvas绘制水球图
分析准备
分析整个水球图,波形的绘制就是整个绘制过程的最大的难点。绘制波形,第一个难点就是波纹的形状,其实可以选择两种方案绘制,第一种,采取圆弧拼接的形式;第二种就是采用三次贝塞尔曲线来绘制曲线。为了再次熟悉三次贝塞尔曲线的绘制过程,所以采用了方式二。波形的绘制难点二就是如何使得波形绘制在圆内,此处便要引入clip
、save
、restore
三个函数的使用了,其详细情况可见MDN文档。
绘制水球图的第二个难点,就是如何绘制水漫文字的效果。水漫文字的效果我们观察发现,未被水漫部分则为最上层颜色,否则为白色。所以,我们就可以考虑先绘制一个带底色的文字,之后再利用裁剪效果,来绘制被水漫的白色部分。
- 先绘制一个带底色的文字
clip
裁剪后在相同位置绘制一个相同的白色文字
我们总共绘制了三层水波纹,在每层绘制水波纹的时候都需要绘制白色部分,否则后面的谁的波纹会将文字盖住。但是带底色的文字只需要在最下层波纹绘制的时候绘制即可。
最后,在水球图绘制完成后,我们会发现圆圈外侧并不光滑,成锯齿状,此时,我们则需要使用canvas的抗锯齿技巧,其实抗锯齿很简单,就像大家在在凑很近看墙面时候,你可能发现墙面是坑坑洼洼的,但是站的远,就可能觉得墙面很平整。因为站的远就看不清细节,我们脑子机自动的认为墙面平整了,抗锯齿也可以利用一样的原理,只要看不清真实边缘,那么边缘则看起来就比较平滑了。所以要用到阴影来模糊掉边缘即可,需要使用到shadowColor
shadowBlur
shadowOffsetX
shadowOffsetY
这几个属性。
代码示例
function WaterCahrt(id, data) {
let canvas = document.getElementById(id)
let ctx = canvas.getContext('2d')
let canvasWidth = canvas.width > canvas.height ? canvas.height : canvas.width
let r = canvasWidth * 0.8 / 2
let offset = [0, 20, 40]
this.canvas = canvas
this.ctx = ctx
this.data = data
this.r = r // 圆半径
this.center = canvasWidth / 2 //圆心x(y)
this.intervalId = null
Object.defineProperty(this, 'offset', { // 定义水波图的偏移量,当偏移量发生变化时自动触发更新
enumerable: false,
configurable: false,
get() {
return offset
},
set(val) {
offset = val
this.refresh()
}
})
}
此处offset设置成了个数组,是为了使三个水波的移动速度具有一定差距。
/**
* 绘制水波图
* @param {number} basex 波纹的起始x值
* @param {number} basey 波纹的起始点y值
* @param {strign} color 波纹颜色
* @param {boolean} flag 是否绘制与波纹相同颜色的文字,理论上只需要最下层的波纹才需要绘制
*/
WaterCahrt.prototype.drawWater = function(basex, basey, color, flag) {
let bezier = this.getBezierPoints(basex, basey) // 生成三次贝塞尔曲线
this.ctx.save() // 存储画笔状态
// 绘制波形
this.ctx.beginPath()
this.ctx.moveTo(this.center - this.r, this.center + this.r)
this.ctx.lineTo(bezier.x, bezier.y)
bezier.points.forEach((item) => {
this.ctx.bezierCurveTo(item.cp1x, item.cp1y, item.cp2x, item.cp2y, item.x, item.y)
})
this.ctx.lineTo(this.center + this.r, this.center + this.r)
this.ctx.closePath()
this.ctx.fillStyle = color
this.ctx.shadowColor = color //抗锯齿
this.ctx.shadowBlur = 2
this.ctx.fill()
this.ctx.shadowBlur = 0
//绘制带底色文字
this.ctx.font = `normal ${this.r * 0.2}px blod`
this.ctx.textAlign = "center"
if(flag)
this.ctx.fillText(`${(this.data * 100).toFixed(1)}%`, this.center, this.center)
// 绘制文字白色部分
this.ctx.clip() // 按照所绘路径裁剪
this.ctx.fillStyle = '#ffffff'
this.ctx.fillText(`${(this.data * 100).toFixed(1)}%`, this.center, this.center)
this.ctx.restore() // 恢复画笔状态,即到未裁剪之前状态
return this
}
三次贝塞尔曲线的点的生成不在赘述,详细生成原理可参考文章《贝塞尔曲线控制点确定的方法》。
总结
此次水球图的绘制,主要是为了对clip、save、restore相关的方法的尝试,同时还引入了抗锯齿的一个小技巧。整体绘制上计算量相对并不大,且无交互效果,只是存在一个动画效果,动画效果的实现则是使用setInterval间隔绘制动画帧即可。整体上来说并没有太大的难度。
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!