The requirements to be fulfilled are as follows:
(1) Graphic similar to fuel gauge
(2) When the page is first opened, after the data is obtained, the fuel quantity indicator (green section) and the middle number must have an animation, starting from 0.
The first choice, of course, is to give a canvas:
<div class="main-wrapper">
<canvas id="canvas" width="360" height="350"></canvas>
</div>
The drawing function is as follows:
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
}
}
Then add some content to the life cycle of 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) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。