js canvas仿支付宝芝麻信用分仪表盘


Posted in Javascript onNovember 16, 2016

这是一个仿支付宝芝麻信用分的一个canvas,其实就是一个动画仪表盘。

首先, 上原图:

js canvas仿支付宝芝麻信用分仪表盘

这个是在下支付宝上的截图,分低各位见笑了。然后看下我用canvas实现的效果图:

<canvas id="canvas" width="400" height="700" data-score='724'></canvas>
<!-- 设置data-score,分数区间[400, 900] -->

js canvas仿支付宝芝麻信用分仪表盘

唉,总感觉不像。这个是GIF图,可能在网页上打开的效果会好一点(当然可能就是这样)。大家可以点击底部预览codepen上的演示。有两个不完美的地方,一个是实际上芝麻信用表盘上的的刻度是不均匀的,我这为了简单的实现就采取相同的刻度;二是表盘上运动的点是有模糊的效果,还没解决。唉,下次再说吧。

接下来还是来说说怎么实现的吧。第一步,国际惯例,创建画布:

var canvas = document.getElementById('canvas'),
 ctx = canvas.getContext('2d'),
 cWidth = canvas.width,
 cHeight = canvas.height;

然后绘制表盘,虽说不是处女座,但也要尽可能做到跟原图上的一样,那就是这个环形开口的角度是多少呢?请上ps来测一下:

js canvas仿支付宝芝麻信用分仪表盘

嗯,136°,这个角度确实刁钻,为了方便接下来的计算,那就约等于140°。那么一个分数段的弧度就是:

var deg1 = Math.PI * 11 / 45
先把中间半透明的刻度层画好:

ctx.save(); //中间刻度层
ctx.beginPath();
ctx.strokeStyle = 'rgba(255, 255, 255, .2)';
ctx.lineWidth = 10;
ctx.arc(0, 0, 135, 0, 11 * deg0, false);
ctx.stroke();
ctx.restore();

接着,画6条刻度线,用for循环来实现:

ctx.save(); // 刻度线
for (var i = 0; i < 6; i++) {
 ctx.beginPath();
 ctx.lineWidth = 2;
 ctx.strokeStyle = 'rgba(255, 255, 255, .3)';
 ctx.moveTo(140, 0);
 ctx.lineTo(130, 0);
 ctx.stroke();
 ctx.rotate(deg1);
}
ctx.restore();

同理,再把大刻度细分为5个小刻度:

ctx.save(); // 细分刻度线
for (i = 0; i < 25; i++) {
 if (i % 5 !== 0){
 ctx.beginPath();
 ctx.lineWidth = 2;
 ctx.strokeStyle = 'rgba(255, 255, 255, .1)';
 ctx.moveTo(140, 0);
 ctx.lineTo(133, 0);
 ctx.stroke();
 }
 ctx.rotate(deg1 / 5);
}
ctx.restore();

刻度到这里就ok了,还需要给刻度标上文字和每个分数段的信用级别,具体的参见代码,因为跟刻度实现的原理差不多,就不??铝恕O衷谧罟丶?褪鞘迪直砼躺夏歉鲈硕?牡悖ú恢?涝趺闯坪簦?挛木徒兴??悖??颐强梢哉庋?耄??歉霭刖逗苄〉脑玻?徊还?腔?谧钔獠慊沸喂斓郎显玻??苍?anvas上的实现方法是:

ctx.arc(x, y, radius, sAngle, eAngle, false);
我们只要控制x, y就能让它动起来,实现我们想要的效果。so,创建一个动点对象:

function Dot() {
 this.x = 0;
 this.y = 0;
 this.draw = function (ctx) {
 ctx.save();
 ctx.beginPath();
 ctx.fillStyle = 'rgba(255, 255, 255, .7)';
 ctx.arc(this.x, this.y, 3, 0, Math.PI * 2, false);
 ctx.fill();
 ctx.restore();
 };
}
var dot = new Dot(),
 dotSpeed = 0.03, //控制动点的速度
 angle = 0, //这个很关键,用来得到动点的坐标x, y
 credit = 400; //信用最低分数

如何得到dot的坐标x, y呢?那就要用到传说中三角函数了。

js canvas仿支付宝芝麻信用分仪表盘

通过上图我们可以得到

x = r * cos(angle), y = r * sin(angle)
在JavaScript中,dot的中心坐标就变成了:

dot.x = radius * Math.cos(angle); //radius为最外层轨道的半径值
dot.y = radius * Math.sin(angle);

接下来我们只要得到这个angle。这个通过弧度与分数的比例关系就可以得到:

var aim = (score - 400) * deg1 / 100;
if (angle < aim) {
 angle += dotSpeed;
}
dot.draw(ctx);

然后让中间的信用分数也能随动点的转动而变化,创建一个text(),为了使数字变化能和动点保持一致,要根据动点的速率来计算数字变化:

function text(process) {
 ctx.save();
 ctx.rotate(10 * deg0);
 ctx.fillStyle = '#000';
 ctx.font = '80px Microsoft yahei';
 ctx.textAlign = 'center';
 ctx.textBaseLine = 'top';
 ctx.fillText(process, 0 ,10);
 ctx.restore();
}
var textSpeed = Math.round(dotSpeed * 100 / deg1),
if (credit < score - textSpeed) {
 credit += textSpeed;
} else if (credit >= score - textSpeed && credit < score) {
 credit += 1; // 这里确保信用分数最后停下来是我们输入的分数
}
text(credit);

最后这一切都逃不过让window.requestAnimationFrame()来控制绘制动画和用ctx.clearRect(0, 0, cWidth, cHeight)来清除画布。

写的不好,大家将就着看,我相信大家理解代码的能力一定强于理解我这些我自己都不知道说什么的文字。

好了,以上。

code:

<!DOCTYPE html>
<html>
 
 <head>
 <meta charset="UTF-8">
 <title>芝麻信用仪表盘</title>
 <style type="text/css">
  html,
  body {
  width: 100%;
  height: 100%;
  margin: 0;
  }
  
  canvas {
  border: 1px solid #eee;
  position: relative;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  background: -webkit-linear-gradient(top, #0e83f5 0%, #21bdf6 100%);
  background: -ms-linear-gradient(top, #0e83f5 0%, #21bdf6 100%);
  background: -moz-linear-gradient(top, #0e83f5 0%, #21bdf6 100%);
  background: linear-gradient(top, #0e83f5 0%, #21bdf6 100%);
  }
 </style>
 <script type="text/javascript">
  window.onload = function() {
  var canvas = document.getElementById('canvas'),
   ctx = canvas.getContext('2d'),
   cWidth = canvas.width,
   cHeight = canvas.height,
   score = canvas.attributes['data-score'].value,
   stage = ['较差', '中等', '良好', '优秀', '极好'],
   radius = 150,
   deg0 = Math.PI / 9,
   deg1 = Math.PI * 11 / 45;
 
  if(score < 400 || score > 900) {
   alert('信用分数区间:400~900');
  } else {
   var dot = new Dot(),
   dotSpeed = 0.03,
   textSpeed = Math.round(dotSpeed * 100 / deg1),
   angle = 0,
   credit = 400;
 
   (function drawFrame() {
 
   ctx.save();
   ctx.clearRect(0, 0, cWidth, cHeight);
   ctx.translate(cWidth / 2, cHeight / 2);
   ctx.rotate(8 * deg0);
 
   dot.x = radius * Math.cos(angle);
   dot.y = radius * Math.sin(angle);
 
   var aim = (score - 400) * deg1 / 100;
   if(angle < aim) {
    angle += dotSpeed;
   }
   dot.draw(ctx);
 
   if(credit < score - textSpeed) {
    credit += textSpeed;
   } else if(credit >= score - textSpeed && credit < score) {
    credit += 1;
   }
   text(credit);
 
   ctx.save();
   ctx.beginPath();
   ctx.lineWidth = 3;
   ctx.strokeStyle = 'rgba(255, 255, 255, .5)';
   ctx.arc(0, 0, radius, 0, angle, false);
   ctx.stroke();
   ctx.restore();
 
   window.requestAnimationFrame(drawFrame);
 
   ctx.save(); //中间刻度层
   ctx.beginPath();
   ctx.strokeStyle = 'rgba(255, 255, 255, .2)';
   ctx.lineWidth = 10;
   ctx.arc(0, 0, 135, 0, 11 * deg0, false);
   ctx.stroke();
   ctx.restore();
 
   ctx.save(); // 刻度线
   for(var i = 0; i < 6; i++) {
    ctx.beginPath();
    ctx.lineWidth = 2;
    ctx.strokeStyle = 'rgba(255, 255, 255, .3)';
    ctx.moveTo(140, 0);
    ctx.lineTo(130, 0);
    ctx.stroke();
    ctx.rotate(deg1);
   }
   ctx.restore();
 
   ctx.save(); // 细分刻度线
   for(i = 0; i < 25; i++) {
    if(i % 5 !== 0) {
    ctx.beginPath();
    ctx.lineWidth = 2;
    ctx.strokeStyle = 'rgba(255, 255, 255, .1)';
    ctx.moveTo(140, 0);
    ctx.lineTo(133, 0);
    ctx.stroke();
    }
    ctx.rotate(deg1 / 5);
   }
   ctx.restore();
 
   ctx.save(); //信用分数
   ctx.rotate(Math.PI / 2);
   for(i = 0; i < 6; i++) {
    ctx.fillStyle = 'rgba(255, 255, 255, .4)';
    ctx.font = '10px Microsoft yahei';
    ctx.textAlign = 'center';
    ctx.fillText(400 + 100 * i, 0, -115);
    ctx.rotate(deg1);
   }
   ctx.restore();
 
   ctx.save(); //分数段
   ctx.rotate(Math.PI / 2 + deg0);
   for(i = 0; i < 5; i++) {
    ctx.fillStyle = 'rgba(255, 255, 255, .4)';
    ctx.font = '10px Microsoft yahei';
    ctx.textAlign = 'center';
    ctx.fillText(stage[i], 5, -115);
    ctx.rotate(deg1);
   }
   ctx.restore();
 
   ctx.save(); //信用阶段及评估时间文字
   ctx.rotate(10 * deg0);
   ctx.fillStyle = '#fff';
   ctx.font = '28px Microsoft yahei';
   ctx.textAlign = 'center';
   if(score < 500) {
    ctx.fillText('信用较差', 0, 40);
   } else if(score < 600 && score >= 500) {
    ctx.fillText('信用中等', 0, 40);
   } else if(score < 700 && score >= 600) {
    ctx.fillText('信用良好', 0, 40);
   } else if(score < 800 && score >= 700) {
    ctx.fillText('信用优秀', 0, 40);
   } else if(score <= 900 && score >= 800) {
    ctx.fillText('信用极好', 0, 40);
   }
 
   ctx.fillStyle = '#80cbfa';
   ctx.font = '14px Microsoft yahei';
   ctx.fillText('评估时间:2016.11.06', 0, 60);
 
   ctx.fillStyle = '#7ec5f9';
   ctx.font = '14px Microsoft yahei';
   ctx.fillText('BETA', 0, -60);
   ctx.restore();
 
   // ctx.save(); //最外层轨道
   ctx.beginPath();
   ctx.strokeStyle = 'rgba(255, 255, 255, .4)';
   ctx.lineWidth = 3;
   ctx.arc(0, 0, radius, 0, 11 * deg0, false);
   ctx.stroke();
   ctx.restore();
 
   })();
  }
 
  function Dot() {
   this.x = 0;
   this.y = 0;
   this.draw = function(ctx) {
   ctx.save();
   ctx.beginPath();
   ctx.fillStyle = 'rgba(255, 255, 255, .7)';
   ctx.arc(this.x, this.y, 3, 0, Math.PI * 2, false);
   ctx.fill();
   ctx.restore();
   };
  }
 
  function text(process) {
   ctx.save();
   ctx.rotate(10 * deg0);
   ctx.fillStyle = '#000';
   ctx.font = '80px Microsoft yahei';
   ctx.textAlign = 'center';
   ctx.textBaseLine = 'top';
   ctx.fillText(process, 0, 10);
   ctx.restore();
  }
  };
 </script>
 </head>
 
 <body>
 <canvas id="canvas" width="400" height="700" data-score='724'></canvas>
 </body>
 
</html>

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
jQuery布局插件UI Layout简介及使用方法
Apr 03 Javascript
禁用Tab键JS代码兼容Firefox和IE
Apr 18 Javascript
jQuery替换textarea中换行的方法
Jun 10 Javascript
基于jquery编写分页插件
Mar 07 Javascript
Vue数据驱动模拟实现4
Jan 12 Javascript
js return返回多个值,通过对象的属性访问方法
Feb 21 Javascript
vue实现nav导航栏的方法
Dec 13 Javascript
Javascript网页抢红包外挂实现分享
Jan 11 Javascript
vue-cli中安装方法(图文详细步骤)
Dec 12 Javascript
jQuery实现可编辑的表格
Dec 11 jQuery
如何使用JS console.log()技巧提高工作效率
Oct 14 Javascript
javascript 数组(list)添加/删除的实现
Dec 17 Javascript
Javascript使用SWFUpload进行多文件上传
Nov 16 #Javascript
微信小程序教程之本地图片上传(leancloud)实例详解
Nov 16 #Javascript
Javascript使用uploadify来实现多文件上传
Nov 16 #Javascript
微信小程序 地图(map)实例详解
Nov 16 #Javascript
微信小程序 选择器(时间,日期,地区)实例详解
Nov 16 #Javascript
微信小程序 图片等比例缩放(图片自适应屏幕)
Nov 16 #Javascript
js以分隔符分隔数组中的元素并转换为字符串的方法
Nov 16 #Javascript
You might like
编译问题
2006/10/09 PHP
在PHP中使用灵巧的体系结构
2006/10/09 PHP
PHP面向对象概念
2011/11/06 PHP
php文本转图片自动换行的方法
2013/03/13 PHP
PHP闭包函数详解
2016/02/13 PHP
Yii2单元测试用法示例
2016/11/12 PHP
laravel自定义分页效果
2017/07/23 PHP
仅IE不支持setTimeout/setInterval函数的第三个以上参数
2011/05/25 Javascript
js限制文本框输入长度两种限制方式(长度、字节数)
2012/12/19 Javascript
使用js对select动态添加和删除OPTION示例代码
2013/08/12 Javascript
jQuery封装的获取Url中的Get参数示例
2013/11/26 Javascript
jQuery三级下拉列表导航菜单代码分享
2020/04/15 Javascript
纯JS焦点图特效实例(可一个页面多用)
2016/12/07 Javascript
原生JS实现小小的音乐播放器
2017/10/16 Javascript
angular中子控制器向父控制器传值的实例
2018/10/08 Javascript
Python的装饰器用法学习笔记
2016/06/24 Python
Python爬虫:通过关键字爬取百度图片
2017/02/17 Python
python3中set(集合)的语法总结分享
2017/03/24 Python
python交互界面的退出方法
2019/02/16 Python
Python面向对象程序设计之类的定义与继承简单示例
2019/03/18 Python
Python2与Python3的区别实例总结
2019/04/17 Python
Python Tensor FLow简单使用方法实例详解
2020/01/14 Python
pytorch模型存储的2种实现方法
2020/02/14 Python
Django+python服务器部署与环境部署教程详解
2020/03/30 Python
pytorch简介
2020/11/11 Python
搭建pypi私有仓库实现过程详解
2020/11/25 Python
详解python使用金山词霸的翻译功能(调试工具断点的使用)
2021/01/07 Python
HTML5离线缓存在tomcat下部署可实现图片flash等离线浏览
2012/12/13 HTML / CSS
利用 Canvas实现绘画一个未闭合的带进度条的圆环
2019/07/26 HTML / CSS
Speedo澳大利亚官网:全球领先游泳品牌
2018/02/04 全球购物
会计毕业生求职简历的自我评价
2013/10/20 职场文书
会展策划与管理专业大学生职业生涯规划
2014/02/07 职场文书
高考备战决心书
2014/03/11 职场文书
医疗专业毕业生求职信
2014/08/28 职场文书
Java中的Kotlin 内部类原理
2022/06/16 Java/Android
使用CSS实现百叶窗效果示例代码
2023/05/07 HTML / CSS