要实现的需求如下:
(1)类似于油量计的图形
(2)刚打开页面时,获取数据后,油量指标(绿色区段)和中间数字,都要有动画,从0开始。
首选,当然是要给出画布啦:
<div class="main-wrapper">
<canvas id="canvas" width="360" height="350"></canvas>
</div>
绘制函数如下:
drawCanvas () {
const bodyWidth = document.body.clientWidth || window.innerWidth
// 获取初始值
const canvas = document.getElementById('canvas')
const ctx = canvas.getContext('2d')
// 解决清晰度问题,让画布宽度等于屏幕宽度,高度等比缩放
let ratio = window.devicePixelRatio || 1
canvas.height = canvas.height / canvas.width * bodyWidth
canvas.width = bodyWidth
canvas.style.width = canvas.width + 'px'
canvas.style.height = canvas.height + 'px'
canvas.width = canvas.width * ratio
canvas.height = canvas.height * ratio
// 解决清晰度问题
const cWidth = canvas.width
const cHeight = canvas.height
const radius = 165
const deg0 = Math.PI / 18
// 设置油量计的最大阈值,根据实际需要设置
const maxStep = 10000
// 当前的数据值
let score = nowScore
// 数据值根据阈值做处理
score = score < 0 ? 0 : (score > maxStep ? maxStep : score)
ctx.save()
ctx.clearRect(0, 0, cWidth, cHeight)
ctx.translate(cWidth / 2, cHeight / 2)
ctx.rotate(13 * deg0)
this.drawFrame(ctx, cWidth, cHeight, score, radius, deg0, maxStep, ratio)
},
drawFrame (ctx, cWidth, cHeight, score, radius, deg0, maxStep, ratio) {
ctx.save()
ctx.clearRect(-cWidth / 2, -cHeight / 2, cWidth, cHeight) // 上面旋转后中心点变了,取反
// 底层大圆
ctx.save()
ctx.scale(ratio, ratio)// 解决清晰度问题
ctx.beginPath()
ctx.lineWidth = 12
ctx.strokeStyle = '#E1DDF5'
ctx.arc(0, 0, radius, 0, 28 * deg0, false)
ctx.lineCap = 'round'
ctx.stroke()
ctx.restore()
// 细分刻度线
ctx.save()
ctx.scale(ratio, ratio)// 解决清晰度问题
for (var i = 0; i < 29; i++) {
ctx.beginPath()
ctx.lineWidth = 4
ctx.strokeStyle = '#9C9EB9'
ctx.moveTo(148, 0)
ctx.lineTo(140, 0)
ctx.stroke()
ctx.rotate(deg0)
}
ctx.restore()
// 中央文字的动画 this.scoreStep是当前帧的进度值
ctx.save()
ctx.scale(ratio, ratio)// 解决清晰度问题
ctx.rotate(23 * deg0)
ctx.textAlign = 'center'
ctx.fillStyle = '#4F4F4F'
ctx.font = '28px PingFangSC-Regular'
ctx.fillText('新增', 0, -30)
ctx.fillStyle = '#2D3142'
ctx.font = '64px Helvetica'
ctx.fillText(this.scoreStep, 0, 30)
ctx.fillStyle = '#4F4F4F'
ctx.font = '16px PingFangSC-Regular'
var tip = `总计:228步`
ctx.fillText(tip, 0, 130)
ctx.restore()
// 内层轨道(绿色区段, 重点)
var angleTo = this.scoreStep / maxStep * 28 * deg0
var xTo = radius * Math.cos(angleTo)
var yTo = radius * Math.sin(angleTo)
var g = ctx.createLinearGradient(0, 0, xTo, yTo)
g.addColorStop(0, '#21D876')
g.addColorStop(1, '#96ED44')
ctx.save()
ctx.scale(ratio, ratio)// 解决清晰度问题
ctx.beginPath()
ctx.strokeStyle = g
ctx.lineWidth = 12
ctx.arc(0, 0, radius, 0, angleTo, false)
ctx.lineCap = 'round'
ctx.stroke()
ctx.restore()
if (this.timerID && this.isDone) {
// 在适当的时机要结束掉动画
window.cancelAnimationFrame(this.timerID)
}
// 设置动画的速率
const step = 500
if (this.scoreStep + step < score) {
this.scoreStep += step
} else if (this.scoreStep + step >= score) {
this.scoreStep = score
}
let _this = this
if (!this.isDone) {
// 本次渲染结束后立马进行下一帧绘制
this.timerID = window.requestAnimationFrame(function () {
_this.drawFrame(ctx, cWidth, cHeight, score, radius, deg0, maxStep, ratio)
})
}
if (this.scoreStep === score) {
this.isDone = true
}
}
然后在vue的生命周期中要加一些内容
mounted () {
window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame
window.cancelAnimationFrame = window.cancelAnimationFrame || window.mozCancelAnimationFrame || window.webkitCancelAnimationFrame || window.msCancelAnimationFrame
this.drawCanvas()
setTimeout(() => {
if (this.timerID) {
window.cancelAnimationFrame(this.timerID)
}
}, 10000)
},
beforeDestroy () {
if (this.timerID) {
window.cancelAnimationFrame(this.timerID)
}
},
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。