canvas实现环形进度条效果


Posted in Javascript onMarch 23, 2017

昨下午睡着了,晚上打开手机才发现朋友给我发了一个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不规则增加的问题。

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

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

Javascript 相关文章推荐
jQuery获取Select选择的Text和Value(详细汇总)
Jan 25 Javascript
jquery实现在光标位置插入内容的方法
Feb 05 Javascript
简介JavaScript中substring()方法的使用
Jun 06 Javascript
jQuery的框架介绍
May 11 Javascript
将List对象列表转换成JSON格式的类实现方法
Jul 04 Javascript
原生JS+CSS实现炫酷重力模拟弹跳系统的登录页面
Nov 01 Javascript
在vue项目中使用Nprogress.js进度条的方法
Jan 31 Javascript
Node.js 多线程完全指南总结
Mar 27 Javascript
jquery实现选项卡切换代码实例
May 14 jQuery
Vue数字输入框组件示例代码详解
Jan 15 Javascript
解决React在安装antd之后出现的Can't resolve './locale'问题(推荐)
May 03 Javascript
Antd-vue Table组件添加Click事件,实现点击某行数据教程
Nov 17 Javascript
Javascript实现登录记住用户名和密码功能
Mar 22 #Javascript
jquery实现图片平滑滚动详解
Mar 22 #jQuery
JavaScript中在光标处插入添加文本标签节点的详细方法
Mar 22 #Javascript
jQuery轻松实现无缝轮播效果
Mar 22 #jQuery
JavaScript登录记住密码操作(超简单代码)
Mar 22 #Javascript
原生JS京东轮播图代码
Mar 22 #Javascript
10道典型的JavaScript面试题
Mar 22 #Javascript
You might like
再次研究下cache_lite
2007/02/14 PHP
基于PHP后台的Android新闻浏览客户端
2016/05/23 PHP
浅谈PHP中类和对象的相关函数
2017/04/26 PHP
PHP实现二维数组中的查找算法小结
2018/06/09 PHP
JS中彻底删除JSON对象组成的数组中的元素
2020/09/22 PHP
javascript 遍历验证所有文本框的值
2009/08/27 Javascript
javascript正则表达式中参数g(全局)的作用
2010/11/11 Javascript
在Node.js中使用HTTP上传文件的方法
2015/06/23 Javascript
js实现表单检测及表单提示的方法
2015/08/14 Javascript
基于vue实现分页/翻页组件paginator示例
2017/03/09 Javascript
React Native验证码倒计时工具类分享
2017/10/24 Javascript
通过js给网页加上水印背景实例
2019/06/17 Javascript
详解elementUI中input框无法输入的问题
2020/04/27 Javascript
Python中shutil模块的常用文件操作函数用法示例
2016/07/05 Python
python 捕获shell脚本的输出结果实例
2017/01/04 Python
python监控文件并且发送告警邮件
2018/06/21 Python
Python 利用pydub库操作音频文件的方法
2019/01/09 Python
python按比例随机切分数据的实现
2019/07/11 Python
Python django框架输入汉字,数字,字符生成二维码实现详解
2019/09/24 Python
Python转换itertools.chain对象为数组的方法
2020/02/07 Python
Mac中PyCharm配置Anaconda环境的方法
2020/03/04 Python
Python实现邮件发送的详细设置方法(遇到问题)
2021/01/18 Python
美国马匹用品和骑马配件购物网站:Horse.com
2018/01/08 全球购物
萨克斯第五大道英国:Saks Fifth Avenue英国
2019/04/01 全球购物
戴尔荷兰官方网站:Dell荷兰
2020/10/04 全球购物
工程总经理工作职责
2013/12/09 职场文书
化妆品店促销方案
2014/02/24 职场文书
求职信怎么写范文
2014/05/26 职场文书
大专生自我鉴定怎么写
2014/09/16 职场文书
学生实习证明范文
2014/09/28 职场文书
2014年综治维稳工作总结
2014/11/17 职场文书
商业门面租房协议书
2014/11/25 职场文书
学习型家庭事迹材料
2014/12/20 职场文书
2015年十一国庆节演讲稿
2015/03/20 职场文书
你有一份《诚信考试承诺书》待领取
2019/11/13 职场文书
windows11怎么查看wifi密码? win11查看wifi密码的技巧
2021/11/21 数码科技