|
|
<template>
|
|
|
<div class="canvas-container" ref="canvasContainer">
|
|
|
<canvas ref="countCanvas"
|
|
|
id="countCircleCanvas"
|
|
|
:width="canvasWidth"
|
|
|
:height="canvasHeight"
|
|
|
:time="time"
|
|
|
:lineWidth="lineWidth"
|
|
|
></canvas>
|
|
|
<p>{{desc}}</p>
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
|
|
|
/**
|
|
|
* 使用方法, 在对应页面引入CountCircle
|
|
|
* <CountCircle :line-width="6" :time="40000" @on-end="onTimeEnd" style="width: 70px; height: 70px; display: block;"></CountCircle>
|
|
|
*/
|
|
|
export default { // 环形倒计时
|
|
|
name: 'count-circle',
|
|
|
props: {
|
|
|
time: {
|
|
|
type: Number,
|
|
|
default: 60000
|
|
|
},
|
|
|
lineWidth: {
|
|
|
type: Number,
|
|
|
default: 8
|
|
|
},
|
|
|
desc: {
|
|
|
type: String,
|
|
|
default: '正在支付中...'
|
|
|
}
|
|
|
},
|
|
|
data() {
|
|
|
return {
|
|
|
canvas: null,
|
|
|
context: null,
|
|
|
totalTime: 60000,
|
|
|
lastTime: 60000,
|
|
|
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.totalTime = this.time;
|
|
|
this.lastTime = this.time;
|
|
|
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
|
|
|
};
|
|
|
console.log(this.canvas, this.circle);
|
|
|
},
|
|
|
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;
|
|
|
|
|
|
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.lastTime / 1000) + 's', this.circle.x, this.circle.y);
|
|
|
ctx.restore();
|
|
|
|
|
|
setTimeout(this.draw.bind(this), this.interval);
|
|
|
}
|
|
|
},
|
|
|
draw() {
|
|
|
if (this.lastTime > 0) {
|
|
|
this.lastTime -= this.interval;
|
|
|
let percent = 1 - this.lastTime / 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> |
...
|
...
|
|