HTML5簡單入門系列(九)

前言

上篇原本應該就是最後一篇了,可是樓主總以爲沒有寫上一個簡單應用,不算是完整的學習系列。因此增長一篇關於動畫的應用,對一個開源動畫的介紹(很基礎,非樓主原創)。html

本篇介紹一個基於Canvas的發光加載動畫(這裏可下載源碼)。算是對以前系列的各個知識點的一個總結吧。 html5

咱們先看看最終的效果截圖:canvas


 

新建一個html和一個js文件

html文件引入該js,並加了一個背景黑色,大致以下這樣:數組

 1 <!DOCTYPE html>
 2 <html>
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>HTML5 Canvas發光Loading動畫DEMO演示</title>
 6     <style>
 7         body {
 8             background: #000;
 9         }
10     </style>
11 </head>
12 <body>
13     <canvas id="c"></canvas>
14     <script src="js/index.js"></script>
15 </body>
16 </html>

 

在畫圖以前,咱們要肯定一下簡單的數據結構。

看效果圖能夠知道,咱們是要畫弧線,全部的動畫都是基於這個弧線來操做的,那咱們就要肯定該弧線的座標、半徑、弧度起始點等。數據結構

要讓其動起來,即旋轉起來,咱們須要旋轉速度(弧度),當前旋轉角度等。dom

咱們整理成以下變量:ide

 1 var c = document.getElementById('c'),
 2 ctx = c.getContext('2d'),
 3 cw = c.width = 400,
 4 ch = c.height = 300,
 5 rand = function (a, b) { return ~~((Math.random() * (b - a + 1)) + a); },//獲取a到b之間的整數值
 6 dToR = function (degrees) { return degrees * (Math.PI / 180); },//角度變弧度
 7 circle = {
 8 x: (cw / 2) + 5,
 9 y: (ch / 2) + 22,//圓心座標
10 radius: 90,//半徑
11 speed: 2,//旋轉速度(角度)
12 rotation: 0,//當前角度
13 angleStart: 270,//圓弧起始弧度
14 angleEnd: 90,//結束弧度
15 hue: 220,//色調
16 thickness: 18,//邊緣粗細
17 blur: 25//模糊度
18 },
19 particles = [],//亮點數組
20 particleMax = 100,//亮點個數

 

19-29行的變量示例中增長其餘效果時會用到,這裏能夠暫時忽略。函數

畫弧線

該弧線帶有一個漸變效果,咱們會用到的APIs有arc(),createLinearGradient()等,方法以下:oop

 1 renderCircle = function () {
 2         ctx.save();//保存當前做圖上下文
 3         ctx.translate(circle.x, circle.y);//移動座標原點到圓心位置
 4         
 5         ctx.beginPath();
 6         ctx.arc(0, 0, circle.radius, dToR(circle.angleStart), dToR(circle.angleEnd), true);
 7         ctx.lineWidth = circle.thickness;
 8 
 9         var gradient1 = ctx.createLinearGradient(0, -circle.radius, 0, circle.radius);
10         gradient1.addColorStop(0, 'hsla(' + circle.hue + ', 60%, 50%, .25)');
11         gradient1.addColorStop(1, 'hsla(' + circle.hue + ', 60%, 50%, 0)');
12 
13         ctx.strokeStyle = gradient1;//弧線是漸變色
14         ctx.stroke();
15         ctx.restore();//畫完弧線以後,畫其餘東西以前,先恢復做圖上下文
16     },

 

這裏就不截圖看效果了,就是一條漸變弧線。學習

旋轉

這裏旋轉其實就很簡單了,只要不停地旋轉角度,在畫弧線以前調用旋轉API rotate()就能夠了。下邊是代碼:

1 updateCircle = function () {
2         if (circle.rotation < 360) {
3             circle.rotation += circle.speed;
4         } else {
5             circle.rotation = 0;
6         }
7     }

 

咱們能夠在畫弧線的方法中加入旋轉角度(renderCircle 方法第四行加上下邊這句便可):

ctx.rotate(dToR(circle.rotation));//旋轉角度,此處是動畫動起來的地方。

 

說到這裏,其實關於動畫的內容就寫的差很少了,只要一個定時器不停地調用這兩個方法就行了。

不過,要注意一點,每次從新畫圖以前咱們都要將canvas清空,咱們調用clearRect或者統一寫一個初始化方法便可,以下:

1 clear = function () {
2         ctx.globalCompositeOperation = 'destination-out';
3         ctx.fillStyle = 'rgba(0, 0, 0, .1)';
4         ctx.fillRect(0, 0, cw, ch);
5         ctx.globalCompositeOperation = 'lighter';
6     }

 

定時器統一調用方法:

1 loop = function () {
2     clear();
3     updateCircle();
4     renderCircle();
5 }

 

設置定時器:

setInterval(loop, 20);

 

如今的效果已經出來了:

剩下的就是給弧線增長效果了。

該示例中爲了更好地展現效果,設置了弧線邊緣,頭部發亮及小亮點顯隱等多個效果,園友可運行代碼查看效果。 

完整的js代碼以下:

  1 /* super inefficient right now, could be improved */
  2 
  3 //http://www.html5tricks.com/html5-canvas-shine-loading.html
  4 
  5 var c = document.getElementById('c'),
  6     ctx = c.getContext('2d'),
  7     cw = c.width = 400,
  8     ch = c.height = 300,
  9     rand = function (a, b) { return ~~((Math.random() * (b - a + 1)) + a); },//獲取a到b之間的整數值
 10     dToR = function (degrees) { return degrees * (Math.PI / 180); },//角度變弧度
 11     circle = {
 12         x: (cw / 2) + 5,
 13         y: (ch / 2) + 22,//圓心座標
 14         radius: 90,//半徑
 15         speed: 2,//旋轉速度(角度)
 16         rotation: 0,//當前角度
 17         angleStart: 270,//圓弧起始弧度
 18         angleEnd: 90,//結束弧度
 19         hue: 220,//色調
 20         thickness: 18,//邊緣粗細
 21         blur: 25//模糊度
 22     },
 23     particles = [],//亮點數組
 24     particleMax = 100,//亮點個數
 25 
 26     //更新旋轉角度
 27     updateCircle = function () {
 28         if (circle.rotation < 360) {
 29             circle.rotation += circle.speed;
 30         } else {
 31             circle.rotation = 0;
 32         }
 33     },
 34     //畫弧線
 35     renderCircle = function () {
 36         ctx.save();//保存當前做圖上下文
 37         ctx.translate(circle.x, circle.y);//移動座標原點到圓心位置
 38         ctx.rotate(dToR(circle.rotation));//旋轉角度,此處是動畫動起來的地方。
 39         ctx.beginPath();
 40         ctx.arc(0, 0, circle.radius, dToR(circle.angleStart), dToR(circle.angleEnd), true);
 41         ctx.lineWidth = circle.thickness;
 42 
 43         var gradient1 = ctx.createLinearGradient(0, -circle.radius, 0, circle.radius);
 44         gradient1.addColorStop(0, 'hsla(' + circle.hue + ', 60%, 50%, .25)');
 45         gradient1.addColorStop(1, 'hsla(' + circle.hue + ', 60%, 50%, 0)');
 46 
 47         ctx.strokeStyle = gradient1;//弧線是漸變色
 48         ctx.stroke();
 49         ctx.restore();//畫完弧線以後,畫其餘東西以前,先恢復做圖上下文
 50     },
 51     //弧線邊緣效果
 52     renderCircleBorder = function () {
 53         ctx.save();
 54         ctx.translate(circle.x, circle.y);
 55         ctx.rotate(dToR(circle.rotation));
 56         ctx.beginPath();
 57         ctx.arc(0, 0, circle.radius + (circle.thickness / 2), dToR(circle.angleStart), dToR(circle.angleEnd), true);
 58         ctx.lineWidth = 2;
 59 
 60         var gradient2 = ctx.createLinearGradient(0, -circle.radius, 0, circle.radius);
 61         gradient2.addColorStop(0, 'hsla(' + circle.hue + ', 100%, 50%, 0)');
 62         gradient2.addColorStop(.1, 'hsla(' + circle.hue + ', 100%, 100%, .7)');
 63         gradient2.addColorStop(1, 'hsla(' + circle.hue + ', 100%, 50%, 0)');
 64 
 65         ctx.strokeStyle = gradient2;
 66         ctx.stroke();
 67         ctx.restore();
 68     },
 69     //弧線頂點發亮
 70     renderCircleFlare = function () {
 71         ctx.save();
 72         ctx.translate(circle.x, circle.y);
 73         ctx.rotate(dToR(circle.rotation + 185));
 74         ctx.scale(1, 1);
 75         ctx.beginPath();
 76         ctx.arc(0, circle.radius, 30, 0, Math.PI * 2, false);
 77         ctx.closePath();
 78         var gradient3 = ctx.createRadialGradient(0, circle.radius, 0, 0, circle.radius, 30);
 79         gradient3.addColorStop(0, 'hsla(330, 50%, 50%, .35)');
 80         gradient3.addColorStop(1, 'hsla(330, 50%, 50%, 0)');
 81         ctx.fillStyle = gradient3;
 82         ctx.fill();
 83         ctx.restore();
 84     },
 85     //發亮延伸
 86     renderCircleFlare2 = function () {
 87         ctx.save();
 88         ctx.translate(circle.x, circle.y);
 89         ctx.rotate(dToR(circle.rotation + 165));
 90         ctx.scale(1.5, 1);
 91         ctx.beginPath();
 92         ctx.arc(0, circle.radius, 25, 0, Math.PI * 2, false);
 93         ctx.closePath();
 94         var gradient4 = ctx.createRadialGradient(0, circle.radius, 0, 0, circle.radius, 25);
 95         gradient4.addColorStop(0, 'hsla(30, 100%, 50%, .2)');
 96         gradient4.addColorStop(1, 'hsla(30, 100%, 50%, 0)');
 97         ctx.fillStyle = gradient4;
 98         ctx.fill();
 99         ctx.restore();
100     },
101     //建立亮點
102     createParticles = function () {
103         if (particles.length < particleMax) {
104             particles.push({
105                 x: (circle.x + circle.radius * Math.cos(dToR(circle.rotation - 85))) + (rand(0, circle.thickness * 2) - circle.thickness),
106                 y: (circle.y + circle.radius * Math.sin(dToR(circle.rotation - 85))) + (rand(0, circle.thickness * 2) - circle.thickness),
107                 vx: (rand(0, 100) - 50) / 1000,
108                 vy: (rand(0, 100) - 50) / 1000,
109                 radius: rand(1, 6) / 2,
110                 alpha: rand(10, 20) / 100
111             });
112         }
113     },
114     //更新已有亮點的座標用於動態更隨展現
115     //同時下降透明度,刪除透明度太低(先建立的點調用該函數的次數最多,也就最容易變低)的亮點。
116     updateParticles = function () {
117         var i = particles.length;
118         while (i--) {
119             var p = particles[i];
120             p.vx += (rand(0, 100) - 50) / 750;
121             p.vy += (rand(0, 100) - 50) / 750;
122             p.x += p.vx;
123             p.y += p.vy;
124             p.alpha -= .01;
125 
126             if (p.alpha < .02) {
127                 particles.splice(i, 1)
128             }
129         }
130     },
131     renderParticles = function () {
132         var i = particles.length;
133         while (i--) {
134             var p = particles[i];
135             ctx.beginPath();
136             ctx.fillRect(p.x, p.y, p.radius, p.radius);
137             ctx.closePath();
138             ctx.fillStyle = 'hsla(0, 0%, 100%, ' + p.alpha + ')';
139         }
140     },
141     clear = function () {
142         ctx.globalCompositeOperation = 'destination-out';
143         ctx.fillStyle = 'rgba(0, 0, 0, .1)';
144         ctx.fillRect(0, 0, cw, ch);
145         ctx.globalCompositeOperation = 'lighter';
146     }
147 loop = function () {
148     clear();
149     updateCircle();
150     renderCircle();
151     renderCircleBorder();
152     renderCircleFlare();
153     renderCircleFlare2();
154     createParticles();
155     updateParticles();
156     renderParticles();
157 }
158 
159 
160 ctx.shadowBlur = circle.blur;
161 ctx.shadowColor = 'hsla(' + circle.hue + ', 80%, 60%, 1)';
162 ctx.lineCap = 'round';
163 
164 setInterval(loop, 20);
View Code

  

小結

本篇沒有什麼內容,只是一個簡單應用。

其實在上篇狀態的保存和恢復中那個示例就是一個簡單動畫了。

其實HTML5中動畫也不算難,簡單說就是畫圖+定時刷新,因此重點仍是在HTML5 APIs的應用及與其餘現有技術的交叉使用。 

相關文章
相關標籤/搜索