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 相关文章推荐
Javascript中eval函数的使用方法与示例
Apr 09 Javascript
用javascript实现的仿Flash广告图片轮换效果
Apr 24 Javascript
ExtJS Grid使用SimpleStore、多选框的方法
Nov 20 Javascript
javascript的原生方法获取数组中的最大(最小)值
Dec 19 Javascript
JavaScript中数组的合并以及排序实现示例
Oct 24 Javascript
深入浅析JavaScript中prototype和proto的关系
Nov 15 Javascript
jQuery中 bind的用法简单介绍
Feb 13 Javascript
jQuery实现的文字逐行向上间歇滚动效果示例
Sep 06 jQuery
JavaScript中的一些隐式转换和总结(推荐)
Dec 22 Javascript
react-router browserHistory刷新页面404问题解决方法
Dec 29 Javascript
从0到1构建vueSSR项目之node以及vue-cli3的配置
Mar 07 Javascript
在vue中动态添加class类进行显示隐藏实例
Nov 09 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/26 PHP
解析关于wamp启动是80端口被占用的问题
2013/06/21 PHP
Javascript 构造函数 实例分析
2008/11/26 Javascript
JS backgroundImage控制
2009/05/19 Javascript
Javascript 类型转换方法
2010/10/24 Javascript
onmouseover和onmouseout的一些问题思考
2013/08/14 Javascript
javascript使用定时函数实现跳转到某个页面
2013/12/25 Javascript
nodejs创建web服务器之hello world程序
2015/08/20 NodeJs
javascript中获取class的简单实现
2016/07/12 Javascript
利用Js+Css实现折纸动态导航效果实例源码
2017/01/25 Javascript
Vuex简单入门
2017/04/19 Javascript
微信小程序开发图片拖拽实例详解
2017/05/05 Javascript
jQuery实现轮播图及其原理详解
2020/04/12 jQuery
在vue中更换字体,本地存储字体非引用在线字体库的方法
2018/09/28 Javascript
基于vue开发微信小程序mpvue-docs跳转页面功能
2019/04/10 Javascript
angular4应用中输入的最小值和最大值的方法
2019/05/17 Javascript
vue 的 solt 子组件过滤过程解析
2019/09/07 Javascript
详解vue之自行实现派发与广播(dispatch与broadcast)
2021/01/19 Vue.js
python控制台英汉汉英电子词典
2020/04/23 Python
python写的ARP攻击代码实例
2014/06/04 Python
python轻松查到删除自己的微信好友
2016/01/10 Python
python简单实例训练(21~30)
2017/11/15 Python
python-itchat 统计微信群、好友数量,及原始消息数据的实例
2019/02/21 Python
欧尚俄罗斯网上超市:Auchan俄罗斯
2018/05/03 全球购物
Myprotein意大利官网:欧洲第一运动营养品牌
2018/11/22 全球购物
海蓝之谜英国官网:La Mer英国
2020/01/15 全球购物
保安员岗位职责
2013/11/17 职场文书
秘书英文求职信范文
2014/01/31 职场文书
马智宇婚礼主持词
2014/03/22 职场文书
读书伴我成长演讲稿
2014/05/07 职场文书
巾帼标兵事迹材料
2014/12/26 职场文书
新郎婚礼答谢词
2015/01/04 职场文书
2015年档案室工作总结
2015/05/23 职场文书
golang协程池模拟实现群发邮件功能
2021/05/02 Golang
浅析Python OpenCV三种滤镜效果
2022/04/11 Python
win11自动弹出虚拟键盘怎么关闭? Win11关闭虚拟键盘的技巧
2023/01/09 数码科技