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>

在画图之前,我们要确定一下简单的数据结构。

看效果图可以知道,我们是要画弧线,所有的动画都是基于这个弧线来操作的,那我们就要确定该弧线的坐标、半径、弧度起始点等。

要让其动起来,即旋转起来,我们需要旋转速度(弧度),当前旋转角度等。

我们整理成如下变量:

 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()等,方法如下:

 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的应用及与其他现有技术的交叉使用。 

原文地址:https://www.cnblogs.com/cotton/p/4422147.html