HTML飞舞的爱心
目录
系列文章
写在前面
完整代码
代码分析
写在后面
系列文章
序号 | 目录 |
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爱心流星雨 |
32 | HTML生日蛋糕 |
33 | HTML3D旋转相册 |
34 | HTML流光爱心 |
35 | HTML满屏飘字 |
36 | HTML飞舞爱心 |
写在前面
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);}// datathis.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();// eventwindow.addEventListener("resize", function () {canvas.resize();offCanvas.resize();offCanvas.offInit();}, false);});})();</script></body></html>
代码分析
这段代码通过 HTML、CSS 和 JavaScript 实现了一个飞舞爱心的动画效果。以下将从代码的结构、功能、逻辑和技术实现等多个方面进行详细分析。
一、代码结构和总体概述
-
HTML 部分
-
定义了基础的 HTML 结构,设置了
<!DOCTYPE html>
声明,语言为en
,并通过<head>
部分导入 CSS 样式。 -
<body>
标签内主要包含 JavaScript 脚本,未添加其他内容。这表明所有的视觉元素均通过 Canvas 动态绘制,无静态 HTML 内容。
-
-
CSS 部分
-
全局样式重置:通过
*
选择器清除了所有元素的默认边距和内边距。 -
HTML 和 body 的
overflow
设置为hidden
,使页面无法滚动,确保动画完整显示。 -
背景颜色设置为黑色,强调彩色爱心的视觉效果。
-
-
JavaScript 部分
-
主要逻辑由多个类和立即执行函数
(function(){...})()
构成。 -
Tool
类提供了一些工具方法,包括随机数生成和颜色生成等。 -
Angle
类用于处理角度和弧度转换。 -
Canvas
类负责管理画布的初始化、元素渲染和窗口缩放适配。 -
Heart
类定义了爱心的属性、绘制方法和动态行为。
-
二、功能分析
-
随机颜色生成
-
Tool
类定义了randomColorRGB
和randomColorHSL
方法,用于生成随机 RGB 和 HSL 颜色。gradientColor
方法进一步提供了径向渐变色的生成。
-
-
角度管理
-
Angle
类封装了角度与弧度的关系,并提供了角度递增和递减的功能。这在爱心的动态变化中起到了关键作用。
-
-
画布初始化
-
Canvas
类用于创建画布并根据屏幕大小动态调整尺寸。通过this.width
和this.height
确定画布的宽高,同时记录鼠标位置。
-
-
爱心绘制
-
Heart
类实现了爱心的绘制逻辑,基于贝塞尔曲线绘制对称的心形图案。 -
每个爱心的颜色、透明度和大小都由随机数控制,呈现多样化的视觉效果。
-
-
动态行为
-
爱心会在屏幕中飞舞,逐渐远离原始位置。
-
通过调整
this.v
(速度)和this.a
(角度)实现运动轨迹的动态变化。
-
-
窗口适配
-
当窗口大小改变时,重新初始化画布和爱心,确保动画效果适配新尺寸。
-
三、核心技术实现
-
Canvas 元素的动态创建
-
JavaScript 通过
document.createElement("canvas")
动态创建画布,并将其添加到页面中。 -
通过
getContext("2d")
获取画布上下文,执行绘图操作。
-
-
贝塞尔曲线绘制心形
-
Heart
类中使用bezierCurveTo
方法绘制了左右对称的心形曲线。 -
具体实现中,两个贝塞尔曲线控制点的位置决定了心形的对称性和弧度。
-
-
颜色渐变和透明度变化
-
爱心的颜色使用 HSL 色值,动态生成亮度和饱和度。通过全局透明度
globalAlpha
实现爱心的透明效果。
-
-
动画实现
-
动画基于
window.requestAnimationFrame
方法实现,该方法比setInterval
更高效,适配屏幕刷新率。 -
动画帧中调用
canvas.render()
方法,逐帧绘制爱心的位置、大小和透明度变化。
-
-
多画布联动
-
使用了两个画布:一个离屏画布(
offCanvas
)用于生成基础数据;另一个在屏幕上展示爱心动画。
-
四、详细逻辑分析
-
工具类(Tool)
- 提供了生成随机数和颜色的工具方法:
-
randomNumber(min, max)
:生成min
到max
之间的随机整数。 -
randomColorRGB
和randomColorHSL
:分别生成 RGB 和 HSL 随机颜色,用于动态变化。
-
- 提供了生成随机数和颜色的工具方法:
-
画布类(Canvas)
- 初始化
-
创建画布,设置宽高。
-
根据屏幕宽度调整爱心尺寸,确保在不同设备上都有合适的显示比例。
-
- 离屏画布(
offCanvas
)-
离屏画布用于生成爱心的初始位置数据,避免直接在主画布上操作,提高性能。
-
- 渲染
-
主循环调用
render
方法绘制每一帧。 -
通过遍历
this.hearts
数组,逐个绘制爱心。
-
- 初始化
-
爱心类(Heart)
- 绘制逻辑
-
使用贝塞尔曲线描绘心形,基于动态参数更新形状和大小。
-
设置全局透明度和颜色,增加视觉层次感。
-
- 运动逻辑
-
爱心的初始位置为随机生成,运动方向和速度通过
v.x
和v.y
控制。 -
超出屏幕后重新初始化位置和属性,实现循环效果。
-
- 绘制逻辑
-
动态交互
-
页面监听
resize
事件,当窗口大小改变时,重新初始化画布和离屏画布数据,确保动画效果保持一致。
-
五、总结
这段代码通过 JavaScript 精心设计了一个动态飞舞的爱心效果,充分展示了 Canvas 的强大能力。整体结构清晰,功能丰富,逻辑合理,是一个兼具美观与性能的动画实现方案。这种实现方式不仅可以用于网页装饰,还可以扩展为互动游戏或者其他创意场景的基础模块。
写在后面
我是一只有趣的兔子,感谢你的喜欢!
相关文章:

HTML飞舞的爱心
目录 系列文章 写在前面 完整代码 代码分析 写在后面 系列文章 序号目录1HTML满屏跳动的爱心(可写字)2HTML五彩缤纷的爱心3HTML满屏漂浮爱心4HTML情人节快乐5HTML蓝色爱心射线6HTML跳动的爱心(简易版)7HTML粒子爱心8HTML蓝色…...

C++中智能指针的使用及其原理 -- RAII,内存泄漏,shared_ptr,unique_ptr,weak_ptr
目录 1.智能指针的使用场景分析 2.RAII和智能指针的设计思路 3.C标准库智能指针的使用 4.智能指针的原理以及模拟实现 5.shared_ptr循环引用问题和weak_ptr 5.1shared_ptr循环引用问题 5.2weak_ptr的原理和部分接口 5.3weak_ptr的简单模拟实现 6. shared_ptr的线程安…...

Linux服务器安装mongodb
因为项目需要做评论功能,领导要求使用mongodb,所以趁机多学习一下。 在服务器我们使用docker安装mongodb 1、拉取mongodb镜像 docker pull mongo (默认拉取最新的镜像) 如果你想指定版本可以这样 docker pull mongo:4.4&#…...

Android11修改摄像头前后置方法,触觉智能RK3568开发板演示
本文介绍在Android11系统下,修改摄像头前后置属性的方法。使用触觉智能EVB3568鸿蒙开发板演示,搭载瑞芯微RK3568,四核A55处理器,主频2.0Ghz,1T算力NPU;支持OpenHarmony5.0及Linux、Android等操作系统&#…...
leetcode 212. 单词搜索 II
给定一个 m x n 二维字符网格 board 和一个单词(字符串)列表 words, 返回所有二维网格上的单词 。 单词必须按照字母顺序,通过 相邻的单元格 内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一…...

Taro 鸿蒙技术内幕系列(三) - 多语言场景下的通用事件系统设计
基于 Taro 打造的京东鸿蒙 APP 已跟随鸿蒙 Next 系统公测,本系列文章将深入解析 Taro 如何实现使用 React 开发高性能鸿蒙应用的技术内幕 背景 在鸿蒙生态系统中,虽然原生应用通常基于 ArkTS 实现,但在实际研发过程中发现,使用 C…...
《Docker Registry(镜像仓库)详解》
一、引言 在容器化技术日益普及的今天,Docker 已成为众多开发者和企业的首选工具。而 Docker Registry(镜像仓库)作为 Docker 生态系统中的重要组成部分,负责存储和分发 Docker 镜像。本文将深入探讨 Docker Registry 的概念、功能…...

AI前景分析展望——GPTo1 SoraAI
引言 人工智能(AI)领域的飞速发展已不仅仅局限于学术研究,它已渗透到各个行业,影响着从生产制造到创意产业的方方面面。在这场技术革新的浪潮中,一些领先的AI模型,像Sora和OpenAI的O1,凭借其强大…...

超级详细讲解转义字符,\? \‘ \f \0 \t等等!!!
C语言有一组特殊的字符称作转义字符,顾名思义,转变原来的意思 1 \? ??)是一个三字母词,在以前的编译器它会被编译为] (??会被编译为[ 因此在以前输入(are you ok ??)就会被编译为are you ok ] 解决这个问题只要在问号前输入\…...
微信小程序数据请求教程:GET与POST请求详解
微信小程序数据请求教程:GET与POST请求详解 引言 在微信小程序的开发过程中,数据请求是至关重要的一部分。通过与后端服务器进行通信,小程序能够获取动态数据,实现丰富的功能。在这篇文章中,我们将深入探讨微信小程序中的数据请求,重点介绍GET和POST请求的使用方法、示…...
Linux系统管理基础指南--习题
目录 一、基础知识与命令 二、 Linux的用户接口 三、文件权限与目录管理 四、shell相关知识 五、软件安装与网络 六、网络进程管理 一、基础知识与命令 1. (操作题)分别执行下述命令 ls -al cd ~ cd man -f man man –k cd man --help cal --help date --help bc --he…...

JVM(JAVA虚拟机)内存溢出导致内存不足,Java运行时环境无法继续
1、先贴出服务最后打印出来的日志,意思就是给虚拟机分配的内存被用完了,没有可用的内存了,服务运行不了了,被动停服了。详细的日志记录在了/home/user/zx/tomcat/apache-tomcat-8.5.82/bin/hs_err_pid147951.log文件里。 Java Ho…...

IOC控制反转详解
IOC(控制反转) component的衍生注解 前面曾经提到,若想要把某个对象交给IOC容器管理,就需要在其声明上加上Component注解。但是Spring中有三层架构,为了更加清晰的标注对象是属于哪一层的,提供了三个Comp…...

Qml-TabBar类使用
Qml-TabBar类使用 TabBar的概述 TabBar继承于Container 由TabButton进行填充,可以与提供currentIndex属性的任何容器或布局控件一起使用,如StackLayout 或 SwipeView;contentHeight : real:TabBar的内容高度,用于计算标签栏的隐…...

C# 常量
文章目录 前言一、整数常量(一)合法与非法实例对比(二)不同进制及类型示例 二、浮点常量三、字符常量四、字符串常量五、定义常量 前言 在 C# 编程的世界里,常量是一类特殊的数据元素,它们如同程序中的 “定…...
diffusion model: prompt-to-prompt 深度剖析
参考:diffusion model(十四): prompt-to-prompt 深度剖析-CSDN博客 P2P提出的Motivation 目前大火的文生图技术(text to image),给定一段文本(prompt)和随机种子,文生图模型会基于这两者生成一张图片。生…...

uniapp实现APP版本升级
App.vue 直接上代码 <script>export default {methods: {//APP 版本升级Urlupload() {// #ifdef APP-PLUSplus.runtime.getProperty(plus.runtime.appid, (info) > {// 版本号变量持久化存储getApp().globalData.version info.version;this.ToLoadUpdate(info.versi…...

uniapp强制修改radio-group内单选组件的状态方法
在uniapp开发中,需要在radio-group内部切换时做判断,提醒客户是否要变换radio的值,但是大家知道radio是单选组件,往往你点击后,是不能再修改状态的,就算你在点击后做判断,修改current的值&#…...
学习python的第十四天之函数——高阶函数和偏函数
学习python的第十四天之函数——高阶函数和偏函数 高阶函数 高阶函数是指那些可以接受一个或多个函数作为参数,或者返回一个函数作为结果的函数。高阶函数是函数式编程范式中的一个重要概念,它们使得代码更加灵活和模块化。 sorted() sorted()函数用于对…...

数据结构之二叉树详解:从原理到实现
1. 什么是二叉树? 二叉树(Binary Tree)是一种树形数据结构,其中每个节点最多有两个子节点,分别被称为左子节点和右子节点。二叉树可以用来表示层次关系,如文件目录、组织结构,或用于快速查找、…...

51c自动驾驶~合集58
我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留,CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制(CCA-Attention),…...
k8s从入门到放弃之Ingress七层负载
k8s从入门到放弃之Ingress七层负载 在Kubernetes(简称K8s)中,Ingress是一个API对象,它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress,你可…...
深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法
深入浅出:JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中,随机数的生成看似简单,却隐藏着许多玄机。无论是生成密码、加密密钥,还是创建安全令牌,随机数的质量直接关系到系统的安全性。Jav…...
1688商品列表API与其他数据源的对接思路
将1688商品列表API与其他数据源对接时,需结合业务场景设计数据流转链路,重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点: 一、核心对接场景与目标 商品数据同步 场景:将1688商品信息…...

微服务商城-商品微服务
数据表 CREATE TABLE product (id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 商品id,cateid smallint(6) UNSIGNED NOT NULL DEFAULT 0 COMMENT 类别Id,name varchar(100) NOT NULL DEFAULT COMMENT 商品名称,subtitle varchar(200) NOT NULL DEFAULT COMMENT 商…...
拉力测试cuda pytorch 把 4070显卡拉满
import torch import timedef stress_test_gpu(matrix_size16384, duration300):"""对GPU进行压力测试,通过持续的矩阵乘法来最大化GPU利用率参数:matrix_size: 矩阵维度大小,增大可提高计算复杂度duration: 测试持续时间(秒&…...
【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分
一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计,提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合:各模块职责清晰,便于独立开发…...

使用 SymPy 进行向量和矩阵的高级操作
在科学计算和工程领域,向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能,能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作,并通过具体…...

使用Spring AI和MCP协议构建图片搜索服务
目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...
站群服务器的应用场景都有哪些?
站群服务器主要是为了多个网站的托管和管理所设计的,可以通过集中管理和高效资源的分配,来支持多个独立的网站同时运行,让每一个网站都可以分配到独立的IP地址,避免出现IP关联的风险,用户还可以通过控制面板进行管理功…...