canvas仿芝麻信用分儀表盤

這是一個仿支付寶芝麻信用分的一個canvas,其實就是一個動畫儀表盤。javascript

首先, 上原圖:css

 

這個是在下支付寶上的截圖,分低各位見笑了。而後看下我用canvas實現的效果圖:html

<canvas id="canvas" width="400" height="700" data-score='724'></canvas>
<!-- 設置data-score,分數區間[400, 900] -->

 

唉,總感受不像。這個是GIF圖,可能在網頁上打開的效果會好一點(固然可能就是這樣)。你們能夠點擊底部預覽codepen上的演示。有兩個不完美的地方,一個是實際上芝麻信用錶盤上的的刻度是不均勻的,我這爲了簡單的實現就採起相同的刻度;二是錶盤上運動的點是有模糊的效果,還沒解決。唉,下次再說吧。java

接下來仍是來講說怎麼實現的吧。第一步,國際慣例,建立畫布:web

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

而後繪製錶盤,雖然說不是處女座,但也要儘量作到跟原圖上的同樣,那就是這個環形開口的角度是多少呢?請上ps來測一下: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循環來實現:this

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個小刻度:spa

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了,還須要給刻度標上文字和每一個分數段的信用級別,具體的參見代碼,由於跟刻度實現的原理差很少,就不囉嗦了。如今最關鍵就是實現錶盤上那個運動的點(不知道怎麼稱呼,下文就叫它動點),咱們能夠這樣想,它是個半徑很小的圓,只不過是畫在最外層環形軌道上圓,而圓在canvas上的實現方法是:

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呢?那就要用到傳說中三角函數了。

 

經過上圖咱們能夠獲得

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:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

<!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>

 

    </body>

 

</html>

相關文章
相關標籤/搜索