count-circle.vue 3.79 KB
<template>
  <div class="canvas-container" ref="canvasContainer">
    <canvas ref="countCanvas"
            id="countCircleCanvas"
            :width="canvasWidth"
            :height="canvasHeight"
            :time="time"
            :totalTime="totalTime"
            :lineWidth="lineWidth"
    ></canvas>
  </div>

</template>

<script>

/**
 * 使用方法, 在对应页面引入CountCircle
 * <CountCircle :line-width="6" :time="time" :total-time="totalTime" @on-end="onTimeEnd" style="width: 70px; height: 70px; display: block;"></CountCircle>
 * 在自己的页面做定时器,修改time,
 */
export default { // 环形倒计时
  name: 'CountCircle',
  props: {
    time: {
      type: Number,
      default: 60000
    },
    totalTime: {
      type: Number,
      default: 60000
    },
    lineWidth: {
      type: Number,
      default: 8
    },
  },
  data() {
    return {
      canvas: null,
      context: null,
      interval: 100,
      circle: {
        x: 0,
        y: 0,
        radius: 0
      },
      canvasWidth: 200,
      canvasHeight: 200,
      pixelRatio: 2,
    };
  },
  mounted() {
    this.resetCanvas();
    this.draw();
  },
  computed: {
    canvasLineWidth() {
      return this.lineWidth * this.pixelRatio;
    }
  },
  methods: {
    resetCanvas() {
      this.canvas = this.$refs.countCanvas;
      if (window.devicePixelRatio) {
        this.pixelRatio = window.devicePixelRatio;
      }

      this.canvasWidth = this.$refs.canvasContainer.getBoundingClientRect().width * this.pixelRatio || 200;
      this.canvasHeight = this.$refs.canvasContainer.getBoundingClientRect().height * this.pixelRatio || 200;
      this.context = this.canvas.getContext('2d');

      this.circle = {
        x: this.canvasWidth / 2,
        y: this.canvasHeight / 2,
        radius: this.canvasWidth / 2
      };
    },
    drawCircle() {
      if (this.canvas && this.context) {
        let ctx = this.context;

        ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
        ctx.save();
        ctx.beginPath();
        ctx.strokeStyle = 'rgba(2, 42, 71, 1)';
        ctx.lineCap = 'round';
        ctx.lineWidth = this.canvasLineWidth - 2;
        ctx.arc(this.circle.x, this.circle.y, (this.circle.radius - this.canvasLineWidth / 2), -Math.PI / 2, (Math.PI + Math.PI / 2), false);
        ctx.stroke();
        ctx.closePath();
        ctx.restore();
      }
    },
    strokeCircle(percent) {
      if (this.canvas && this.context) {
        this.drawCircle();

        let ctx = this.context;

        if (percent > 0) {
          ctx.save();
          ctx.strokeStyle = '#f0f0f0';
          ctx.lineCap = 'square';
          ctx.lineWidth = this.canvasLineWidth;

          ctx.beginPath();
          ctx.arc(this.circle.x, this.circle.y, (this.circle.radius - this.canvasLineWidth / 2), -Math.PI / 2, Math.PI * ( 2 * percent - 0.5), false);
          ctx.stroke();
          ctx.closePath();
          ctx.restore();
        }

        ctx.save();
        ctx.font = 'bold ' + 20 * this.pixelRatio + 'px "ufofont"';
        ctx.fillStyle = '#022A47';
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';
        ctx.fillText(Math.round(this.time / 1000) + 's', this.circle.x, this.circle.y);
        ctx.restore();

        setTimeout(this.draw.bind(this), this.interval);
      }
    },
    draw() {
      if (this.time > 0) {
        let percent = 1 - this.time / this.totalTime;

        this.strokeCircle(percent);
      } else {
        this.$emit('on-end');
      }
    }
  }
};
</script>

<style lang="scss" scoped>
  .canvas-container {
    display: block;
    width: 100%;
    margin: 44px auto;

    canvas {
      position: relative;
      width: 100%;
      height: 100%;
    }

    p {
      text-align: center;
      font-weight: 600;
      font-size: 28px;
      white-space: nowrap;
    }
  }

</style>