本文将详细介绍一个基于HTML5 Canvas实现的爱心动画案例,从代码结构到具体实现细节,帮助读者深入理解前端动画开发的技术要点。
本项目实现了一个动态的爱心动画效果。当用户移动鼠标时,页面上会显示多个跟随鼠标移动的小爱心图标,并且在画布上生成动态的爱心粒子效果。这些粒子随着时间的变化逐渐消失,形成一种梦幻般的效果。
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
- <title>前端青山</title>
- <style>
- html, body {
- height: 100%;
- padding: 0;
- margin: 0;
- }
- canvas {
- position: absolute;
- width: 100%;
- height: 100%;
- background-color: black;
- }
- div {
- width: 20px;
- height: 20px;
- position: absolute;
- }
- div img {
- width: 20px;
- height: 20px;
- }
- </style>
- </head>
- <body>
- <canvas id="pinkboard" width="805" height="946">
- Canvas Not Support
- </canvas>
- <div><img src="img/Heart.png"/></div>
- <div><img src="img/Heart.png"/></div>
- <div><img src="img/Heart.png"/></div>
- <div><img src="img/Heart.png"/></div>
- <div><img src="img/Heart.png"/></div>
- <div><img src="img/Heart.png"/></div>
- <div><img src="img/Heart.png"/></div>
- <div><img src="img/Heart.png"/></div>
- </body>
- </html>
- var oDivs = document.querySelectorAll("div");
- document.onmousemove = function (event) {
- var event = event || window.event;
- oDivs[0].style.top = event.clientY + "px";
- oDivs[0].style.left = event.clientX + "px";
- for (var i = oDivs.length - 1; i > 0; i--) {
- oDivs[i].style.top = oDivs[i-1].offsetTop + "px";
- oDivs[i].style.left = oDivs[i-1].offsetLeft + "px";
- }
- }
- document.onmousedown = function (event) {
- console.log(new Date().getSeconds());
- }
- var settings = {
- particles: {
- length: 500, // 最大粒子数量
- duration: 2, // 粒子持续时间(秒)
- velocity: 100, // 粒子速度(像素/秒)
- effect: -0.75, // 效果参数
- size: 50, // 粒子大小(像素)
- },
- };
- (function () {
- var b = 0; var c = ["ms", "moz", "webkit", "o"];
- for (var a = 0; a < c.length && !window.requestAnimationFrame; ++a) {
- window.requestAnimationFrame = window[c[a] + "RequestAnimationFrame"];
- window.cancelAnimationFrame = window[c[a] + "CancelAnimationFrame"] || window[c[a] + "CancelRequestAnimationFrame"];
- }
- if (!window.requestAnimationFrame) {
- window.requestAnimationFrame = function (h, e) {
- var d = new Date().getTime();
- var f = Math.max(0, 16 - (d - b));
- var g = window.setTimeout(function () { h(d + f) }, f);
- b = d + f;
- return g;
- }
- }
- if (!window.cancelAnimationFrame) {
- window.cancelAnimationFrame = function (d) {
- clearTimeout(d);
- }
- }
- })();
- var Point = (function () {
- function Point(x, y) {
- this.x = (typeof x !== 'undefined') ? x : 0;
- this.y = (typeof y !== 'undefined') ? y : 0;
- }
- Point.prototype.clone = function () {
- return new Point(this.x, this.y);
- };
- Point.prototype.length = function (length) {
- if (typeof length == 'undefined')
- return Math.sqrt(this.x * this.x + this.y * this.y);
- this.normalize();
- this.x *= length;
- this.y *= length;
- return this;
- };
- Point.prototype.normalize = function () {
- var length = this.length();
- this.x /= length;
- this.y /= length;
- return this;
- };
- return Point;
- })();
- var Particle = (function () {
- function Particle() {
- this.position = new Point();
- this.velocity = new Point();
- this.acceleration = new Point();
- this.age = 0;
- }
- Particle.prototype.initialize = function (x, y, dx, dy) {
- this.position.x = x;
- this.position.y = y;
- this.velocity.x = dx;
- this.velocity.y = dy;
- this.acceleration.x = dx * settings.particles.effect;
- this.acceleration.y = dy * settings.particles.effect;
- this.age = 0;
- };
- Particle.prototype.update = function (deltaTime) {
- this.position.x += this.velocity.x * deltaTime;
- this.position.y += this.velocity.y * deltaTime;
- this.velocity.x += this.acceleration.x * deltaTime;
- this.velocity.y += this.acceleration.y * deltaTime;
- this.age += deltaTime;
- };
- Particle.prototype.draw = function (context, image) {
- function ease(t) {
- return (--t) * t * t + 1;
- }
- var size = image.width * ease(this.age / settings.particles.duration);
- context.globalAlpha = 1 - this.age / settings.particles.duration;
- context.drawImage(image, this.position.x - size / 2, this.position.y - size / 2, size, size);
- };
- return Particle;
- })();
- var ParticlePool = (function () {
- var particles,
- firstActive = 0,
- firstFree = 0,
- duration = settings.particles.duration;
-
- function ParticlePool(length) {
- particles = new Array(length);
- for (var i = 0; i < particles.length; i++)
- particles[i] = new Particle();
- }
- ParticlePool.prototype.add = function (x, y, dx, dy) {
- particles[firstFree].initialize(x, y, dx, dy);
- firstFree++;
- if (firstFree == particles.length) firstFree = 0;
- if (firstActive == firstFree) firstActive++;
- if (firstActive == particles.length) firstActive = 0;
- };
- ParticlePool.prototype.update = function (deltaTime) {
- if (firstActive < firstFree) {
- for (var i = firstActive; i < firstFree; i++)
- particles[i].update(deltaTime);
- }
- if (firstFree < firstActive) {
- for (var i = firstActive; i < particles.length; i++)
- particles[i].update(deltaTime);
- for (var i = 0; i < firstFree; i++)
- particles[i].update(deltaTime);
- }
- while (particles[firstActive].age >= duration && firstActive != firstFree) {
- firstActive++;
- if (firstActive == particles.length) firstActive = 0;
- }
- };
- ParticlePool.prototype.draw = function (context, image) {
- if (firstActive < firstFree) {
- for (var i = firstActive; i < firstFree; i++)
- particles[i].draw(context, image);
- }
- if (firstFree < firstActive) {
- for (var i = firstActive; i < particles.length; i++)
- particles[i].draw(context, image);
- for (var i = 0; i < firstFree; i++)
- particles[i].draw(context, image);
- }
- };
- return ParticlePool;
- })();
- (function (canvas) {
- var context = canvas.getContext('2d'),
- particles = new ParticlePool(settings.particles.length),
- particleRate = settings.particles.length / settings.particles.duration, // 粒子/秒
- time;
-
- function pointOnHeart(t) {
- return new Point(
- 160 * Math.pow(Math.sin(t), 3),
- 130 * Math.cos(t) - 50 * Math.cos(2 * t) - 20 * Math.cos(3 * t) - 10 * Math.cos(4 * t) + 25
- );
- }
-
- var image = (function () {
- var canvas = document.createElement('canvas'),
- context = canvas.getContext('2d');
- canvas.width = settings.particles.size;
- canvas.height = settings.particles.size;
-
- function to(t) {
- var point = pointOnHeart(t);
- point.x = settings.particles.size / 2 + point.x * settings.particles.size / 350;
- point.y = settings.particles.size / 2 - point.y * settings.particles.size / 350;
- return point;
- }
-
- context.beginPath();
- var t = -Math.PI;
- var point = to(t);
- context.moveTo(point.x, point.y);
- while (t < Math.PI) {
- t += 0.01;
- point = to(t);
- context.lineTo(point.x, point.y);
- }
- context.closePath();
- context.fillStyle = '#ea80b0';
- context.fill();
-
- var image = new Image();
- image.src = canvas.toDataURL();
- return image;
- })();
-
- function render() {
- requestAnimationFrame(render);
-
- var newTime = new Date().getTime() / 1000,
- deltaTime = newTime - (time || newTime);
- time = newTime;
-
- context.clearRect(0, 0, canvas.width, canvas.height);
-
- var amount = particleRate * deltaTime;
- for (var i = 0; i < amount; i++) {
- var pos = pointOnHeart(Math.PI - 2 * Math.PI * Math.random());
- var dir = pos.clone().length(settings.particles.velocity);
- particles.add(canvas.width / 2 + pos.x, canvas.height / 2 - pos.y, dir.x, -dir.y);
- }
-
- particles.update(deltaTime);
- particles.draw(context, image);
- }
-
- function onResize() {
- canvas.width = canvas.clientWidth;
- canvas.height = canvas.clientHeight;
- }
- window.onresize = onResize;
-
- setTimeout(function () {
- onResize();
- render();
- }, 10);
- })(document.getElementById('pinkboard'));
本文详细介绍了如何使用HTML5 Canvas实现一个动态的爱心动画效果。通过解析代码,我们了解了粒子系统的实现原理,以及如何利用JavaScript和Canvas API进行动画渲染。希望本文能为前端开发者提供一些灵感和技术参考,帮助大家在项目中实现更多有趣的效果。
通过学习这些资源,读者可以进一步深入了解和掌握前端动画开发的相关知识和技术。