看到一篇《用初中数学知识撸一个canvas环形进度条》关于如何绘制canvas环形进度条的文章,深受启发;所以自己手把手照着葫芦画瓢一般写了一遍,总结一下!
canvas初始化
var cans = document.getElementById("myCanvas");
var context = cans.getContext("2d");
小程序中:
var context = tt.createCanvasContext("canvas");
//必须放在"methods:{}"的方法中,不能作为变量放在 page({})外部。因为需要等页面初始化完成后,再初始化 canvas
绘制图形:空心与实心
//绘制实心矩形:
context.fillStyle = 'blue';
context.fillRect(100,125,50,50); //(x,y,w,h)
//绘制空心矩形
context.strokeStyle = 'yellow';
context.strokeRect(100,180,50,50) //(x,y,w,h)
小程序中:
//绘制实心矩形:
context.setFillStyle('#ff0000');
context.arc(100,75,50,0,2*Math.PI);
//context.fillRect(100,125,50,50); //(x,y,w,h)
context.fill();
//绘制空心矩形
context.setStrokeStyle('#f00000');
context.arc(100,75,50,0,2*Math.PI);
//context.strokeRect(100,180,50,50) //(x,y,w,h)
context.stroke();
绘制文字
//画文字
ctx.font = '36px Arial,"Microsoft YaHei"'
ctx.fillStyle = "#f00000";
ctx.textAlign = 'center'
ctx.textBaseline = 'middle'
ctx.fillText('文字', w, h); //w,h 画布宽、高
小程序:
//画文字
context.setFontSize(36)
context.setFillStyle('#f0000');
context.setTextAlign('center')
context.setTextBaseline('middle')
context.fillText('文字', w, h); //w,h 画布宽、高
环形进度条示例
<!DOCTYPE html>
<html>
<body>
<canvas id="myCanvas"></canvas>
<script>
/*
//绘制实心矩形:
context.fillStyle = 'blue';
context.fillRect(100,125,50,50); //(x,y,w,h)
//绘制空心矩形
context.strokeStyle = 'yellow';
context.strokeRect(100,180,50,50) //(x,y,w,h)
*/
var cans = document.getElementById("myCanvas");
var ctx = cans.getContext("2d");
var percent = 0,//分数
classifier = '分'//单位
var counts = 58
var gradient = null;
var circleRadius = 55, //圆环半径
pointRadius = 5, //圆点半径
pointColor= '#FF4949', //圆点颜色
circleWidth = 2, //圆环线宽
lineWidth = 8, //进度条线宽
circleColor= '#f00000',
lineColorStops = [
{ percent: 0, color: '#FF9933' },
{ percent: 1, color: '#FF4949' }
];
//设置canvas尺寸
cans.width = canvasSize()
cans.height = canvasSize()
//***设置动画***
function animateDrawArc(){
var startDeg = 270;
var nextDeg = getTargetDegByPercentage(percent)
var startArc = deg2Arc(startDeg);
var nextArc = deg2Arc(nextDeg);
// 根据角度获取点的位置
function getPositionsByDeg(deg) {
let x = 0;
let y = 0;
if (deg >= 0 && deg <= 90) {
// 0~90度
x = circleRadius * (1 + Math.cos(deg2Arc(deg)))
y = circleRadius * (1 + Math.sin(deg2Arc(deg)))
} else if (deg > 90 && deg <= 180) {
// 90~180度
x = circleRadius * (1 - Math.cos(deg2Arc(180 - deg)))
y = circleRadius * (1 + Math.sin(deg2Arc(180 - deg)))
} else if (deg > 180 && deg <= 270) {
// 180~270度
x = circleRadius * (1 - Math.sin(deg2Arc(270 - deg)))
y = circleRadius * (1 - Math.cos(deg2Arc(270 - deg)))
} else {
// 270~360度
x = circleRadius * (1 + Math.cos(deg2Arc(360 - deg)))
y = circleRadius * (1 - Math.sin(deg2Arc(360 - deg)))
}
return { x, y }
}
// 根据开始角度和进度百分比求取目标角度
function getTargetDegByPercentage(percentage) {
if (percentage === 100) {
return startDeg + 360
} else {
const targetDeg = (startDeg + 360 * percentage / 100) % 360
return targetDeg
}
}
//画圆环
ctx.beginPath();
ctx.strokeStyle= circleColor;
ctx.lineWidth = circleWidth;
ctx.arc(outerRadius(), outerRadius(), circleRadius, 0, deg2Arc(360))
ctx.stroke();
//画文字
ctx.font = '36px Arial,"Microsoft YaHei"'
ctx.fillStyle = "#f00000";
ctx.textAlign = 'center'
ctx.textBaseline = 'middle'
ctx.fillText(percent, outerRadius(), outerRadius());
ctx.font = '15px Arial,"Microsoft YaHei"'
ctx.fillText(classifier, outerRadius() + 30, outerRadius() + 5);
//画进度条/设置渐变色
gradient = ctx.createLinearGradient(circleRadius, 0, circleRadius, circleRadius * 2);
lineColorStops.forEach(item => {
gradient.addColorStop(item.percent, item.color);
});
//画进度弧
ctx.strokeStyle = gradient;
ctx.lineWidth = lineWidth;
ctx.beginPath();
ctx.arc(outerRadius(), outerRadius(), circleRadius, startArc, nextArc);
ctx.stroke();
// 画进度圆点
const pointPosition = getPositionsByDeg(nextDeg);
ctx.fillStyle = pointColor;
ctx.beginPath();
ctx.arc(pointPosition.x + pointRadius, pointPosition.y + pointRadius, pointRadius, 0, deg2Arc(360));
this.ctx.fill();
}
// 外围半径
function outerRadius() {
return circleRadius + pointRadius
}
// canvas宽/高
function canvasSize() {
return 2 * outerRadius()
}
// deg转弧度
function deg2Arc(deg) {
return deg / 180 * Math.PI
}
//循环调用动画
var countDown = function () {
// 清空画布
ctx.clearRect(0, 0, cans.width, cans.height);
percent ++
console.info('动画', percent)
animateDrawArc()
if ( percent == counts) {
return false
}else {
count()
}
}
var count = function (){
setTimeout(function(){
countDown()
}, 10)
}
//初始化调用
countDown()
</script>
</body>
</html>
小程序:
<!--canvas环形进度条-->
<canvas canvas-id="canvas" class="canvas" style="width: {{canvasSize}}px; height: {{canvasSize}}px;"></canvas>
// pages/component/circle/circle.js
var startDeg = 270;
var gradient = null
var percents = 0 //赋值分数
Component({
data: {
canvasSize: 0
},
properties: {
//接收分数
percent: {
type: Number,
value: 60
},
fontSize: {
type: Number,
value: 36
},
fontColor: {
type: String,
value: '#3B77E3'
},
circleRadius: {
type: Number,
value: 55
},
pointRadius: {
type: Number,
value: 5
},
circleWidth: {
type: Number,
value: 2
},
lineWidth: {
type: Number,
value: 8
},
pointColor: {
type: String,
value: '#3B77E3'
},
circleColor: {
type: String,
value: '#3B77E3'
},
lineColorStops: {
type: Object,
value: [
{ percent: 0, color: '#13CDE3' },
{ percent: 1, color: '#3B77E3' }
]
},
},
methods: {
// 外围半径
outerRadius: function() {
return this.properties.circleRadius + this.properties.pointRadius
},
// canvas宽/高
canvasSize: function(e) {
console.log('properties', e)
this.setData({
canvasSize: 2 * this.outerRadius()
})
this.countDown()
},
//循环调用动画
countDown: function () {
let that = this
percents ++
console.info('动画', percents)
this.animateDrawArc()
if ( percents == that.properties.percent) {
return false
}else {
that.count()
}
},
count: function (){
let that = this
setTimeout(function(){
that.countDown()
}, 30)
},
//设置动画
animateDrawArc: function(){
var that = this;
var context = tt.createCanvasContext("canvas");
var nextDeg = this.getTargetDegByPercentage(percents);
var startArc = this.deg2Arc(startDeg);
var nextArc = this.deg2Arc(nextDeg);
var outerRadius = this.outerRadius(),
circleRadius = this.properties.circleRadius,
pointRadius = this.properties.pointRadius;
//画圆环
context.beginPath();
context.setStrokeStyle(that.properties.circleColor);
context.setLineWidth(that.properties.circleWidth);
context.arc(outerRadius, outerRadius, circleRadius, 0, that.deg2Arc(360));
context.stroke();
//画文字
context.setFontSize(that.properties.fontSize)
context.setFillStyle(that.properties.fontColor);
context.setTextAlign('center')
context.setTextBaseline('middle')
context.fillText(percents, outerRadius, outerRadius);
context.setFontSize(14)
context.fillText('分', outerRadius + 30, outerRadius + 4);
//画进度条//设置渐变色
gradient = context.createLinearGradient(circleRadius, 0, circleRadius, circleRadius * 2)
that.properties.lineColorStops.forEach(item => {
gradient.addColorStop(item.percent, item.color);
});
//画进度弧
context.setStrokeStyle(gradient);
context.setLineWidth(that.properties.lineWidth);
context.beginPath();
context.arc(outerRadius, outerRadius, circleRadius, startArc, nextArc);
context.stroke();
//画圆点
const pointPosition = that.getPositionsByDeg(nextDeg);
context.setFillStyle(that.properties.pointColor);
context.beginPath();
context.arc(pointPosition.x + pointRadius, pointPosition.y + pointRadius, pointRadius, 0, that.deg2Arc(360));
context.fill();
//画布绘制
context.draw();
},
//根据开始角度和进度百分比求取目标角度
getTargetDegByPercentage: function(percentage) {
if (percentage === 100) {
return startDeg + 360
} else {
const targetDeg = (startDeg + 360 * percentage / 100) % 360
return targetDeg
}
},
// 根据角度获取点的位置
getPositionsByDeg: function(deg) {
let x = 0;
let y = 0;
if (deg >= 0 && deg <= 90) {
// 0~90度
x = this.properties.circleRadius * (1 + Math.cos(this.deg2Arc(deg)))
y = this.properties.circleRadius * (1 + Math.sin(this.deg2Arc(deg)))
} else if (deg > 90 && deg <= 180) {
// 90~180度
x = this.properties.circleRadius * (1 - Math.cos(this.deg2Arc(180 - deg)))
y = this.properties.circleRadius * (1 + Math.sin(this.deg2Arc(180 - deg)))
} else if (deg > 180 && deg <= 270) {
// 180~270度
x = this.properties.circleRadius * (1 - Math.sin(this.deg2Arc(270 - deg)))
y = this.properties.circleRadius * (1 - Math.cos(this.deg2Arc(270 - deg)))
} else {
// 270~360度
x = this.properties.circleRadius * (1 + Math.cos(this.deg2Arc(360 - deg)))
y = this.properties.circleRadius * (1 - Math.sin(this.deg2Arc(360 - deg)))
}
return { x, y }
},
//deg转弧度
deg2Arc: function(deg) {
return deg / 180 * Math.PI
}
},
//组件生命周期函数,在组件布局完成后执行
ready: function() {
this.canvasSize(this.properties)
},
})