canvas实现环形进度条效果

昨下午睡着了,晚上打开手机才发现朋友给我发了一个QQ消息,问我这个怎么实现?

这里就选canvas来简单写一下 先上代码,然后在说一说需要注意的点:

<!DOCTYPE html>

<html>

<head>

<meta charset="utf-8">

<title>canvas环形进度条</title>

<style>

body{

background-color:#000;

text-align: center;

}

.canvas1{

margin-top: 100px;

display: inline-block;

background-color: #FFF;

}

</style>

</head>

<body>

<canvas id="circle_process" class="canvas1"></canvas>

<script>

/*

需求:环形、一周分为10个片段,根据进度去走的一个状态

技术选型:canvas (挑战加熟悉)

思路:

01 首先中间的文字部分不用说,使用canvas的画文字。

02 圆形是个规则图形,那么为了避免画不规则图形,我们可以用圆和矩形来重叠出效果。

a. 大的灰色背景圆

b. 小一圈的白色背景圆

c. 以同心圆的圆心为圆心,小圆为半径为半径复制画10个小的矩形

*/

//初始化动画变量

var requestAnimationFrame = window.requestAnimationFrame || window.msRequestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame;

var cancelAnimationFrame = window.cancelAnimationFrame || window.msCancelAnimationFrame || window.mozCancelAnimationFrame || window.webkitCancelRequestAnimationFrame;

//初始化当前进度数

var curPercentCount = 0;

//获取canvas对象,设置画布大小

var oC = document.querySelector('#circle_process');

oC.width = 300;

oC.height = 300;

//获取canvas执行上下文

var ctx = oC.getContext('2d');

//定义小矩形的个数

var miniRectCount = 10;

//定义圆心位置

var cirCenter = {

x:oC.width/2,

y:oC.height/2

};

//定义小矩形的大小rectSize

var rectSize = {

width:0,

height:0

};

//圆对象构造函数

function Circle(center,radius){

this.center = center;

this.radius = radius;

}

//小矩形对象构造函数

function MiniRect(length,width){

this.length = length;

this.width = width;

}

//角度转换成弧度的函数

function d2a(angleInt){

return angleInt*Math.PI / 180;

}

//百分比转换角度函数(这里减90因为arc0度是从右侧开始的)

function percentTurn(percentFloat){

return percentFloat * 360 / 100 - 90;

}

//画当前百分比扇形的方法

function drawFanForPercent(percentFloat){

ctx.beginPath();

ctx.moveTo(cirCenter.x,cirCenter.y);

ctx.lineTo(oC.width/2,(oC.height-baseCircle.radius*2)/2);

ctx.arc(cirCenter.x,cirCenter.y,baseCircle.radius,d2a(-90),d2a(percentTurn(percentFloat)));

ctx.fillStyle = 'aqua';

ctx.fill();

ctx.closePath();

}

//画圆的函数

function drawArc(center,radius,start,end,type,color){

start = start || 0;

end = end || 360;

ctx.beginPath();

ctx.arc(center.x,center.y,radius,d2a(start),d2a(end));

ctx.fillStyle = color;

ctx.strokeStyle = color;

if(!!type){

(type === 'fill') && ctx.fill();

(type === 'stroke') && ctx.stroke();

}

ctx.closePath();

}

//画文字的函数

function drawPercentText(text,percentInt){

ctx.beginPath();

ctx.fillStyle = 'aqua';

ctx.font="italic small-caps bold 40px Calibri";

ctx.textAlign = 'center';

ctx.fillText(text,cirCenter.x,cirCenter.y-18,100);

ctx.closePath();

ctx.beginPath();

ctx.fillStyle = 'aqua';

ctx.font="italic small-caps bold 60px Calibri";

ctx.textAlign = 'center';

ctx.fillText(percentInt+'%',cirCenter.x,cirCenter.y+40,100);

ctx.closePath();

}

//画小方块的方法

function drawMiniRect(startPoint,width,height,axisPoint,rotateAngle){

/*

ctx.beginPath();

//平移,画出第一个

ctx.save();

ctx.translate(startPoint.x,startPoint.y);

ctx.fillStyle = '#FFF';

ctx.fillRect(0,0,rectSize.width,rectSize.height);

ctx.restore();

ctx.closePath();

//这种先平移画出在旋转的思路是错的,画之后就不能转了

ctx.save();

ctx.translate(axisPoint.x,axisPoint.y);

ctx.rotate(rotateAngle);

ctx.restore();

*/

ctx.save();

ctx.translate(axisPoint.x,axisPoint.y); /*画布平移到圆的中心*/

ctx.rotate(d2a(rotateAngle)); /*旋转*/

/*画*/

ctx.beginPath();

ctx.fillStyle = '#FFF';

ctx.fillRect(startPoint.x,startPoint.y,rectSize.width,rectSize.height);

ctx.closePath();

ctx.restore();

}

//画整体

function draw(curPercent){

//底部灰色圆

drawArc(baseCircle.center,baseCircle.radius,null,null,'fill','#CCC');

//进度扇形

drawFanForPercent(curPercent);

//内部白色遮挡圆

drawArc(innerCircle.center,innerCircle.radius,null,null,'fill','#FFF');

//画文字

drawPercentText('当前进度',curPercent);

//十个小的矩形

for(var i=0; i<miniRectCount; i++){

drawMiniRect(startPoint,rectSize.width,rectSize.height,cirCenter,i*360/miniRectCount);

}

}

//实例化底圆和内圆

var baseCircle = new Circle(cirCenter,130);

var innerCircle = new Circle(cirCenter,100);

//设置rectSize数值

rectSize.width = 15;

rectSize.height = baseCircle.radius - innerCircle.radius + 5;

//设置第一个小矩形的起始点 (这里有误差)

// var startPoint = {

// x: oC.width /2 - 7.5,

// y: (oC.height - baseCircle.radius*2) / 2

// };

//由于平移到中心点之后画的位置是在画布外的,所以重新定义

var startPoint = {

x:-7.5,

y:-baseCircle.radius - 2

};

//这里开定时去显示当前是百分之几的进度

var raf = null;

var percent = 0;

function actProcess(percentFloat){

percentFloat = percentFloat || 100;

percent = Math.round(percentFloat);

console.log(percent);

curPercentCount++;

raf = requestAnimationFrame(function(){

actProcess(percentFloat);

});

draw(curPercentCount);

if(curPercentCount >= percent){

cancelAnimationFrame(raf);

return;

}

}

actProcess(50);

// cancelAnimationFrame(raf);

//这里没搞懂为什么percent会加 ?

//解: requestAnimationFrame中方法还是需要有参数,这里就用匿名函数回调的执行体去指定。

/*

//setInterval的方式

function actProcess(percentFloat){

if(curPercentCount >= percentFloat){

clearInterval(timer);

return;

}

curPercentCount++;

draw(curPercentCount);

}

clearInterval(timer);

var timer = setInterval(function(){

actProcess(50);

},16.7);

*/

//直接画弧形的测试:

//drawArc(innerCircle.center,innerCircle.radius,0,260,'fill','red');

/*

用到的技术点:

01 canvas平移

02 canvas画布状态保存于恢复

03 canvas旋转

04 canvas clearRect配合动画requestAnimationFrame

05 canvas写文字

*/

</script>

</body>

</html>

接下来说一些注意点和我写的过程中碰到的疑问:

疑问:

01 整体代码没有封装成一个组件,感兴趣的同学可以封装一下。 我这有时间也会封装。

02 画文字的时候只能单独画一行文字么? 怎样进行换行?

03 canvas怎样处理响应式?

注意点:

01 画布平移之后,画布上的点也会被平移,所以我在定义第一个小矩形的起始点的时候才会重新定义一个负值。

02 直接画弧形来控制进度不准确,因为arc会自动closePath(),最终形成这样的一个效果。

03 默认圆的0度起始位置是从3点钟方向开始的(见上图),那么想从12点钟位置开始走进度,需要减去90度的角度。

04 requestAnimationFrame的回调函数在有参数的情况下还是需要传参数的,需要借助匿名函数回调,在执行体里面去执行想要loop的函数内容(可传参数)。否者会出现注释中写道的pecent不规则增加的问题。

先就这样,之后可能会结合一个上传图片的小功能尝试把它封装成一个组件。

以上是 canvas实现环形进度条效果 的全部内容, 来源链接: utcz.com/z/324969.html

回到顶部