2025年3月14日 星期五 甲辰(龙)年 月十三 设为首页 加入收藏
rss
您当前的位置:首页 > 计算机 > 编程开发 > Html+Div+Css(前端)

Css动画:爱心动画效果实现

时间:02-12来源:作者:点击数:23

前言

本文将详细介绍一个基于HTML5 Canvas实现的爱心动画案例,从代码结构到具体实现细节,帮助读者深入理解前端动画开发的技术要点。

一、项目概述

1.1 功能介绍

本项目实现了一个动态的爱心动画效果。当用户移动鼠标时,页面上会显示多个跟随鼠标移动的小爱心图标,并且在画布上生成动态的爱心粒子效果。这些粒子随着时间的变化逐渐消失,形成一种梦幻般的效果。

1.2 技术栈
  • HTML5:用于创建基本的页面结构。
  • CSS3:用于设置页面样式。
  • JavaScript:用于实现动画逻辑。

二、代码解析

2.1 HTML 结构
  • <!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>
2.1.1 页面结构
  • <canvas> 标签:用于绘制动态的爱心粒子效果。
  • <div> 标签:用于显示跟随鼠标移动的小爱心图标。
2.1.2 样式设置
  • html, body:设置页面的高度为100%,去除默认的内边距和外边距。
  • canvas:设置画布的位置为绝对定位,宽度和高度为100%,背景色为黑色。
  • div:设置每个小爱心图标的大小为20x20像素,位置为绝对定位。
  • div img:设置小爱心图标的大小为20x20像素。
2.2 JavaScript 实现
2.2.1 鼠标事件处理
  • 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());
  • }
2.2.1.1 onmousemove 事件
  • event 对象:获取当前鼠标事件对象。
  • oDivs[0]:将第一个小爱心图标的位置设置为鼠标当前位置。
  • for 循环:将其他小爱心图标的位置依次设置为前一个小爱心图标的位置,形成跟随效果。
2.2.1.2 onmousedown 事件
  • console.log(new Date().getSeconds()):记录鼠标点击时的时间秒数。
2.2.2 粒子系统实现
2.2.2.1 设置
  • var settings = {
  •   particles: {
  •       length: 500, // 最大粒子数量
  •       duration: 2, // 粒子持续时间(秒)
  •       velocity: 100, // 粒子速度(像素/秒)
  •       effect: -0.75, // 效果参数
  •       size: 50, // 粒子大小(像素)
  •   },
  • };
  • length:最大粒子数量。
  • duration:粒子持续时间。
  • velocity:粒子速度。
  • effect:效果参数,用于调整粒子的运动轨迹。
  • size:粒子大小。
2.2.2.2 requestAnimationFrame 兼容性处理
  • (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);
  •       }
  •   }
  • })();
  • requestAnimationFrame:用于请求浏览器在下一次重绘之前调用指定的函数。
  • cancelAnimationFrame:用于取消通过 requestAnimationFrame 注册的回调函数。
2.2.2.3 Point 类
  • 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;
  • })();
  • Point 类:表示一个二维坐标点。
  • clone 方法:返回一个新的 Point 对象,其坐标值与当前对象相同。
  • length 方法:计算点的长度,或设置点的长度。
  • normalize 方法:将点归一化,使其长度为1。
2.2.2.4 Particle 类
  • 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;
  • })();
  • Particle 类:表示一个粒子。
  • initialize 方法:初始化粒子的位置、速度和加速度。
  • update 方法:更新粒子的位置、速度和年龄。
  • draw 方法:在画布上绘制粒子,使用 ease 函数控制粒子的大小和透明度。
2.2.2.5 ParticlePool 类
  • 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;
  • })();
  • ParticlePool 类:管理粒子池,包括添加、更新和绘制粒子。
  • add 方法:向粒子池中添加新的粒子。
  • update 方法:更新所有活动粒子的状态。
  • draw 方法:在画布上绘制所有活动粒子。
2.2.2.6 主程序
  • (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'));
  • pointOnHeart 函数:计算心形路径上的点。
  • image 变量:创建一个表示心形图案的图像。
  • render 函数:主渲染循环,负责更新和绘制粒子。
  • onResize 函数:处理窗口大小变化,重新设置画布的尺寸。
  • setTimeout:延迟启动渲染循环,确保初始渲染时画布尺寸正确。

三、总结

本文详细介绍了如何使用HTML5 Canvas实现一个动态的爱心动画效果。通过解析代码,我们了解了粒子系统的实现原理,以及如何利用JavaScript和Canvas API进行动画渲染。希望本文能为前端开发者提供一些灵感和技术参考,帮助大家在项目中实现更多有趣的效果。

四、扩展阅读

  • HTML5 Canvas 官方文档:https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API
  • JavaScript 实现动画效果:https://www.cdsy.xyz/computer/programme/js/20210307/cd161511120212595.html
  • 粒子系统原理:https://en.wikipedia.org/wiki/Particle_system

通过学习这些资源,读者可以进一步深入了解和掌握前端动画开发的相关知识和技术。

方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门
本栏推荐