<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>