canvas实现环形渐变进度条(vue)

在这里插入图片描述
1.画板部分

<!--画布尺寸的控制-->
    <div class="canvas_area" ref="canvasArea">
        <canvas :id="id" width="208px" height="208px"></canvas>
    </div>

2.css样式

<style lang="less" scoped>
    .total {
        width: 100vw;
        height: 100vh;
        background-color: rgb(244, 244, 244);
        /*background-color: #ffffff;*/
    }

    .bg_par {
        background-color: rgb(244, 244, 244);
    }
    .canvas_area{
        /* transform 缩小后会有占位情况*/
        /*transform:scale(0.5, 0.5);*/
        /*-webkit-transform:scale(0.5, 0.5);  !*兼容-webkit-引擎浏览器*!*/
        /*-moz-transform:scale(0.5, 0.5);*/
        display: flex;
        align-items: center;
        justify-content: center;
        zoom: 0.55;
        canvas{
            background-color: transparent;
        }
    }

</style>

3.js部分

<script>
export default {
  ...
  //涉及到的一些变量
  data () {
    return {
      title: '环形进度条',
      canvas: '',
      cacheCanvas: '',
      keeprate: ' ',该进度条的数值   //可根据需要自行调整
      id: '', // 设置画布id
      context: '',
      cirX: 0, // 圆心x坐标
      cirY: 0, // 圆心y坐标
      cirR: 0, // 圆半径
      ratio: '', // 绘制圆环占整个圆的比例  百分比小数
      rad: 0,
      ballR: 8, // 填充小球半径
      timer: '',
      tempRatio: 0,
      speed: 1, // 加载速度
      width: '', // 画布宽高
      height: '',
      rate: '',
      speed2: 1.5 // 减速
    }
  }
  ...
  mounted () {
    // 进行数据初始化的一些处理
    /* 进行弧度计算 将360度分成100份,那么每一份就是rad度 */
    this.rad = (Math.PI * 2) / 100
    console.log('ratio' + this.ratio)
    // 获取画布的宽 高
    this.width = document.getElementById(this.id).offsetWidth
    this.height = document.getElementById(this.id).offsetHeight
    // 设置圆心
    this.cirX = this.width / 2
    this.cirY = this.height / 2
    // 设置圆的半径
    this.cirR = 87
    this.initCanvas()
  },
  ...
  methods: {
    initCanvas () { 
      ...此处省略部分逻辑代码... 
      if (!this.cacheCanvas) {
        this.cacheCanvas = document.createElement('canvas')
        this.cacheCanvas.width = this.width
        this.cacheCanvas.height = this.height
      }
      this.context = this.cacheCanvas.getContext('2d')
      this.context.save()
      this.context.clearRect(0, 0, this.canvas.width + 1, this.canvas.height + 1) // 部分Android机器很奇葩,如果局部刷新会出现空白的情况
      this.drawBottomCircle() // 绘制
      this.context.restore()

      // 双缓冲,先画到临时canvas,再转到正式canvas
      // 获取到对应的canvas元素
      this.canvas = document.getElementById(this.id)
      // 创建 context 对象
      this.context = this.canvas.getContext('2d')
      this.context.clearRect(0, 0, this.canvas.width + 1, this.canvas.height + 1)
      this.context.drawImage(this.cacheCanvas, 0, 0, this.canvas.width, this.canvas.height)
  
      if (this.rate > 0) {
        window.requestAnimationFrame(this.drawFrame, this.canvas)
      } else {
        // 如果该进度值不存在的情况下则只显示底环和文字
        this.drawBottomCircle()
        this.fillTextContent('--')
      }
    },
    /* 绘制底层圆环 */
    drawBottomCircle () {
      // 开始一个新的绘制路径
      this.context.beginPath()
      // 设置弧线颜色
      this.context.strokeStyle = 'rgb(254,238,225)'
      this.context.lineWidth = '8'
      this.context.arc(this.cirX, this.cirY, this.cirR, 0, 2 * Math.PI, false)
      // 按指定路径绘制弧线
      this.context.stroke()
      this.context.closePath()
    },
    /* 绘制上层填充路径 */
    drawTopFillPath (n) {
      if (this.rate >= 0) {
        // 处理某些度数时效果无法正常显示 属于优化,占位
        n = Math.floor(n)
        if (n > 50) n = n - 3
        // 圆环起始位置,正下方
        let startAngle = -1.375 * Math.PI / 2
        // 圆环结束位置,一个整圆(Math.PI*2)乘以比例 + 起始位置
        let endAngle = -1.375 * Math.PI / 2 + n * (this.rad)
        // 每次绘制的弧度单位,越小颜色分布越均匀,但图形较小时边缘越糙
        const unit = 0.05
        // 根据最小弧度单位计算整个圆弧可以切成多少小份
        let division = parseInt((endAngle - startAngle) / unit, 10)
        // 生成渐变色数组  此处传的值为渐变的两个颜色的十六进制,如果取到的是rgb,可
        //以通过下面的colorHex() 方法进行转化
        let gradient = this.gradientColor([255, 187, 165], [255, 89, 38], division)

        let start = startAngle
        let end = start
        for (let i = 0; i < division; i++) {
          this.context.beginPath()
          this.context.lineCap = 'round'
          end = start + unit
          this.context.lineWidth = '22'
          this.context.strokeStyle = gradient[i]
          this.context.arc(this.cirX, this.cirY, this.cirR, start, end)
          this.context.stroke()
          start += unit
        }
      }
    },
    /* 绘制上层填充小球 */
    drawTopFillBall () {
      if (this.rate > 0) {
        // 填充小球颜色  rgb(203,215,253)
        this.context.fillStyle = '#ffffff'
        // 计算小球位置
        this.context.beginPath()
        let rad = 247.5
        // 坐标微调
        let x = Math.cos(rad) * this.cirR + 21
        let y = Math.sin(rad) * this.cirR + 19
        this.context.arc(x + this.cirX, this.cirY - y, this.ballR, 0, 2 * Math.PI)
        // this.context.arc(x + this.cirX, this.cirY - y, this.ballR, 0, 2 * Math.PI)
        this.context.closePath()
        this.context.fill()
      }
    },
    /* 进行文字填充 */
    fillTextContent (n) {
      if (n !== '--') {
        n = n.toFixed(2) + '%'
      }
      // this.context.beginPath()
      this.context.fillStyle = 'rgb(255,87,34)'
      this.context.font = '38px ArialMT'
      this.context.textAlign = 'center'
      this.context.textBaseline = 'middle'
      this.context.fillText(n, this.cirX, this.cirY - 12)
      this.context.fillStyle = 'rgb(153,153,153)'
      this.context.font = '24px HiraginoSansGB-W3'
      this.context.fillText('进度', this.cirX, this.cirY + 37)
    },
    drawFrame () {
      window.requestAnimationFrame(this.drawFrame, this.canvas)
      this.context.clearRect(0, 0, this.width, this.height)
      // 绘制底层圆环
      this.drawBottomCircle()
      // 绘制上层填充轨迹
      this.drawTopFillPath(this.tempRatio)
      // 绘制上层填充小球
      this.drawTopFillBall()
      // 进行文字填充  此处可以添加自己的逻辑处理
      ....
      this.fillTextContent(this.tempRatio) 
      //进行动画的加速减速控制 自行调整  通过对speed变量的控制实现加速减速
      ...此处可以添加自己的逻辑处理...
    },
    //  startColor:开始颜色hex  endColor:结束颜色hex   step:几个阶级(几步)
    gradientColor (startColor, endColor, step) {
      // let startRGB = this.colorRgb(startColor)// 转换为rgb数组模式
      let startR = startColor[0]
      let startG = startColor[1]
      let startB = startColor[2]

      // let endRGB = this.colorRgb(endColor)
      let endR = endColor[0]
      let endG = endColor[1]
      let endB = endColor[2]

      let sR = (endR - startR) / step// 总差值
      let sG = (endG - startG) / step
      let sB = (endB - startB) / step

      var colorArr = []
      for (var i = 0; i < step; i++) {
        // 计算每一步的hex值
        var hex = this.colorHex('rgb(' + parseInt((sR * i + startR)) + ',' + parseInt((sG * i + startG)) + ',' + parseInt((sB * i + startB)) + ')')
        colorArr.push(hex)
      }
      return colorArr
    },
    // rgb转化为十六进制的方法
    colorHex (rgb) {
      var _this = rgb
      var reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/
      if (/^(rgb|RGB)/.test(_this)) {
        var aColor = _this.replace(/(?:(|)|rgb|RGB)*/g, '').split(',')
        var strHex = '#'
        for (var i = 0; i < aColor.length; i++) {
          var hex = Number(aColor[i]).toString(16)
          hex = hex < 10 ? 0 + '' + hex : hex// 保证每个rgb的值为2位
          if (hex === '0') {
            hex += hex
          }
          strHex += hex
        }
        if (strHex.length !== 7) {
          strHex = _this
        }
        return strHex
      } else if (reg.test(_this)) {
        var aNum = _this.replace(/#/, '').split('')
        if (aNum.length === 6) {
          return _this
        } else if (aNum.length === 3) {
          var numHex = '#'
          for (let i = 0; i < aNum.length; i += 1) {
            numHex += (aNum[i] + aNum[i])
          }
          return numHex
        }
      } else {
        return _this
      }
    }
  }
}
</script>
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值