canvas仿芝麻信用分仪表盘

rmfi6531 8年前
   <p>这是一个仿支付宝芝麻信用分的一个canvas,其实就是一个动画仪表盘。</p>    <p>首先, 上原图:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/4f64b928b2710b4ad3f438d5b8d6a663.png"></p>    <p>这个是在下支付宝上的截图,分低各位见笑了。然后看下我用canvas实现的效果图:</p>    <pre>  <code class="language-javascript"><canvas id="canvas" width="400" height="700" data-score='724'></canvas>  <!-- 设置data-score,分数区间[400, 900] -->  </code></pre>    <p style="text-align:center"><img src="https://simg.open-open.com/show/15edc230b87dcc3ff3162bd8af25aadd.gif"></p>    <p>唉,总感觉不像。这个是GIF图,可能在网页上打开的效果会好一点(</p>    <p>当然可能就是这样</p>    <p>)。大家可以点击底部预览codepen上的演示。有两个不完美的地方,一个是实际上芝麻信用表盘上的的刻度是不均匀的,我这为了简单的实现就采取相同的刻度;二是表盘上运动的点是有模糊的效果,还没解决。唉,下次再说吧。</p>    <p>接下来还是来说说怎么实现的吧。第一步,国际惯例,创建画布:</p>    <pre>  <code class="language-javascript">var canvas = document.getElementById('canvas'),      ctx = canvas.getContext('2d'),      cWidth = canvas.width,      cHeight = canvas.height;  </code></pre>    <p>然后绘制表盘,虽说不是处女座,但也要尽可能做到跟原图上的一样,那就是这个环形开口的角度是多少呢?请上ps来测一下:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/fb2944e0f94c154c4c844bfb67be35f9.png"></p>    <p>嗯,136°,这个角度确实刁钻,为了方便接下来的计算,那就约等于140°。那么一个分数段的弧度就是:</p>    <pre>  <code class="language-javascript">var deg1 = Math.PI * 11 / 45  </code></pre>    <p>先把中间半透明的刻度层画好:</p>    <pre>  <code class="language-javascript">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();  </code></pre>    <p>接着,画6条刻度线,用for循环来实现:</p>    <pre>  <code class="language-javascript">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();  </code></pre>    <p>同理,再把大刻度细分为5个小刻度:</p>    <pre>  <code class="language-javascript">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();  </code></pre>    <p>刻度到这里就ok了,还需要给刻度标上文字和每个分数段的信用级别,具体的参见代码,因为跟刻度实现的原理差不多,就不啰嗦了。现在最关键就是实现表盘上那个运动的点(不知道怎么称呼,下文就叫它动点),我们可以这样想,它是个半径很小的圆,只不过是画在最外层环形轨道上圆,而圆在 canvas 上的实现方法是:</p>    <pre>  <code class="language-javascript">ctx.arc(x, y, radius, sAngle, eAngle, false);  </code></pre>    <p>我们只要控制x, y就能让它动起来,实现我们想要的效果。so,创建一个动点对象:</p>    <pre>  <code class="language-javascript">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; //信用最低分数  </code></pre>    <p>如何得到dot的坐标x, y呢?那就要用到传说中三角函数了。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/e4aa3e589e3eed1c82dc786b8bc4bd63.png"></p>    <p>通过上图我们可以得到</p>    <pre>  <code class="language-javascript">x = r * cos(angle), y = r * sin(angle)  </code></pre>    <p>在JavaScript中,dot的中心坐标就变成了:</p>    <pre>  <code class="language-javascript">dot.x = radius * Math.cos(angle); //radius为最外层轨道的半径值  dot.y = radius * Math.sin(angle);  </code></pre>    <p>接下来我们只要得到这个angle。这个通过弧度与分数的比例关系就可以得到:</p>    <pre>  <code class="language-javascript">var aim = (score - 400) * deg1 / 100;  if (angle < aim) {    angle += dotSpeed;  }  dot.draw(ctx);  </code></pre>    <p>然后让中间的信用分数也能随动点的转动而变化,创建一个 text() ,为了使数字变化能和动点保持一致,要根据动点的速率来计算数字变化:</p>    <pre>  <code class="language-javascript">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);  </code></pre>    <p>最后这一切都逃不过让window.requestAnimationFrame()来控制绘制动画和用ctx.clearRect(0, 0, cWidth, cHeight)来清除画布。</p>    <p> </p>    <p> </p>    <p>来自:http://www.cnblogs.com/libin-1/p/6068340.html</p>    <p> </p>