canvas动态绘制渐变色环形百分比
项目中需要根据数据动态绘制渐变环形百分比(非线性渐变和放射渐变),代码基于Vue。将100%的渐变环形图设置为Pattern,进行绘制。
// 注册环形百分比组件
Vue.component("v-ratechart", {
props: {
rate: Number,
count: Number,
imgsrc: String
},
data: function() {
return {
timer: Number,
ctx: Object,
//圆心(x, y) 半径r
x: 55,
y: 55,
r: 50,
oldRate: Number
}
},
template: "<canvas width='160' height='160' ref='rateCanvas'></canvas>",
mounted: function() {
var that = this;
this.oldRate = this.rate;
var canvas = this.$refs.rateCanvas,
ctx = canvas.getContext("2d");
this.ctx = ctx; //拷贝指针
//绘制灰色轨道
ctx.lineWidth = 10;
ctx.lineCap = 'round';
this.drawTrack(ctx);
// 加载100%图片
var img = new Image();
// IE不兼容脚本引入svg
var format = '.svg'
if( window.ActiveXObject || "ActiveXObject" in window ) {
format = '.png'
}
img.src = "/img/sysMonitorCompnt/" + this.imgsrc + format;
img.onload = function() {
that.ImageLoadCallBack(ctx, this)
};
},
methods: {
/**
* 绘制轨道
*/
drawTrack: function(ctx) {
ctx.save()
// 绘制环形轨道
ctx.beginPath();
ctx.strokeStyle = 'rgba(0, 0, 0, 0.08)';
ctx.arc(this.x, this.y, this.r, 20 / 180 * Math.PI, 300 / 180 * Math.PI);
ctx.stroke();
ctx.restore()
},
/**
* 图片加载回调
*/
ImageLoadCallBack: function(ctx, img) {
//绘制百分比 渐变填充
var rateFill = ctx.createPattern(img, 'no-repeat');
ctx.strokeStyle = rateFill;
this.drawCircleAndText(ctx, 20, this.rate * 300, 0, this.rate * 10000 / 100, 0, this.count, 1)
},
/**
* 动态绘制渐渐变填充和文本
* startAngle: 当前变化角度
* endAngle: 环形变化结束角度
* startRate: 当前变化百分比
* endRate: 变化终止百分比
* startCount: 当前变化系统数量
* endCount: 变化终止系统数量
* isIncrease: 是否增长
*/
drawCircleAndText: function(ctx, startAngle, endAngle, startRate, endRate, startCount, endCount, isIncrease) {
var that = this,
velocity = 3; //百分比、环形填充动画速度
//文本通用样式
ctx.fillStyle = '#fff';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
//清空画布
ctx.clearRect(0, 0, 160, 160);
//重新绘制轨道
this.drawTrack(ctx)
//动态绘制环形填充
if ( isIncrease >= 0 && startAngle < endAngle && endAngle != 0 ) {
//角度递增 与百分比速度同步
startAngle += endAngle * (velocity * 1 / endRate);
if(startAngle > endAngle) {
startAngle = endAngle;
}
}else if( isIncrease < 0 && startAngle > endAngle){
//角度递增 与百分比速度同步
startAngle -= (startAngle - endAngle) * (velocity * 1 / (startRate - endRate != 0 ? startRate - endRate : 1));
if(startAngle < endAngle) {
startAngle = endAngle;
}
}else if(isIncrease >= 0 && endAngle == 0) {
// 数据为0,不绘制
startAngle = 0
}
ctx.beginPath();
ctx.arc(this.x, this.y, this.r, 0, startAngle / 180 * Math.PI, false);
ctx.stroke();
//动态绘制系统数量文本
if( isIncrease > 0 && startCount < endCount ) {
// 与百分比速度同步
startCount += endCount * (velocity * 1 / endRate);
if(startCount > endCount) {
startCount = endCount;
}
}else if( isIncrease < 0 ) {
startCount -= (startCount - endCount) * (velocity * 1 / (startRate - endRate != 0 ? startRate - endRate : 1));
if(startCount < endCount) {
startCount = endCount;
}
}
//绘制系统数量
ctx.font = '46px PingFangSC-Medium';
ctx.fillText( Math.round( startCount ), this.x, this.y);
//动态绘制比率文本
if( isIncrease > 0 && startRate < endRate ) {
startRate += velocity;
if( startRate > endRate) {
startRate = endRate;
}
}else if( isIncrease < 0 ) {
startRate -= velocity;
if( startRate < endRate) {
startRate = endRate;
}
}
ctx.font = '15px PingFangSC-Medium';
var rateLoctionX = this.x + this.r * Math.sin(30 / 180 * Math.PI) + 30,
rateLoctionY = this.y - this.r + 10;
ctx.fillText( startRate + '%', rateLoctionX, rateLoctionY);
// 刷新画布
that.timer = window.requestAnimationFrame(function () {
that.drawCircleAndText(ctx, startAngle, endAngle, startRate, endRate, startCount, endCount, isIncrease);
});
//所有元素动态绘制完毕,停止刷新
if( (isIncrease > 0 && startAngle >= endAngle && startRate >= endRate && startCount >= endCount) ||
(isIncrease < 0 && startAngle <= endAngle && startRate <= endRate && startCount <= endCount)) {
window.cancelAnimationFrame( that.timer )
}
}
},
watch: {
'count': function(val, oldVal) {
var isIncrease = val - oldVal;
// 更新比例绘图
this.drawCircleAndText(this.ctx, this.oldRate * 300, this.rate * 300, this.oldRate * 10000 / 100, this.rate * 10000 / 100, oldVal, val, isIncrease);
this.oldRate = this.rate;
}
}
});
html直接引入
<div class="contentRate">
<v-ratechart :rate=status.SystemNumRate :count=status.SystemNum :imgsrc="statusCardClass[index]"></v-ratechart>
</div>
最终效果