HTML飞舞的爱心

news2024/11/28 2:45:23

目录

系列文章

写在前面

完整代码

代码分析

写在后面


系列文章

序号目录
1HTML满屏跳动的爱心(可写字)
2HTML五彩缤纷的爱心
3HTML满屏漂浮爱心
4HTML情人节快乐
5HTML蓝色爱心射线
6HTML跳动的爱心(简易版)
7HTML粒子爱心
8HTML蓝色动态爱心
9HTML跳动的爱心(双心版)
10HTML橙色动态粒子爱心
11HTML旋转爱心
12HTML爱情树
13HTML3D相册
14HTML旋转相册
15HTML基础烟花秀
16HTML炫酷烟花秀
17HTML粉色烟花秀
18HTML新春烟花
19HTML龙年大吉
20HTML圣诞树
21HTML大雪纷飞
22HTML想见你
23HTML元素周期表
24HTML飞舞的花瓣
25HTML星空特效
26HTML黑客帝国字母雨
27HTML哆啦A梦
28HTML流星雨
29HTML沙漏爱心
30HTML爱心字母雨
31HTML爱心流星雨
32HTML生日蛋糕
33HTML3D旋转相册
34HTML流光爱心
35HTML满屏飘字
36HTML飞舞爱心

写在前面

HTML语言实现飞舞的爱心完整代码。

完整代码

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>飞舞爱心</title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }

    html,
    body {
      overflow: hidden;
    }

    body {
      position: relative;
      background: #000;
    }
  </style>

</head>

<body>
  <!-- partial:index.partial.html -->

  <!-- partial -->
  <script>


    class Tool {
      // random number.
      static randomNumber(min, max) {
        return Math.floor(Math.random() * (max - min + 1) + min);
      }
      // random color rgb.
      static randomColorRGB() {
        return (
          "rgb(" +
          this.randomNumber(0, 255) +
          ", " +
          this.randomNumber(0, 255) +
          ", " +
          this.randomNumber(0, 255) +
          ")"
        );
      }
      // random color hsl.
      static randomColorHSL(hue, saturation, lightness) {
        return (
          "hsl(" +
          hue +
          ", " +
          saturation +
          "%, " +
          lightness +
          "%)"
        );
      }
      // gradient color.
      static gradientColor(ctx, cr, cg, cb, ca, x, y, r) {
        const col = cr + "," + cg + "," + cb;
        const g = ctx.createRadialGradient(x, y, 0, x, y, r);
        g.addColorStop(0, "rgba(" + col + ", " + (ca * 1) + ")");
        g.addColorStop(0.5, "rgba(" + col + ", " + (ca * 0.5) + ")");
        g.addColorStop(1, "rgba(" + col + ", " + (ca * 0) + ")");
        return g;
      }
    }

    /*
      When want to use Angle and radian.
    */

    class Angle {
      constructor(a) {
        this.a = a;
        this.rad = (this.a * Math.PI) / 180;
      }

      incDec(num) {
        this.a += num;
        this.rad = (this.a * Math.PI) / 180;
      }
    }

    /*
      variable for canvas.
    */

    let canvas;
    let offCanvas;

    class Canvas {
      constructor(bool) {
        // create canvas.
        this.canvas = document.createElement("canvas");
        // if on screen.
        if (bool === true) {
          this.canvas.style.position = 'relative';
          this.canvas.style.display = 'block';
          this.canvas.style.top = 0;
          this.canvas.style.left = 0;
          document.getElementsByTagName("body")[0].appendChild(this.canvas);
        }
        this.ctx = this.canvas.getContext("2d");
        this.width = this.canvas.width = window.innerWidth;
        this.height = this.canvas.height = window.innerHeight;
        // size.
        this.width < 768 ? this.heartSize = 180 : this.heartSize = 250;
        // mouse infomation.
        this.mouseX = null;
        this.mouseY = null;
        // sprite array and quantity.
        this.hearts = [];
        this.offHeartNum = 1;
        this.offHearts = [];
        // offscreen data.
        this.data = null;
      }

      onInit() {
        let index = 0;
        for (let i = 0; i < this.height; i += 12) {
          for (let j = 0; j < this.width; j += 12) {
            let oI = (j + i * this.width) * 4 + 3;
            if (this.data[oI] > 0) {
              index++;
              const h = new Heart(canvas.ctx, j + Tool.randomNumber(-3, 3), i + Tool.randomNumber(-3, 3), Tool.randomNumber(6, 12), index);
              canvas.hearts.push(h);
            }
          }
        }
      }

      offInit() {
        for (let i = 0; i < this.offHeartNum; i++) {
          const s = new Heart(this.ctx, this.width / 2, this.height / 2.3, this.heartSize);
          this.offHearts.push(s);
        }
        for (let i = 0; i < this.offHearts.length; i++) {
          this.offHearts[i].offRender(i);
        }
        // data
        this.data = this.ctx.getImageData(0, 0, this.width, this.height).data;
        // on screen init.
        this.onInit();
      }

      render() {
        this.ctx.clearRect(0, 0, this.width, this.height);
        for (let i = 0; i < this.hearts.length; i++) {
          this.hearts[i].render(i);
        }
      }

      resize() {
        this.offHearts = [];
        this.hearts = [];
        this.width = this.canvas.width = window.innerWidth;
        this.height = this.canvas.height = window.innerHeight;
        this.width < 768 ? this.heartSize = 180 : this.heartSize = 250;
      }
    }

    class Heart {
      constructor(ctx, x, y, r, i) {
        this.ctx = ctx;
        this.init(x, y, r, i);
      }

      init(x, y, r, i) {
        this.x = x;
        this.xi = x;
        this.y = y;
        this.yi = y;
        this.r = r;
        this.i = i * 0.5 + 200;
        this.l = this.i;
        this.c = `hsl(330, ${Tool.randomNumber(90, 100)}%, ${Tool.randomNumber(65, 75)}%)`;
        this.a = new Angle(Tool.randomNumber(0, 360));
        this.v = {
          x: Math.random(),
          y: -Math.random()
        };
        this.ga = Math.random();
      }

      draw() {
        const ctx = this.ctx;
        ctx.save();
        ctx.globalCompositeOperation = 'lighter';
        ctx.globalAlpha = this.ga;
        ctx.beginPath();
        ctx.fillStyle = this.c;
        ctx.moveTo(this.x, this.y + this.r);
        ctx.bezierCurveTo(
          this.x - this.r - this.r / 5,
          this.y + this.r / 1.5,
          this.x - this.r,
          this.y - this.r,
          this.x,
          this.y - this.r / 5
        );
        ctx.bezierCurveTo(
          this.x + this.r,
          this.y - this.r,
          this.x + this.r + this.r / 5,
          this.y + this.r / 1.5,
          this.x,
          this.y + this.r
        );
        ctx.closePath();
        ctx.fill();
        ctx.restore();
      }

      updateParams() {
        this.a.incDec(1);
        Math.sin(this.a.rad) < 0 ? this.r = -Math.sin(this.a.rad) * 20 : this.r = Math.sin(this.a.rad) * 20;
      }

      updatePosition() {
        this.l -= 1;
        if (this.l < 0) {
          this.v.y -= 0.01;
          this.v.x += 0.02;
          this.y += this.v.y;
          this.x += this.v.x;
        }
      }

      wrapPosition() {
        if (this.x > canvas.width * 1.5) {
          this.init(this.xi, this.yi, Tool.randomNumber(6, 12), this.i);
        }
      }

      render() {
        this.wrapPosition();
        this.updateParams();
        this.updatePosition();
        this.draw();
      }

      offRender(i) {
        this.draw();
      }
    }

    (function () {
      "use strict";
      window.addEventListener("load", function () {
        offCanvas = new Canvas(false);
        canvas = new Canvas(true);

        offCanvas.offInit();
        function render() {
          window.requestAnimationFrame(function () {
            canvas.render();
            render();
          });
        }

        render();

        // event
        window.addEventListener("resize", function () {
          canvas.resize();
          offCanvas.resize();
          offCanvas.offInit();
        }, false);
      });
    })();
  </script>

</body>

</html>

代码分析

这段代码通过 HTML、CSS 和 JavaScript 实现了一个飞舞爱心的动画效果。以下将从代码的结构、功能、逻辑和技术实现等多个方面进行详细分析。


一、代码结构和总体概述

  1. HTML 部分

    • 定义了基础的 HTML 结构,设置了 <!DOCTYPE html> 声明,语言为 en,并通过 <head> 部分导入 CSS 样式。

    • <body> 标签内主要包含 JavaScript 脚本,未添加其他内容。这表明所有的视觉元素均通过 Canvas 动态绘制,无静态 HTML 内容。

  2. CSS 部分

    • 全局样式重置:通过 * 选择器清除了所有元素的默认边距和内边距。

    • HTML 和 body 的 overflow 设置为 hidden,使页面无法滚动,确保动画完整显示。

    • 背景颜色设置为黑色,强调彩色爱心的视觉效果。

  3. JavaScript 部分

    • 主要逻辑由多个类和立即执行函数 (function(){...})() 构成。

    • Tool 类提供了一些工具方法,包括随机数生成和颜色生成等。

    • Angle 类用于处理角度和弧度转换。

    • Canvas 类负责管理画布的初始化、元素渲染和窗口缩放适配。

    • Heart 类定义了爱心的属性、绘制方法和动态行为。


二、功能分析

  1. 随机颜色生成

    • Tool 类定义了 randomColorRGB 和 randomColorHSL 方法,用于生成随机 RGB 和 HSL 颜色。gradientColor 方法进一步提供了径向渐变色的生成。

  2. 角度管理

    • Angle 类封装了角度与弧度的关系,并提供了角度递增和递减的功能。这在爱心的动态变化中起到了关键作用。

  3. 画布初始化

    • Canvas 类用于创建画布并根据屏幕大小动态调整尺寸。通过 this.width 和 this.height 确定画布的宽高,同时记录鼠标位置。

  4. 爱心绘制

    • Heart 类实现了爱心的绘制逻辑,基于贝塞尔曲线绘制对称的心形图案。

    • 每个爱心的颜色、透明度和大小都由随机数控制,呈现多样化的视觉效果。

  5. 动态行为

    • 爱心会在屏幕中飞舞,逐渐远离原始位置。

    • 通过调整 this.v(速度)和 this.a(角度)实现运动轨迹的动态变化。

  6. 窗口适配

    • 当窗口大小改变时,重新初始化画布和爱心,确保动画效果适配新尺寸。


三、核心技术实现

  1. Canvas 元素的动态创建

    • JavaScript 通过 document.createElement("canvas") 动态创建画布,并将其添加到页面中。

    • 通过 getContext("2d") 获取画布上下文,执行绘图操作。

  2. 贝塞尔曲线绘制心形

    • Heart 类中使用 bezierCurveTo 方法绘制了左右对称的心形曲线。

    • 具体实现中,两个贝塞尔曲线控制点的位置决定了心形的对称性和弧度。

  3. 颜色渐变和透明度变化

    • 爱心的颜色使用 HSL 色值,动态生成亮度和饱和度。通过全局透明度 globalAlpha 实现爱心的透明效果。

  4. 动画实现

    • 动画基于 window.requestAnimationFrame 方法实现,该方法比 setInterval 更高效,适配屏幕刷新率。

    • 动画帧中调用 canvas.render() 方法,逐帧绘制爱心的位置、大小和透明度变化。

  5. 多画布联动

    • 使用了两个画布:一个离屏画布(offCanvas)用于生成基础数据;另一个在屏幕上展示爱心动画。


四、详细逻辑分析

  1. 工具类(Tool)

    • 提供了生成随机数和颜色的工具方法:
      • randomNumber(min, max):生成 min 到 max 之间的随机整数。

      • randomColorRGB 和 randomColorHSL:分别生成 RGB 和 HSL 随机颜色,用于动态变化。

  2. 画布类(Canvas)

    • 初始化
      • 创建画布,设置宽高。

      • 根据屏幕宽度调整爱心尺寸,确保在不同设备上都有合适的显示比例。

    • 离屏画布(offCanvas
      • 离屏画布用于生成爱心的初始位置数据,避免直接在主画布上操作,提高性能。

    • 渲染
      • 主循环调用 render 方法绘制每一帧。

      • 通过遍历 this.hearts 数组,逐个绘制爱心。

  3. 爱心类(Heart)

    • 绘制逻辑
      • 使用贝塞尔曲线描绘心形,基于动态参数更新形状和大小。

      • 设置全局透明度和颜色,增加视觉层次感。

    • 运动逻辑
      • 爱心的初始位置为随机生成,运动方向和速度通过 v.x 和 v.y 控制。

      • 超出屏幕后重新初始化位置和属性,实现循环效果。

  4. 动态交互

    • 页面监听 resize 事件,当窗口大小改变时,重新初始化画布和离屏画布数据,确保动画效果保持一致。


五、总结

这段代码通过 JavaScript 精心设计了一个动态飞舞的爱心效果,充分展示了 Canvas 的强大能力。整体结构清晰,功能丰富,逻辑合理,是一个兼具美观与性能的动画实现方案。这种实现方式不仅可以用于网页装饰,还可以扩展为互动游戏或者其他创意场景的基础模块。

写在后面

我是一只有趣的兔子,感谢你的喜欢!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2248765.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

英伟达推出了全新的小型语言模型家族——Hymba 1.5B

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

低速接口项目之串口Uart开发(二)——FIFO实现串口数据的收发回环测试

本节目录 一、设计思路 二、loop环回模块 三、仿真模块 四、仿真验证 五、上板验证 六、往期文章链接本节内容 一、设计思路 串口数据的收发回环测试&#xff0c;最简单的硬件测试是把Tx和Rx连接在一起&#xff0c;然后上位机进行发送和接收测试&#xff0c;但是需要考虑到串…

《Java核心技术I》树集

树集 TreeSet类与散列类似&#xff0c;树集是一个有序集合(sorted collection)。 可以以任意顺序将元素插入到集合中&#xff0c;遍历集合时&#xff0c;自动按照排序后的顺序呈现。 插入5个字符串&#xff0c;访问添加的元素 package treeSet;import java.util.TreeSet;pu…

Tailwind CSS v4.0 开启

Tailwind CSS v4.0 发布了 Tailwind CSS 是一个为快速开发而精心设计的原子类 CSS 框架&#xff0c;它提供了充满设计感和应用程序至上的能力来创建组件&#xff0c;它在最新的 2.0 版本中加入了暗黑模式&#xff0c;开箱即用。 Tailwind Connect 大会上&#xff0c;与会者抢先…

QML TableView 实例演示 + 可能遇到的一些问题(Qt_6_5_3)

一、可能遇到的一些问题 Q1&#xff1a;如何禁用拖动&#xff1f; 在TableView下加一句代码即可&#xff1a; interactive: false 补充&#xff1a;这个属性并不专属于TableView&#xff0c;而是一个通用属性。很多Controls下的控件都可以使用&#xff0c;其主要作用就是控…

详细描述一下Elasticsearch更新和删除文档的过程?

大家好&#xff0c;我是锋哥。今天分享关于【详细描述一下Elasticsearch更新和删除文档的过程&#xff1f;】面试题。希望对大家有帮助&#xff1b; 详细描述一下Elasticsearch更新和删除文档的过程&#xff1f; 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 在 E…

(五)Ubuntu22.04+Stable-Diffusion-webui AI绘画 模型转换插件安装及其使用

一、说明 这是秋叶大佬开发的一个模型转换插件&#xff0c;秋叶整合包中自带。如果你的 Stable Diffusion WebUI 中没有这个插件&#xff0c;请使用下面这个地址安装&#xff0c;安装完成之后别忘了重启 WebUI。 模型转换插件 https://github.com/Akegarasu/sd-webui-model-c…

Python数据分析实例五、US 大选捐款数据分析

美国联邦选举委员会 (FEC) 公布了对政治竞选活动的贡献数据。这包括投稿人姓名、职业和雇主、地址和投款金额。2012 年美国总统大选的贡献数据以单个 150 MB 的 CSV 文件P00000001-ALL.csv形式提供,该文件可以通过以下pandas.read_csv加载: import pandas as pdfec = pd.r…

原生微信小程序画表格

wxml部分&#xff1a; <view class"table__scroll__view"><view class"table__header"><view class"table__header__item" wx:for"{{TableHeadtitle}}" wx:key"index">{{item.title}}</view></…

TCP/IP协议攻击与防范

一、TCP/IP协议攻击介绍 1.1 Internet的结构​ LAN&#xff1a;局域网 WAN&#xff1a;广域网 WLAN&#xff1a;无线局域网 私有IP地址与公有IP地址&#xff1f; 私有地址&#xff1a;A类&#xff1a;10.0.0.0~10.255.255.255 B类&#xff1a;172.16.0.0~172.31.255.255…

微信小程序2-地图显示和地图标记

一、index修改页面&#xff0c;让页面能够显示地图和一个添加标记的按钮。 index.wxml <scroll-view class"scrollarea" scroll-y type"list"><view class"index_container"><map id"map" style"width: 100%; h…

【C++】从C语言到C++学习指南

如果你也是从C语言一路过来的&#xff0c;那么请一起看下去吧&#xff01; 文章目录 面型对象程序设计C基础C和C一些语法区别C在非对象方面对C语言的扩充C的一些标准&#xff08;兼容旧标准&#xff09; 首先&#xff0c;在C的学习中&#xff0c;我们要时刻清醒一点&#xff1…

Fakelocation Server服务器/专业版 ubuntu

前言:需要Ubuntu系统 Fakelocation开源文件系统需求 Ubuntu | Fakelocation | 任务一 任务一 更新Ubuntu&#xff08;安装下载不再赘述&#xff09; sudo -i # 提权 sudo apt update # 更新软件包列表 sudo apt upgrade # 升级已安装的软…

探索Python的HTTP之旅:揭秘Requests库的神秘面纱

文章目录 **探索Python的HTTP之旅&#xff1a;揭秘Requests库的神秘面纱**第一部分&#xff1a;背景介绍第二部分&#xff1a;Requests库是什么&#xff1f;第三部分&#xff1a;如何安装Requests库&#xff1f;第四部分&#xff1a;Requests库的五个简单函数使用方法第五部分&…

WPF——ICON按钮制作

前言 首先ICON按钮&#xff0c;即带图标按钮&#xff0c;即图标按钮。 图标按钮在开发时&#xff0c;主要是有两种方式来进行。一是在Button的Content内添加Image&#xff0c;然后设置Image的属性Source来实现&#xff0c;这种方式主要是简单易操作&#xff0c;对于初学者来说…

【MySQL篇】持久化和非持久化统计信息的深度剖析(第一篇,总共六篇)

&#x1f4ab;《博主介绍》&#xff1a;✨又是一天没白过&#xff0c;我是奈斯&#xff0c;DBA一名✨ &#x1f4ab;《擅长领域》&#xff1a;✌️擅长Oracle、MySQL、SQLserver、阿里云AnalyticDB for MySQL(分布式数据仓库)、Linux&#xff0c;也在扩展大数据方向的知识面✌️…

三开关VUE组件

一、使用效果 <template><QqThreeSwitch v-model"value" /><!-- <SqThreeSwitch v-model"value" :options"[test1, test2, test3]"><template #left-action><div style"display: flex"><IconMoon…

线段树与树状数组 (C++)

线段树&#xff1a;基于分治思想的二叉树&#xff0c;用于维护区间信息&#xff08;区间和&#xff0c;区间最值等&#xff09;&#xff0c;区间修改和区间查询的时间复杂度为logn 叶子节点存储元素本身&#xff0c;非叶子节点存取区间信息 1.节点&#xff1a;是一个结构体&a…

vue3 uniapp 扫普通链接或二维码打开小程序并获取携带参数

vue3 uniapp 扫普通链接或二维码打开小程序并获取携带参数 微信公众平台添加配置 微信公众平台 > 开发管理 > 开发设置 > 扫普通链接二维码打开小程序 配置链接规则需要下载校验文档给后端存入服务器中&#xff0c;保存配置的时候会校验一次&#xff0c;确定当前的配…

数据结构(初阶6)---二叉树(遍历——递归的艺术)(详解)

二叉树的遍历与练习 一.二叉树的基本遍历形式1.前序遍历(深度优先遍历)2.中序遍历(深度优先遍历)3.后序遍历(深度优先遍历)4.层序遍历&#xff01;&#xff01;(广度优先遍历) 二.二叉树的leetcode小练习1.判断平衡二叉树1&#xff09;正常解法2&#xff09;优化解法 2.对称二叉…