
目录
写在前面
完整代码
代码分析
推荐系列
写在后面
写在前面
如何用HTML代码实现爱心+流星雨的动态效果?本期博主将带着大家探索神奇的HTML。
完整代码
<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <title>爱心流星雨</title>
  <style>
    html,
    body {
      height: 100%;
      padding: 0;
      margin: 0;
      background-color: black;
    }
    canvas {
      position: absolute;
      width: 100%;
      height: 100%;
    }
</style>
</head>
<body>
  <canvas id="pinkboard"></canvas>
  <canvas id="stars"></canvas>
  <script>
    var context;
    var arr = new Array();
    var starCount = 800;
    var rains = new Array();
    var rainCount = 20;
    //初始化画布及context
    function init() {
      //获取canvas
      var stars = document.getElementById("stars");
      windowWidth = window.innerWidth; //当前的窗口的高度
      stars.width = windowWidth;
      stars.height = window.innerHeight;
      //获取context
      context = stars.getContext("2d");
    }
    //创建一个星星对象
    var Star = function () {
      this.x = windowWidth * Math.random();//横坐标
      this.y = 5000 * Math.random();//纵坐标
      this.text = "❤";//文本
      this.color = "#ea80b0";//颜色
      //产生随机颜色
      this.getColor = function () {
        var _r = Math.random();
        if (_r < 0.5) {
          this.color = "#333";
        } else {
          this.color = "#ea80b0";
        }
      }
      //初始化
      this.init = function () {
        this.getColor();
      }
      //绘制
      this.draw = function () {
        context.fillStyle = this.color;
        context.fillText(this.text, this.x, this.y);
      }
    }
    //页面加载的时候
    window.onload = function () {
      init();
      //画星星
      for (var i = 0; i < starCount; i++) {
        var star = new Star();
        star.init();
        star.draw();
        arr.push(star);
      }
      //画流星
      for (var i = 0; i < rainCount; i++) {
        var rain = new MeteorRain();
        rain.init();
        rain.draw();
        rains.push(rain);
      }
      playStars();//绘制闪动的星星
      playRains();//绘制流星
    }
    //星星闪起来
    function playStars() {
      for (var n = 0; n < starCount; n++) {
        arr[n].getColor();
        arr[n].draw();
      }
      setTimeout("playStars()", 100);
    }
    var MeteorRain = function () {
      this.x = -1;
      this.y = -1;
      this.length = -1;//长度
      this.angle = 30; //倾斜角度
      this.width = -1;//宽度
      this.height = -1;//高度
      this.speed = 1;//速度
      this.offset_x = -1;//横轴移动偏移量
      this.offset_y = -1;//纵轴移动偏移量
      this.alpha = 1; //透明度
      this.color1 = "#ea80b0";//流星的色彩
      this.color2 = "";  //流星的色彩
      this.init = function () //初始化
{
        this.getPos();
        this.alpha = 1;//透明度
        this.getRandomColor();
        //最小长度,最大长度
        var x = Math.random() * 80 + 150;
        this.length = Math.ceil(x);
        //                  x = Math.random()*10+30;
        this.angle = 30; //流星倾斜角
        x = Math.random() + 0.5;
        this.speed = Math.ceil(x); //流星的速度
        var cos = Math.cos(this.angle * 3.14 / 180);
        var sin = Math.sin(this.angle * 3.14 / 180);
        this.width = this.length * cos;  //流星所占宽度
        this.height = this.length * sin;//流星所占高度
        this.offset_x = this.speed * cos;
        this.offset_y = this.speed * sin;
      }
      this.getRandomColor = function () {
        var a = Math.ceil(255 - 240 * Math.random());
        //中段颜色
        this.color1 = "rgba(" + a + "," + a + "," + a + ",1)";
        //结束颜色
        this.color2 = "black";
      }
      this.countPos = function ()//
{
        //往左下移动,x减少,y增加
        this.x = this.x - this.offset_x;
        this.y = this.y + this.offset_y;
      }
      this.getPos = function () //
{
        //横坐标200--1200
        this.x = Math.random() * window.innerWidth; //窗口高度
        //纵坐标小于600
        this.y = Math.random() * window.innerHeight;  //窗口宽度
      }
      this.draw = function () //绘制一个流星的函数
{
        context.save();
        context.beginPath();
        context.lineWidth = 1; //宽度
        context.globalAlpha = this.alpha; //设置透明度
        //创建横向渐变颜色,起点坐标至终点坐标
        var line = context.createLinearGradient(this.x, this.y,
          this.x + this.width,
          this.y - this.height);
        //分段设置颜色
        line.addColorStop(0, "#ea80b0");
        line.addColorStop(0.3, this.color1);
        line.addColorStop(0.6, this.color2);
        context.strokeStyle = line;
        //起点
        context.moveTo(this.x, this.y);
        //终点
        context.lineTo(this.x + this.width, this.y - this.height);
        context.closePath();
        context.stroke();
        context.restore();
      }
      this.move = function () {
        //清空流星像素
        var x = this.x + this.width - this.offset_x;
        var y = this.y - this.height;
        context.clearRect(x - 3, y - 3, this.offset_x + 5, this.offset_y + 5);
        //                  context.strokeStyle="red";
        //                  context.strokeRect(x,y-1,this.offset_x+1,this.offset_y+1);
        //重新计算位置,往左下移动
        this.countPos();
        //透明度增加
        this.alpha -= 0.002;
        //重绘
        this.draw();
      }
    }
    //绘制流星
    function playRains() {
      for (var n = 0; n < rainCount; n++) {
        var rain = rains[n];
        rain.move();//移动
        if (rain.y > window.innerHeight) {//超出界限后重来
          context.clearRect(rain.x, rain.y - rain.height, rain.width, rain.height);
          rains[n] = new MeteorRain();
          rains[n].init();
        }
      }
      setTimeout("playRains()", 2);
    }
    var settings = {
      particles: {
        length: 500, 
        duration: 2, 
        velocity: 100, 
        effect: -0.75, 
        size: 20, 
      },
    };
    (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) {
        var i;
        if (firstActive < firstFree) {
          for (i = firstActive; i < firstFree; i++)
            particles[i].update(deltaTime);
        }
        if (firstFree < firstActive) {
          for (i = firstActive; i < particles.length; i++)
            particles[i].update(deltaTime);
          for (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 (i = firstActive; i < firstFree; i++)
            particles[i].draw(context, image);
        }
        if (firstFree < firstActive) {
          for (i = firstActive; i < particles.length; i++)
            particles[i].draw(context, image);
          for (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'));
</script>
</body>
</html> 
代码分析
HTML和CSS
页面主要包含两个部分:一个<canvas>标签用于绘制粒子爱心效果,另一个<canvas>标签用于绘制闪烁的心形和流星动画。CSS设置了整个页面的背景颜色为黑色,并使两个画布覆盖整个浏览器窗口。
JavaScript
JavaScript部分主要通过window.onload事件来初始化画布和启动动画。init()函数设置了stars画布的宽度和高度,并获取其绘图上下文。
心形动画
心形动画由一个Star类实现,该类有以下属性和方法:
-  
x:心形的横坐标。 -  
y:心形的纵坐标。 -  
text:显示的文本(心形符号)。 -  
color:心形的颜色。 
-  
getColor():随机设置心形颜色。 -  
init():初始化心形的颜色。 -  
draw():在画布上绘制心形。 
在页面加载时,会创建大量的Star对象,并调用其init()和draw()方法进行绘制。playStars()函数使心形闪烁,通过不断调用getColor()和draw()方法,并使用setTimeout定时器循环实现。
流星动画
流星动画由一个MeteorRain类实现,该类有以下属性和方法:
-  
x和y:流星的起始位置。 -  
length:流星的长度。 -  
angle:流星的倾斜角度。 -  
width和height:流星的宽高。 -  
speed:流星的速度。 -  
offset_x和offset_y:流星的横向和纵向位移量。 -  
alpha:流星的透明度。 -  
color1和color2:流星的颜色(使用渐变效果)。 
-  
init():初始化流星的起始位置、速度和颜色。 -  
getRandomColor():随机生成流星的颜色。 -  
countPos():计算流星的位置。 -  
getPos():生成流星的起始位置。 -  
draw():在画布上绘制流星。 -  
move():更新流星的位置。 
在页面加载时,会创建一定数量的MeteorRain对象,并调用其init()和draw()方法进行绘制。playRains()函数通过不断调用move()方法来更新流星的位置,并使用setTimeout定时器循环实现。
爱心动画
粒子爱心效果由多个类实现,包括Point、Particle和ParticlePool。
-  
Point类:表示二维平面上的一个点,提供克隆、长度和归一化方法。
 -  
Particle类:表示一个粒子,具有位置、速度、加速度和年龄等属性,提供初始化、更新和绘制方法。
 -  
ParticlePool类:管理粒子的池,提供添加、更新和绘制粒子的方法。
 
粒子爱心效果通过pointOnHeart(t)函数生成心形路径上的点。粒子池管理和更新这些粒子,并在每一帧动画中绘制它们。
在初始化时,会创建一个心形路径的粒子图像,并通过requestAnimationFrame实现动画的逐帧渲染。render()函数是动画的核心,负责更新时间、清空画布、创建新粒子、更新和绘制粒子。onResize()函数确保画布在窗口大小改变时自适应调整。
总结
整个代码通过HTML、CSS和JavaScript实现了一个视觉效果丰富的网页动画,展示了闪烁的心形和流星,以及中心的粒子爱心效果。通过使用canvas和逐帧动画技术,代码创造了一个生动的视觉体验。
推荐系列
| 序号 | 目录 | 
| 1 | HTML满屏跳动的爱心(可写字) | 
| 2 | HTML五彩缤纷的爱心 | 
| 3 | HTML满屏漂浮爱心 | 
| 4 | HTML情人节快乐 | 
| 5 | HTML蓝色爱心射线 | 
| 6 | HTML跳动的爱心(简易版) | 
| 7 | HTML粒子爱心 | 
| 8 | HTML蓝色动态爱心 | 
| 9 | HTML跳动的爱心(双心版) | 
| 10 | HTML橙色动态粒子爱心 | 
| 11 | HTML旋转爱心 | 
| 12 | HTML爱情树 | 
| 13 | HTML3D相册 | 
| 14 | HTML旋转相册 | 
| 15 | HTML基础烟花秀 | 
| 16 | HTML炫酷烟花秀 | 
| 17 | HTML粉色烟花秀 | 
| 18 | HTML新春烟花 | 
| 19 | HTML龙年大吉 | 
| 20 | HTML圣诞树 | 
| 21 | HTML大雪纷飞 | 
| 22 | HTML想见你 | 
| 23 | HTML元素周期表 | 
| 24 | HTML飞舞的花瓣 | 
| 25 | HTML星空特效 | 
| 26 | HTML黑客帝国字母雨 | 
| 27 | HTML哆啦A梦 | 
| 28 | HTML流星雨 | 
| 29 | HTML沙漏爱心 | 
| 30 | HTML爱心字母雨 | 
| 31 | HTML爱心流星雨 | 
写在后面
我是一只有趣的兔子,感谢你的喜欢!


















