欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

canvas实现环形进度条效果

程序员文章站 2024-02-05 15:16:28
昨下午睡着了,晚上打开手机才发现朋友给我发了一个qq消息,问我这个怎么实现? 这里就选canvas来简单写一下 先上代码,然后在说一说需要注意的点: <...

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

canvas实现环形进度条效果

这里就选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(),最终形成这样的一个效果。

canvas实现环形进度条效果

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

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

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

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持!