Canvas动画(PC端 移动端)
一,介绍与需求
1.1,介绍
canvas是HTML5中新增一个HTML5标签与操作canvas的javascript API,它可以实现在网页中完成动态的2D与3D图像技术。 标记和 SVG以及 VML 之间的一个重要的不同是, 有一个基于 JavaScript 的绘图 API,而 SVG 和 VML 使用一个 XML 文档来描述绘图。SVG 绘图很容易编辑与生成,但功能明显要弱一些。canvas可以完成动画、游戏、图表、图像处理等原来需要Flash完成的一些功能
1.2,需求
实现特殊的动画效果
二,动画实现
以跟随鼠标/手指移动的火为例
第一步:创建Canvas标签
1
第二步:获取Canvas标签
1 let canvas = document.getElementById('fire');2 if(canvas.getContext){3 var ctx = canvas.getContext('2d');4 //drawing code here
5 } else{6 alert("不支持Canvas")7 }
第三步:绘制火花
1 var Spark = function(mouse) {2
3 this.cx =mouse.x;4 this.cy =mouse.y;5 this.x = rand(this.cx - 40, this.cx + 40);6 this.y = rand(this.cy, this.cy + 5);7 this.lx = this.x;8 this.ly = this.y;9 this.vy = rand(1, 3);10 this.vx = rand(-4, 4);11 this.r = rand(0, 1);12 this.life = rand(4, 5);13 this.alive = true;14 this.c ={15
16 h: Math.floor(rand(2, 40)),17 s: 100,18 l: rand(40, 100),19 a: rand(0.8, 0.9)20
21 }22
23 }24 Spark.prototype.update = function() {25
26 this.lx = this.x;27 this.ly = this.y;28
29 this.y -= this.vy;30 this.x += this.vx;31
32 if (this.x < this.cx)33 this.vx += 0.2;34 else
35 this.vx -= 0.2;36
37 this.vy += 0.08;38 this.life -= 0.1;39
40 if (this.life <= 0) {41
42 this.c.a -= 0.05;43
44 if (this.c.a <= 0)45 this.alive = false;46
47 }48
49 }50 Spark.prototype.draw = function(ctx) {51
52 ctx.beginPath();53 ctx.moveTo(this.lx, this.ly);54 ctx.lineTo(this.x, this.y);55 ctx.strokeStyle = "hsla( " + this.c.h + ", " + this.c.s + "%, " + this.c.l + "%, " + (this.c.a / 2) + ")";56 ctx.lineWidth = this.r * 2;57 ctx.lineCap = 'round';58 ctx.stroke();59 ctx.closePath();60
61 ctx.beginPath();62 ctx.moveTo(this.lx, this.ly);63 ctx.lineTo(this.x, this.y);64 ctx.strokeStyle = "hsla( " + this.c.h + ", " + this.c.s + "%, " + this.c.l + "%, " + this.c.a + ")";65 ctx.lineWidth = this.r;66 ctx.stroke();67 ctx.closePath();68
69 }
第四步:绘制火焰
1 var Flame = function(mouse) {2
3 this.cx =mouse.x;4 this.cy =mouse.y;5 this.x = rand(this.cx - 25, this.cx + 25);6 this.y = rand(this.cy - 5, this.cy + 5);7 this.vy = rand(1, 3);8 this.vx = rand(-1, 1);9 this.r = rand(20, 30);10 this.life = rand(3, 6);11 this.alive = true;12 this.c ={13
14 h: Math.floor(rand(2, 40)),15 s: 100,16 l: rand(80, 100),17 a: 0,18 ta: rand(0.8, 0.9)19
20 }21
22
23 }24 Flame.prototype.update = function() {25
26 this.y -= this.vy;27 this.vy += 0.05;28
29
30 this.x += this.vx;31
32 if (this.x < this.cx)33 this.vx += 0.1;34 else
35 this.vx -= 0.1;36
37
38
39
40 if (this.r > 0)41 this.r -= 0.1;42
43 if (this.r <= 0)44 this.r = 0;45
46
47
48 this.life -= 0.15;49
50 if (this.life <= 0) {51
52 this.c.a -= 0.05;53
54 if (this.c.a <= 0)55 this.alive = false;56
57 } else if (this.life > 0 && this.c.a < this.c.ta) {58
59 this.c.a += .08;60
61 }62
63 }64 Flame.prototype.draw = function(ctx) {65
66 ctx.beginPath();67 ctx.arc(this.x, this.y, this.r * 3, 0, 2 *Math.PI);68 ctx.fillStyle = "hsla( " + this.c.h + ", " + this.c.s + "%, " + this.c.l + "%, " + (this.c.a / 20) + ")";69 ctx.fill();70
71 ctx.beginPath();72 ctx.arc(this.x, this.y, this.r, 0, 2 *Math.PI);73 ctx.fillStyle = "hsla( " + this.c.h + ", " + this.c.s + "%, " + this.c.l + "%, " + this.c.a + ")";74 ctx.fill();75
76 }
第五步:绘制火
1 var Fire = function() {2
3 this.canvas = document.getElementById('fire');4 this.ctx = this.canvas.getContext('2d');5 this.canvas.height = window.innerHeight;//window.innerHeight
6 this.canvas.width = window.innerWidth;//window.innerWidth
7
8 this.aFires =[];9 this.aSpark =[];10 this.aSpark2 =[];11
12
13
14 this.mouse ={15 x: this.canvas.width * .5,16 y: this.canvas.height * .75,17 }18
19
20
21 this.init();22
23 }24 Fire.prototype.init = function() {25 //跳转语句
26 if (system.win || system.mac ||system.xll) {27 this.canvas.addEventListener('mousemove', this.updateMouse.bind(this), false);//PC端
28 } else{29 this.canvas.addEventListener('touchmove', this.updateMouse.bind(this), false);//移动端
30 }31
32
33 }34 Fire.prototype.run = function() {35
36 this.update();37 this.draw();38
39 if (this.bRuning)40 requestAnimationFrame(this.run.bind(this));41
42 }43 Fire.prototype.start = function() {44
45 this.bRuning = true;46 this.run();47
48 }49 Fire.prototype.stop = function() {50
51 this.bRuning = false;52
53 }54 Fire.prototype.update = function() {55
56 this.aFires.push(new Flame(this.mouse));57 this.aSpark.push(new Spark(this.mouse));58 this.aSpark2.push(new Spark(this.mouse));59
60
61
62 for (var i = this.aFires.length - 1; i >= 0; i--) {63
64 if (this.aFires[i].alive)65 this.aFires[i].update();66 else
67 this.aFires.splice(i, 1);68
69 }70
71 for (var i = this.aSpark.length - 1; i >= 0; i--) {72
73 if (this.aSpark[i].alive)74 this.aSpark[i].update();75 else
76 this.aSpark.splice(i, 1);77
78 }79
80
81 for (var i = this.aSpark2.length - 1; i >= 0; i--) {82
83 if (this.aSpark2[i].alive)84 this.aSpark2[i].update();85 else
86 this.aSpark2.splice(i, 1);87
88 }89
90 }91
92 Fire.prototype.draw = function() {93
94 this.ctx.globalCompositeOperation = "source-over";95 this.ctx.fillStyle = "rgba( 15, 5, 2, 1 )";96 this.ctx.fillRect(0, 0, window.innerWidth, window.innerHeight);97
98 this.grd = this.ctx.createRadialGradient(this.mouse.x, this.mouse.y - 200, 200, this.mouse.x, this.mouse.y - 100, 0);99 this.grd.addColorStop(0, "rgb( 15, 5, 2 )");100 this.grd.addColorStop(1, "rgb( 30, 10, 2 )");101 this.ctx.beginPath();102 this.ctx.arc(this.mouse.x, this.mouse.y - 100, 200, 0, 2 *Math.PI);103 this.ctx.fillStyle = this.grd;104 this.ctx.fill();105
106
107 this.ctx.font = "15em Amatic SC";108 this.ctx.textAlign = "center";109 this.ctx.strokeStyle = "rgb(50, 20, 0)";110 this.ctx.fillStyle = "rgb(120, 10, 0)";111 this.ctx.lineWidth = 2;112 this.ctx.strokeText("", this.canvas.width / 2, this.canvas.height * .72);113 this.ctx.fillText("", this.canvas.width / 2, this.canvas.height * .72);114
115
116
117 this.ctx.globalCompositeOperation = "overlay";//or lighter or soft-light
118
119 for (var i = this.aFires.length - 1; i >= 0; i--) {120
121 this.aFires[i].draw(this.ctx);122
123 }124
125 this.ctx.globalCompositeOperation = "soft-light";//"soft-light";//"color-dodge";
126
127 for (var i = this.aSpark.length - 1; i >= 0; i--) {128
129 if ((i % 2) === 0)130 this.aSpark[i].draw(this.ctx);131
132 }133
134
135 this.ctx.globalCompositeOperation = "color-dodge";//"soft-light";//"color-dodge";
136
137 for (var i = this.aSpark2.length - 1; i >= 0; i--) {138
139 this.aSpark2[i].draw(this.ctx);140
141 }142
143
144 }145
146 Fire.prototype.updateMouse = function(e) {147 //跳转语句
148 if (system.win || system.mac || system.xll) {//PC端
149 this.mouse.x =e.clientX;150 this.mouse.y =e.clientY;151 } else {//移动端
152 e.preventDefault();//阻止默认行为
153 this.mouse.x = e.changedTouches[0].clientX;154 this.mouse.y = e.changedTouches[0].clientY;155 }156
157
158 }
第六步:调用
1 varoCanvas;2 init = function() {3
4 oCanvas = newFire();5 oCanvas.start();6
7
8 }9 window.onload = init;
随机数函数
1 rand = function (min, max) { return Math.random() * (max - min) + min; };
效果如下:
三,PC端与移动端处理
3.1,判断是PC端还是移动端
1 //平台、设备和操作系统
2 var system ={3 win: false,4 mac: false,5 xll: false
6 };7 //检测平台
8 var p =navigator.platform;9 system.win = p.indexOf("Win") == 0;10 system.mac = p.indexOf("Mac") == 0;11 system.x11 = (p == "X11") || (p.indexOf("Linux") == 0);12
13 //跳转语句
14 if (system.win || system.mac ||system.xll) {15 this.canvas.addEventListener('mousemove', this.updateMouse.bind(this), false);//PC端 鼠标移动
16 } else{17 this.canvas.addEventListener('touchmove', this.updateMouse.bind(this), false);//移动端 手指滑动
18 }
3.2,PC端还是移动端的事件处理
1 //跳转语句
2 if (system.win || system.mac || system.xll) {//PC端
3 this.mouse.x =e.clientX;4 this.mouse.y =e.clientY;5 } else {//移动端
6 e.preventDefault();//阻止默认行为
7 this.mouse.x = e.changedTouches[0].clientX;8 this.mouse.y = e.changedTouches[0].clientY;9 }
移动端需禁止缩放
1