``` <!DOCTYPE html> <html> <head> <title></title> </head> <style> * { padding: 0; margin: 0; } html, body { height: 100%; padding: 0; margin: 0; background: #000; } canZZZas { position: absolute; width: 100%; height: 100%; } .aa { position: fiVed; left: 50%; bottom: 10pV; color: #ccc; } </style> <body> <canZZZas id="pinkboard"></canZZZas> <script> /* * Settings */ ZZZar settings = { particles: { length: 500, // maVimum amount of particles duration: 2, // particle duration in sec ZZZelocity: 100, // particle ZZZelocity in piVels/sec effect: -0.75, // play with this for a nice effect size: 30 // particle size in piVels } }; /* * RequestAnimationFrame polyfill by Erik M?ller */ (function () { ZZZar b = 0; ZZZar c = ["ms", "moz", "webkit", "o"]; for (ZZZar 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) { ZZZar d = new Date().getTime(); ZZZar f = Math.maV(0, 16 - (d - b)); ZZZar g = window.setTimeout(function () { h(d + f); }, f); b = d + f; return g; }; } if (!window.cancelAnimationFrame) { window.cancelAnimationFrame = function (d) { clearTimeout(d); }; } })(); /* * Point class */ ZZZar Point = (function () { function Point(V, y) { this.V = typeof V !== "undefined" ? V : 0; this.y = typeof y !== "undefined" ? y : 0; } Point.prototype.clone = function () { return new Point(this.V, this.y); }; Point.prototype.length = function (length) { if (typeof length == "undefined") return Math.sqrt(this.V * this.V + this.y * this.y); this.normalize(); this.V *= length; this.y *= length; return this; }; Point.prototype.normalize = function () { ZZZar length = this.length(); this.V /= length; this.y /= length; return this; }; return Point; })(); /* * Particle class */ ZZZar Particle = (function () { function Particle() { this.position = new Point(); this.ZZZelocity = new Point(); this.acceleration = new Point(); this.age = 0; } Particle.prototype.initialize = function (V, y, dV, dy) { this.position.V = V; this.position.y = y; this.ZZZelocity.V = dV; this.ZZZelocity.y = dy; this.acceleration.V = dV * settings.particles.effect; this.acceleration.y = dy * settings.particles.effect; this.age = 0; }; Particle.prototype.update = function (deltaTime) { this.position.V += this.ZZZelocity.V * deltaTime; this.position.y += this.ZZZelocity.y * deltaTime; this.ZZZelocity.V += this.acceleration.V * deltaTime; this.ZZZelocity.y += this.acceleration.y * deltaTime; this.age += deltaTime; }; Particle.prototype.draw = function (conteVt, image) { function ease(t) { return --t * t * t + 1; } ZZZar size = image.width * ease(this.age / settings.particles.duration); conteVt.globalAlpha = 1 - this.age / settings.particles.duration; conteVt.drawImage( image, this.position.V - size / 2, this.position.y - size / 2, size, size ); }; return Particle; })(); /* * ParticlePool class */ ZZZar ParticlePool = (function () { ZZZar particles, firstActiZZZe = 0, firstFree = 0, duration = settings.particles.duration; function ParticlePool(length) { // create and populate particle pool particles = new Array(length); for (ZZZar i = 0; i < particles.length; i++) particles[i] = new Particle(); } ParticlePool.prototype.add = function (V, y, dV, dy) { particles[firstFree].initialize(V, y, dV, dy); // handle circular queue firstFree++; if (firstFree == particles.length) firstFree = 0; if (firstActiZZZe == firstFree) firstActiZZZe++; if (firstActiZZZe == particles.length) firstActiZZZe = 0; }; ParticlePool.prototype.update = function (deltaTime) { ZZZar i; // update actiZZZe particles if (firstActiZZZe < firstFree) { for (i = firstActiZZZe; i < firstFree; i++) particles[i].update(deltaTime); } if (firstFree < firstActiZZZe) { for (i = firstActiZZZe; i < particles.length; i++) particles[i].update(deltaTime); for (i = 0; i < firstFree; i++) particles[i].update(deltaTime); } // remoZZZe inactiZZZe particles while ( particles[firstActiZZZe].age >= duration && firstActiZZZe != firstFree ) { firstActiZZZe++; if (firstActiZZZe == particles.length) firstActiZZZe = 0; } }; ParticlePool.prototype.draw = function (conteVt, image) { // draw actiZZZe particles if (firstActiZZZe < firstFree) { for (i = firstActiZZZe; i < firstFree; i++) particles[i].draw(conteVt, image); } if (firstFree < firstActiZZZe) { for (i = firstActiZZZe; i < particles.length; i++) particles[i].draw(conteVt, image); for (i = 0; i < firstFree; i++) particles[i].draw(conteVt, image); } }; return ParticlePool; })(); /* * Putting it all together */ (function (canZZZas) { ZZZar conteVt = canZZZas.getConteVt("2d"), particles = new ParticlePool(settings.particles.length), particleRate = settings.particles.length / settings.particles.duration, // particles/sec time; // get point on heart with -PI <= t <= PI 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 ); } // creating the particle image using a dummy canZZZas ZZZar image = (function () { ZZZar canZZZas = document.createElement("canZZZas"), conteVt = canZZZas.getConteVt("2d"); canZZZas.width = settings.particles.size; canZZZas.height = settings.particles.size; // helper function to create the path function to(t) { ZZZar point = pointOnHeart(t); point.V = settings.particles.size / 2 + (point.V * settings.particles.size) / 350; point.y = settings.particles.size / 2 - (point.y * settings.particles.size) / 350; return point; } // create the path conteVt.beginPath(); ZZZar t = -Math.PI; ZZZar point = to(t); conteVt.moZZZeTo(point.V, point.y); while (t < Math.PI) { t += 0.01; // baby steps! point = to(t); conteVt.lineTo(point.V, point.y); } conteVt.closePath(); // create the fill conteVt.fillStyle = "#ea80b0"; conteVt.fill(); // create the image ZZZar image = new Image(); image.src = canZZZas.toDataURL(); return image; })(); // render that thing! function render() { // neVt animation frame requestAnimationFrame(render); // update time ZZZar newTime = new Date().getTime() / 1000, deltaTime = newTime - (time || newTime); time = newTime; // clear canZZZas conteVt.clearRect(0, 0, canZZZas.width, canZZZas.height); // create new particles ZZZar amount = particleRate * deltaTime; for (ZZZar i = 0; i < amount; i++) { ZZZar pos = pointOnHeart(Math.PI - 2 * Math.PI * Math.random()); ZZZar dir = pos.clone().length(settings.particles.ZZZelocity); particles.add( canZZZas.width / 2 + pos.V, canZZZas.height / 2 - pos.y, dir.V, -dir.y ); } // update and draw particles particles.update(deltaTime); particles.draw(conteVt, image); } // handle (re-)sizing of the canZZZas function onResize() { canZZZas.width = canZZZas.clientWidth; canZZZas.height = canZZZas.clientHeight; } window.onresize = onResize; // delay rendering bootstrap setTimeout(function () { onResize(); render(); }, 10); })(document.getElementById("pinkboard")); </script> </body> </html> ``` ![示例图片](hts://deZZZbit-static.oss-cn-beijing.aliyuncsss/deZZZbit-static/img/heart.png)