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 相关文章推荐
Riot.js 快速的JavaScript单元测试框架
Nov 09 Javascript
js中widow.open()方法使用详解
Jul 30 Javascript
jQuery取得设置清空select选择的文本与值
Jul 08 Javascript
Html5的placeholder属性(IE兼容)实现代码
Aug 30 Javascript
jquery ajaxfileupload异步上传插件使用详解
Feb 08 Javascript
js 获取今天以及过去日期
Apr 11 Javascript
fetch 使用及如何接收JS传值
Nov 11 Javascript
vue-devtools的安装步骤
Apr 23 Javascript
webpack结合express实现自动刷新的方法
May 07 Javascript
微信小程序一周时间表功能实现
Oct 17 Javascript
Vue-Cli项目优化操作的实现
Oct 27 Javascript
基于aotu.js实现微信自动添加通讯录中的联系人功能
May 28 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
php 分页原理详解
2009/08/21 PHP
PHP操作mysql函数详解,mysql和php交互函数
2011/05/19 PHP
PHP5中实现多态的两种方法实例分享
2014/04/21 PHP
php检测文件编码的方法示例
2014/04/25 PHP
php判断类是否存在函数class_exists用法分析
2014/11/14 PHP
php转换颜色为其反色的方法
2015/04/27 PHP
php使用curl通过代理获取数据的实现方法
2016/05/16 PHP
微信支付开发发货通知实例
2016/07/12 PHP
php curl优化下载微信头像的方法总结
2018/09/07 PHP
JQuery 绑定select标签的onchange事件,弹出选择的值,并实现跳转、传参
2011/01/06 Javascript
3种不同方式的焦点图轮播特效分享
2013/10/30 Javascript
基于zepto.js实现仿手机QQ空间的大图查看组件ImageView.js详解
2015/03/05 Javascript
JavaScript实现在页面间传值的方法
2015/04/07 Javascript
JS替换字符串中空格方法
2015/04/17 Javascript
使用JavaScript实现ajax的实例代码
2016/05/11 Javascript
VUE Error: getaddrinfo ENOTFOUND localhost
2018/05/03 Javascript
element-ui上传一张图片后隐藏上传按钮功能
2019/05/22 Javascript
layui操作列按钮个数和文字颜色的判断实例
2019/09/11 Javascript
[00:12]DAC2018 Miracle-站上中单舞台,他能否再写奇迹?
2018/04/06 DOTA
[38:23]完美世界DOTA2联赛循环赛 FTD vs PXG BO2第二场 11.01
2020/11/02 DOTA
Python动态生成多维数组的方法示例
2018/08/09 Python
Python实现合并excel表格的方法分析
2019/04/13 Python
python打包多类型文件的操作方法
2020/09/21 Python
python opencv实现图像配准与比较
2021/02/09 Python
HTML5 HTMLCollection和NodeList的区别详解
2020/04/29 HTML / CSS
澳大利亚家具和家居用品在线商店:Interiors Online
2018/03/05 全球购物
捷克建筑材料网上商店:DEK.cz
2021/03/06 全球购物
天网面试题
2013/04/07 面试题
学校后勤人员职责
2013/12/27 职场文书
个人授权委托书
2014/04/03 职场文书
搞笑爱情保证书
2014/04/29 职场文书
应届生面试求职信
2014/07/02 职场文书
组工干部演讲稿
2014/09/02 职场文书
2015年春节标语口号
2014/12/09 职场文书
Redis高可用集群redis-cluster详解
2022/03/20 Redis
为什么MySQL8新特性会修改自增主键属性
2022/04/18 MySQL