canvas绘制带文本和圆点的圆环

<template>
  <div :style="style" class="WorkRingChart">
   <canvas class="WorkRingChart-canvas" ref="canvas"></canvas>
  </div>
</template>

<script>
  import resize from '@/mixins/eventResize';
export default {
  name: "WorkRingChart",
  props: {
    options: {
      type: Object,
      default: () => ({})
    },
    width: {
      type: Number,
      default: 0
    },
    height: {
      type: Number,
      default: 0
    },
    align: {
      type: String,
      default: 'left'
    },
    // 内环标签
    innerContent: {
      type: String,
      default: ''
    },
    // 外环标签
    outerContent: {
      type: String,
      default: ''
    },
    // 主标题
    mainTitle: {
      type: String,
      default: ''
    },
    // 副标题
    subTextTitle: {
      type: String,
      default: ''
    },
    // 主标题字体大小
    mainFontSize: {
      type: Number,
      default: 20
    },
    // 副标题字体大小
    subTextFontSize: {
      type: Number,
      default: 15
    },
    innerColor: {
      type: String,
      default: 'black'
    },
    outerColor: {
      type: String,
      default: 'white'
    }
  },
  mixins: [resize],
  watch: {
    options: {
      handler () {
        this.resize();
      },
      deep: true
    }
  },
  computed: {
    style () {
      return {
        width: `${this.options.width}%`,
        height: `${this.options.height}%`
      }
    }
  },
  mounted () {
    window.addEventListener('resize', this.resize);
    this.resize();
  },
  beforeDestroy () {
    window.removeEventListener('resize', this.resize);
  },
  methods: {
    resize() {
      /*
      @drawing_elem: 绘制对象
      @percent:绘制圆环百分比, 范围[0, 100]
      @forecolor: 绘制圆环的前景色,颜色代码
      @bgcolor: 绘制圆环的背景色,颜色代码
      */
      const innerLabel = this.options.innerContent;
      const outerLabel = this.options.outerContent;
      const mainTitle = this.options.mainTitle;
      const subTextTitle = this.options.subTextTitle;
      const drawing_elem = this.$refs['canvas'];
      if (!(drawing_elem && drawing_elem.getContext && JSON.stringify(this.options) !== '{}')) {
        return;
      }
      const { width, height } = this.$el.getBoundingClientRect();
      drawing_elem.width = width;
      drawing_elem.height = height;
      const mainFontSize = this.options.mainFontSize * height / 155;
      const subTextFontSize = this.options.subTextFontSize * height / 155;
      const context = drawing_elem.getContext("2d");
      context.font = `${14 * height / 155}px Helvetica`;
      const billWidth = context.measureText(innerLabel).width;
      const storageWidth = context.measureText(outerLabel).width;
      let textWidth = billWidth >= storageWidth ? billWidth : storageWidth;
      // const center_x = width / 2;
      // 右上文本距离圆环的间距
      const labelDistance = 10;
      // 外半径
      const _radius = (height / 2) >= (width - textWidth - labelDistance) ? (width - textWidth - labelDistance) : (height / 2);
      // 横轴坐标
      const center_x = (height / 2) >= (width - textWidth - labelDistance) ? _radius : (() => {
        if (this.options.align === 'left') {
          return _radius;
        } else if (this.options.align === 'right') {
          return (width - textWidth - labelDistance);
        }
      })();
      // 圆环粗细
      const lineWidth = 6 * height / 155;
      // 圆环间隔
      const circleDistance = 20 * height / 155;
      // 圆环顶端圆球大小
      const ballSize = 6 * height / 155;
      const total = 100;
      let value = 50;

      // 绘制背景圆圈
      function backgroundCircle(radius, lineWidth) {
        context.save();
        context.beginPath();
        context.lineWidth = lineWidth; //设置线宽
        context.lineCap = "round";
        context.strokeStyle = 'rgba(255,255,255,0.05)';
        context.arc(center_x, _radius, radius, 0, Math.PI * 3 / 2, false);
        context.stroke();
        context.closePath();
        context.restore();
      }

      //绘制运动圆环
      function foregroundCircle(n, t, radius, lineWidth, color){
        context.save();
        context.strokeStyle = color;
        context.lineWidth = lineWidth;
        context.lineCap = "round";
        const rotateDeg = Math.PI * 2 * 3 / 4 * n / t;
        context.beginPath();
        context.arc(center_x, _radius, radius,  -Math.PI / 2, rotateDeg, true); //用于绘制圆弧context.arc(x坐标,y坐标,半径,起始角度,终止角度,顺时针/逆时针)
        context.stroke();
        context.closePath();
        context.restore();
        context.beginPath();
        if (rotateDeg > (Math.PI * 3 / 2)) {
          const angle = rotateDeg - Math.PI * 3 / 2;
          context.arc(center_x + Math.sin(angle) * radius, _radius + Math.cos(angle) * radius, ballSize,  0, Math.PI * 2);
        } else if (rotateDeg > Math.PI) {
          const angle = rotateDeg - Math.PI;
          context.arc(center_x - Math.cos(angle) * radius, _radius + Math.sin(angle) * radius, ballSize,  0, Math.PI * 2);
        } else {
          const angle = rotateDeg;
          context.arc(center_x - Math.sin(angle) * radius, _radius - Math.cos(angle) * radius, ballSize,  0, Math.PI * 2);
        }
        context.fillStyle = color;
        context.fill();
        context.closePath();
      }

      //绘制文字
      function text(n, color, radius, lineWidth){
        context.save(); //save和restore可以保证样式属性只运用于该段canvas元素
        const fontSize = 14 * height / 155;
        context.fillStyle = color;
        context.font = `${subTextFontSize}px Helvetica`;
        context.fillText(subTextTitle, center_x - context.measureText(subTextTitle).width/2, _radius);
        context.font = `${mainFontSize}px Helvetica`;
        context.fillText(mainTitle, center_x - context.measureText(mainTitle).width/2, _radius + (20 * height / 155));
        context.font = `${fontSize}px Helvetica`;
        context.fillText(innerLabel, center_x + labelDistance, _radius - radius + (20 * height / 155) + Math.abs(lineWidth / 2 - fontSize) / 2);
        context.font = `${fontSize}px Helvetica`;
        context.fillText(outerLabel, center_x + labelDistance, _radius - radius + Math.abs(lineWidth / 2 - fontSize) / 2);
        context.restore();
      }
      if ((_radius - circleDistance - lineWidth) < 0) {
        return;
      }
      context.clearRect(0, 0, width, height);
      backgroundCircle(_radius - lineWidth, lineWidth);
      foregroundCircle(value, total, _radius - lineWidth, lineWidth, this.options.outerColor);
      text(value, 'white', _radius - lineWidth, lineWidth);
      backgroundCircle(_radius - circleDistance - lineWidth, lineWidth);
      foregroundCircle(value, total, _radius - circleDistance - lineWidth, lineWidth, this.options.innerColor);
      //执行动画
//       (function drawFrame(){
//         window.requestAnimationFrame(drawFrame);
//         context.clearRect(0, 0, width, height);
//         backgroundCircle();
//         text(speed);
//         foregroundCircle(speed);
//         if(speed >= percent) return;
//         speed += 1;
//       }());
    }
  }
}
</script>

<style lang="scss" scoped>
.WorkRingChart {
  display: inline-block;
  &-canvas {
  }
}
</style>

posted @ 2020-06-16 16:48  代码男孩  阅读(702)  评论(0编辑  收藏  举报