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 相关文章推荐
自定义ExtJS控件之下拉树和下拉表格附源码
Oct 15 Javascript
jQuery实现tag便签去重效果的方法
Jan 20 Javascript
实现easyui的datagrid导出为excel的示例代码
Nov 10 Javascript
JS实现DIV高度自适应窗口示例
Feb 16 Javascript
快速掌握jquery分页插件jqPaginator的使用方法
Aug 09 jQuery
ES6学习教程之块级作用域详解
Oct 09 Javascript
微信小程序实现图片预览功能
Jan 31 Javascript
bootstrap treeview 树形菜单带复选框及级联选择功能
Jun 08 Javascript
jQuery利用FormData上传文件实现批量上传
Dec 04 jQuery
vue 左滑删除功能的示例代码
Jan 28 Javascript
javascript canvas时钟模拟器
Jul 13 Javascript
详解如何在Javascript中使用Object.freeze()
Oct 18 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
php 三维饼图的实现代码
2008/09/28 PHP
使用PHP实现蜘蛛访问日志统计
2013/07/05 PHP
php后门URL的防范
2013/11/12 PHP
ThinkPHP自动转义存储富文本编辑器内容导致读取出错的解决方法
2014/08/08 PHP
CI框架无限级分类+递归的实现代码
2016/11/01 PHP
php中请求url的五种方法总结
2017/07/13 PHP
PHP设计模式(四)原型模式Prototype实例详解【创建型】
2020/05/02 PHP
ExtJs的Date格式字符代码
2010/12/30 Javascript
HTML DOM的nodeType值介绍
2011/03/31 Javascript
使用insertAfter()方法在现有元素后添加一个新元素
2014/05/28 Javascript
Function.prototype.apply()与Function.prototype.call()小结
2016/04/27 Javascript
教你JS中的运算符乘方、开方及变量格式转换
2016/08/09 Javascript
js中小数向上取整数,向下取整数,四舍五入取整数的实现(必看篇)
2017/02/13 Javascript
jQuery遮罩层实例讲解
2017/05/11 jQuery
vue Render中slots的使用的实例代码
2017/07/19 Javascript
总结js中的一些兼容性易错的问题
2017/12/18 Javascript
微信小程序表单验证form提交错误提示效果
2020/06/19 Javascript
初试vue-cli使用HBuilderx打包app的坑
2019/07/17 Javascript
vue2.0+SVG实现音乐播放圆形进度条组件
2019/09/21 Javascript
Antd中单个DatePicker限定时间输入范围操作
2020/10/29 Javascript
一篇文章让你搞懂JavaScript 原型和原型链
2020/11/23 Javascript
[16:27]DOTA2 HEROS教学视频教你分分钟做大人-艾欧
2014/06/11 DOTA
对python 操作solr索引数据的实例详解
2018/12/07 Python
树莓派用python中的OpenCV输出USB摄像头画面
2019/06/22 Python
Python学习笔记之Zip和Enumerate用法实例分析
2019/08/14 Python
Pytorch中实现只导入部分模型参数的方式
2020/01/02 Python
python开发入门——set的使用
2020/09/03 Python
如何拷贝一整个Java对象,包括它的状态
2013/12/27 面试题
行政部工作岗位职责范本
2014/03/05 职场文书
婚纱摄影师求职信范文
2014/04/17 职场文书
一年级学生评语
2014/04/23 职场文书
住房租房协议书
2014/08/20 职场文书
家庭困难证明
2014/10/12 职场文书
教师辞职书范文
2015/02/26 职场文书
入党个人总结范文
2015/03/02 职场文书
为什么MySQL分页用limit会越来越慢
2021/07/25 MySQL