3

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.
image.png

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)
    }
  },

穿职业装的程序媛
32 声望4 粉丝