HTML旋转爱心

系列文章

序号目录
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飞舞爱心
37HTML雪花圣诞树

目录

系列文章

写在前面

完整代码

代码分析

写在后面


写在前面

HTML语言实现旋转爱心的完整代码。

完整代码

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

<head>
    <title>Love</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
    <style>
        body {
            font-family: Monospace;
            background-color: #f0f0f0;
            margin: 0px;
            overflow: hidden;
        }
    </style>
</head>

<body>


    <script src="js/nb.js"></script>

    <script src="js/Projector.js"></script>
    <script src="js/CanvasRenderer.js"></script>

    <script src="js/tween.min.js"></script>
    <script src="js/Sparks.js"></script>

    <!-- load the font file from canvas-text -->

    <script src="js/helvetiker_regular.typeface.js"></script>


    <script>

        var container;

        var camera, scene, renderer;

        var group, text, plane;

        var targetRotation = 0;
        var targetRotationOnMouseDown = 0;

        var mouseX = 0;
        var mouseXOnMouseDown = 0;

        var windowHalfX = window.innerWidth / 2;
        var windowHalfY = window.innerHeight / 2;

        var heartShape, particleCloud, sparksEmitter, emitterPos;
        var _rotation = 0;
        var timeOnShapePath = 0;

        init();
        animate();

        function init() {

            container = document.createElement('div');
            document.body.appendChild(container);


            //相机
            camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 1000);
            camera.position.set(0, 150, 800);

            //场景
            scene = new THREE.Scene();

            group = new THREE.Group();
            scene.add(group);

            // Get text from hash

            var string = "Love";
            var hash = document.location.hash.substr(1);

            if (hash.length !== 0) {

                string = hash;

            }

            var text3d = new THREE.TextGeometry(string, {
                size: 80,
                height: 20,
                curveSegments: 2,
                font: "helvetiker"

            });

            text3d.computeBoundingBox();
            var centerOffset = -0.35 * (text3d.boundingBox.max.x - text3d.boundingBox.min.x);

            var textMaterial = new THREE.MeshBasicMaterial({ color: Math.random() * 0xffffff, overdraw: 0.5 });

            text = new THREE.Mesh(text3d, textMaterial);

            // Potentially, we can extract the vertices or faces of the text to generate particles too.
            // Geo > Vertices > Position

            text.position.x = centerOffset;
            text.position.y = 100;
            text.position.z = 0;

            text.rotation.x = 0;
            text.rotation.y = Math.PI * 2;

            group.add(text);


            particleCloud = new THREE.Object3D(); // Just a group
            particleCloud.y = 800;
            group.add(particleCloud);

            // Create Particle Systems

            // Heart

            var x = 0, y = 0;

            heartShape = new THREE.Shape();

            heartShape.moveTo(x + 25, y + 25);
            heartShape.bezierCurveTo(x + 25, y + 25, x + 20, y, x, y);
            heartShape.bezierCurveTo(x - 30, y, x - 30, y + 35, x - 30, y + 35);
            heartShape.bezierCurveTo(x - 30, y + 55, x - 10, y + 77, x + 25, y + 95);
            heartShape.bezierCurveTo(x + 60, y + 77, x + 80, y + 55, x + 80, y + 35);
            heartShape.bezierCurveTo(x + 80, y + 35, x + 80, y, x + 50, y);
            heartShape.bezierCurveTo(x + 35, y, x + 25, y + 25, x + 25, y + 25);

            var hue = 0;

            var hearts = function (context) {

                context.globalAlpha = 0.5;
                var x = 0, y = 0;
                context.scale(0.05, -0.05); // Scale so canvas render can redraw within bounds
                context.beginPath();
                // From http://blog.burlock.org/html5/130-paths
                context.bezierCurveTo(x + 2.5, y + 2.5, x + 2.0, y, x, y);
                context.bezierCurveTo(x - 3.0, y, x - 3.0, y + 3.5, x - 3.0, y + 3.5);
                context.bezierCurveTo(x - 3.0, y + 5.5, x - 1.0, y + 7.7, x + 2.5, y + 9.5);
                context.bezierCurveTo(x + 6.0, y + 7.7, x + 8.0, y + 5.5, x + 8.0, y + 3.5);
                context.bezierCurveTo(x + 8.0, y + 3.5, x + 8.0, y, x + 5.0, y);
                context.bezierCurveTo(x + 3.5, y, x + 2.5, y + 2.5, x + 2.5, y + 2.5);
                context.fill();
                context.lineWidth = 0.5; //0.05
                context.stroke();

            }

            var setTargetParticle = function () {

                var material = new THREE.SpriteCanvasMaterial({
                    program: hearts
                });

                material.color.setHSL(hue, 1, 0.75);
                hue += 0.001;
                if (hue > 1)
                    hue -= 1;

                particle = new THREE.Sprite(material);

                particle.scale.x = particle.scale.y = Math.random() * 40 + 40;
                particleCloud.add(particle);

                return particle;

            };

            var onParticleCreated = function (p) {

                p.target.position.copy(p.position);

            };

            var onParticleDead = function (particle) {

                particle.target.visible = false;
                particleCloud.remove(particle.target);

            };

            sparksEmitter = new SPARKS.Emitter(new SPARKS.SteadyCounter(160));

            emitterpos = new THREE.Vector3();

            sparksEmitter.addInitializer(new SPARKS.Position(new SPARKS.PointZone(emitterpos)));
            sparksEmitter.addInitializer(new SPARKS.Lifetime(0, 2));
            sparksEmitter.addInitializer(new SPARKS.Target(null, setTargetParticle));

            sparksEmitter.addInitializer(new SPARKS.Velocity(new SPARKS.PointZone(new THREE.Vector3(0, -50, 10))));

            // TOTRY Set velocity to move away from centroid

            sparksEmitter.addAction(new SPARKS.Age());
            //sparksEmitter.addAction(new SPARKS.Accelerate(0.2));
            sparksEmitter.addAction(new SPARKS.Move());
            sparksEmitter.addAction(new SPARKS.RandomDrift(50, 50, 2000));

            sparksEmitter.addCallback("created", onParticleCreated);
            sparksEmitter.addCallback("dead", onParticleDead);
            sparksEmitter.addCallback("updated", function (particle) {

                particle.target.position.copy(particle.position);

            });

            sparksEmitter.start();

            // End Particles


            renderer = new THREE.CanvasRenderer();
            renderer.setClearColor(0xf0f0f0);
            renderer.setPixelRatio(window.devicePixelRatio);
            renderer.setSize(window.innerWidth, window.innerHeight);
            container.appendChild(renderer.domElement);

            document.addEventListener('mousedown', onDocumentMouseDown, false);
            document.addEventListener('touchstart', onDocumentTouchStart, false);
            document.addEventListener('touchmove', onDocumentTouchMove, false);

            //

            window.addEventListener('resize', onWindowResize, false);

        }

        function onWindowResize() {

            windowHalfX = window.innerWidth / 2;
            windowHalfY = window.innerHeight / 2;

            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();

            renderer.setSize(window.innerWidth, window.innerHeight);

        }

        //

        document.addEventListener('mousemove', onDocumentMouseMove, false);

        function onDocumentMouseDown(event) {

            event.preventDefault();

            mouseXOnMouseDown = event.clientX - windowHalfX;
            targetRotationOnMouseDown = targetRotation;

            if (sparksEmitter.isRunning()) {

                sparksEmitter.stop();

            } else {

                sparksEmitter.start();

            }

        }

        function onDocumentMouseMove(event) {

            mouseX = event.clientX - windowHalfX;

            targetRotation = targetRotationOnMouseDown + (mouseX - mouseXOnMouseDown) * 0.02;

        }

        function onDocumentTouchStart(event) {

            if (event.touches.length == 1) {

                event.preventDefault();

                mouseXOnMouseDown = event.touches[0].pageX - windowHalfX;
                targetRotationOnMouseDown = targetRotation;

            }

        }

        function onDocumentTouchMove(event) {

            if (event.touches.length == 1) {

                event.preventDefault();

                mouseX = event.touches[0].pageX - windowHalfX;
                targetRotation = targetRotationOnMouseDown + (mouseX - mouseXOnMouseDown) * 0.05;

            }

        }

        //

        function animate() {//更新场景

            requestAnimationFrame(animate);

            render();

        }

        function render() {

            timeOnShapePath += 0.0337;

            if (timeOnShapePath > 1)
                timeOnShapePath -= 1;

            // TODO Create a PointOnShape Action/Zone in the particle engine
            var pointOnShape = heartShape.getPointAt(timeOnShapePath);

            emitterpos.x = pointOnShape.x * 5 - 100;
            emitterpos.y = -pointOnShape.y * 5 + 400;

            // Pretty cool effect if you enable this
            // particleCloud.rotation.y += 0.05;

            group.rotation.y += (targetRotation - group.rotation.y) * 0.05;
            renderer.render(scene, camera);
        }

    </script>

</body>

</html>

代码分析

这段代码使用了 Three.js 和 Sparks.js 进行 3D 渲染和粒子特效创建,其核心功能是生成一个动态心形粒子效果,并结合“Love”文本渲染,实现互动的 3D 场景。以下是详细的分解与分析:


HTML 结构

  1. 页面头部:

    • 使用了 <meta> 设置编码为 UTF-8,支持视口适配移动设备。

    • 引入内联样式表,设置 body 背景色和无滚动属性。

  2. 核心 JavaScript 文件:

    • nb.jsProjector.jsCanvasRenderer.js: 提供 Three.js 的扩展功能和基础渲染器(CanvasRenderer 用于非 WebGL 的情况下)。

    • tween.min.js: 动画库,提供平滑过渡的时间控制。

    • Sparks.js: 粒子系统的实现,定义了粒子的生命周期、位置和行为。

    • helvetiker_regular.typeface.js: 引入 Three.js 字体文件,用于生成 3D 文本。


JavaScript 核心逻辑

1. 初始化场景

通过 init() 函数初始化 Three.js 场景,包括以下部分:

  • 相机(camera): 使用 PerspectiveCamera 设置透视投影,相机位置 (0, 150, 800)

  • 场景(scene): 通过 Scene() 创建场景对象,添加子对象 group(用于管理场景内的物体)。

  • 文本渲染: 利用 TextGeometry 和字体 helvetiker 创建 “Love” 文本,并设置:
    • 尺寸:80。

    • 厚度:20。

    • 颜色随机。 文本被添加到 group,并设定其位置 (x: centerOffset, y: 100, z: 0)


2. 粒子系统

代码中定义了一个粒子云 particleCloud,粒子的行为和特效通过 Sparks.js 实现:

  • 心形路径(heartShape): 使用 Three.Shape() 定义一个心形路径,结合 bezierCurveTo() 创建心形曲线。

  • 粒子行为:
    • 每个粒子通过 SpriteCanvasMaterial 渲染,附加一个绘制心形的 program

    • 粒子大小随机,范围在 40 至 80。

    • 粒子被添加到 particleCloud

  • 粒子发射器(sparksEmitter):
    • 使用 SPARKS.SteadyCounter 控制每秒 160 粒子。

    • 初始化器包括:
      • 位置:Position 定义初始点为心形路径点。

      • 生命期:Lifetime 设置粒子存活时间为 0 至 2 秒。

      • 速度:Velocity 定义粒子向下漂移 (0, -50, 10)

    • 行为动作:
      • Age:控制粒子的生命周期。

      • Move:移动粒子。

      • RandomDrift:随机漂移,增强动态效果。

    • 注册回调:
      • created: 每创建一个粒子时更新位置。

      • dead: 粒子死亡后移除。


3. 用户交互

支持鼠标和触摸交互:

  • 鼠标事件:
    • mousedown: 点击后切换粒子发射器的运行状态(开启/停止)。

    • mousemove: 控制粒子云的旋转角度。

  • 触摸事件:
    • touchstart 和 touchmove 控制移动设备上的旋转交互。

通过 targetRotation 和 group.rotation.y 实现平滑旋转。


4. 动画更新

animate() 函数使用 requestAnimationFrame() 实现持续更新:

  • 时间路径控制timeOnShapePath 变量控制粒子沿心形路径运动,结合 heartShape.getPointAt() 获取路径上的点坐标。

  • 粒子更新: 每一帧调整粒子的位置,使其形成动态效果。

  • 渲染: 调用 renderer.render() 更新整个场景。


核心亮点

  1. 动态心形路径粒子: 粒子在心形路径上运动,路径通过贝塞尔曲线精确定义。

  2. 三维交互: 利用鼠标、触摸事件调整视角,增强体验感。

  3. 文本渲染: 将 3D 文本和粒子效果结合,提升视觉吸引力。


小结

该代码通过 Three.js 和 Sparks.js 实现了一个视觉效果丰富、互动性强的 3D 粒子场景,展示了动态文本和心形粒子的结合。适合用于情感表达、节日贺卡或动态网页展示场景。

写在后面

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于STM32的智能存储物流柜(单片机毕设)

前言 源代码下载链接&#xff1a; https://download.csdn.net/download/m0_74712453/90071696 需要实物的可以私信博主或者在文章最下方添加好友。 目录 一、项目简介和视频演示 二、硬件实现 1. 具体功能 2. 硬件框图和原理图 3. 主控部分 4. 驱动模块 4.1 SG90 180…

声音克隆技术:探索与实践 —— 从GPT-SoVITS V2到未来趋势20241201

声音克隆技术&#xff1a;探索与实践 —— 从GPT-SoVITS V2到未来趋势 引言&#xff1a;AI与声音创作的完美碰撞 &#x1f3a4;✨ 声音克隆技术正以惊人的速度改变语音生成的方式。从文本到语音&#xff0c;从音色到情感&#xff0c;人工智能赋予了声音创作全新的可能性。 在这…

Lighthouse(灯塔)—— Chrome 浏览器性能测试工具

1.认识 Lighthouse Lighthouse 是 Google 开发的一款开源性能测试工具&#xff0c;用于分析网页或 Web 应用的性能、可访问性、最佳实践、安全性以及 SEO 等关键指标。开发人员可以通过 Lighthouse 快速了解网页的性能瓶颈&#xff0c;并基于优化建议进行改进。 核心功能&…

DApp开发前端框架选择:React还是Vue?

在区块链DApp开发中&#xff0c;前端框架的选择对用户体验和开发效率至关重要。React和Vue作为两大主流前端框架&#xff0c;各自拥有广泛的开发者基础和丰富的生态支持。那么在DApp开发中&#xff0c;该如何选择适合自己的框架呢&#xff1f;下面我们来比较一下&#xff0c;看…

如何通过 Windows 自带的启动管理功能优化电脑启动程序

在日常使用电脑的过程中&#xff0c;您可能注意到开机后某些程序会自动运行。这些程序被称为“自启动”或“启动项”&#xff0c;它们可以在系统启动时自动加载并开始运行&#xff0c;有时甚至在后台默默工作。虽然一些启动项可能是必要的&#xff08;如杀毒软件&#xff09;&a…

WPF_3

x名称空间的由来和作用 WPF程序中有这样的代码&#xff1a; x:Class"WpfControlLibrary1.UserControl1"<!--这是对x的使用-->xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft.com/winfx/…

智能社区服务小程序+ssm(lw+演示+源码+运行)

摘要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已逐步成熟。本文介绍了智能社区服务小程序的开发全过程。通过分析智能社区服务小程序管理的不足&#xff0c;创建了一个计算机管理智能社区服务小程序的方案。文章介绍了智能社区服务…

ABAP - 系统集成之SAP的数据同步到OA(泛微E9)服务器数据库

需求背景 项目经理说每次OA下单都需要调用一次SAP的接口获取数据&#xff0c;导致效率太慢了&#xff0c;能否把SAP的数据保存到OA的数据库表里&#xff0c;这样OA可以直接从数据库表里获取数据效率快很多。思来想去&#xff0c;提供了两个方案。 在集群SAP节点下增加一个SQL S…

Nginx配置https(Ubuntu、Debian、Linux、麒麟)

Ubuntu操作系统&#xff0c;Debian系统底层是Ubuntu&#xff0c;差异不大 ubuntu 安装nginx 1.安装依赖 sudo apt-get update sudo apt-get install gcc sudo apt-get install libpcre3 libpcre3-dev sudo apt-get install zlib1g zlib1g-dev sudo apt-get install openssl lib…

OCR的评价指标和常用数据集

1.OCR任务简介 OCR(Optical Character Recognition,光学字符识别)是指对包含文本内容的图像或者视频进行处理识别&#xff0c;并提取其中所包含的文字及排版信息的过程。例如&#xff0c;一个常见的应用是将包含文档图像的不可编辑状态的 PDF 文档通过 OCR 技术识别后&#xf…

【人工智能的深度分析与最新发展趋势】

人工智能的深度分析与最新发展趋势 引言 人工智能&#xff08;AI&#xff09;是现代科技的重要组成部分&#xff0c;它涉及模拟人类智能的算法和技术。随着计算能力的提升和数据量的激增&#xff0c;AI的应用正在迅速渗透到各个行业。本文将深入分析人工智能的概念、技术、应…

【JavaWeb后端学习笔记】Mybatis基础操作以及动态SQL(增、删、改、查)

Mybatis 0、环境准备0.1 准备数据库表emp&#xff1b;0.2 准备SpringBoot工程0.3 配置文件中引入数据库连接信息0.4 创建对应的实体类0.5 准备Mapper接口 1、MyBatis基础操作1.1 删除1.2 新增&#xff08;主键返回&#xff09;1.3 更新1.4 查询&#xff08;解决字段名与类属性名…

QT5 Creator (Mingw编译器) 调用VS2019 (阿里云 oss C++库) 报错的解决方法

方法就是不要用VS2019编译&#xff0c;要用MINgw32编译。注意要安装高版本的qt&#xff0c;其自带的mingw编译器才能支持&#xff0c;找不到qt5cored.dll&#xff0c;就把qt5core.dll改名为qt5cored.dll。 编译命令如下&#xff1a; cmake -G "MinGW Makefiles" ^-…

Scala—Slice(提取子序列)方法详解

Scala—Slice&#xff08;提取子序列&#xff09;方法详解 在 Scala 中&#xff0c;slice 方法用于从集合中提取一个连续的子序列&#xff08;切片&#xff09;。可以应用于多种集合类型&#xff0c;如 List、Array、Seq 等。 一、slice 方法的定义 slice 根据提供的起始索引…

Alibaba EasyExcel 导入导出全家桶

一、阿里巴巴EasyExcel的优势 首先说下EasyExcel相对 Apache poi的优势&#xff1a; EasyExcel也是阿里研发在poi基础上做了封装&#xff0c;改进产物。它替开发者做了注解列表解析&#xff0c;表格填充等一系列代码编写工作&#xff0c;并将此抽象成通用和可扩展的框架。相对p…

基于SpringBoot+Vue的美妆购物网站

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏&#xff1a;…

【Altium Designer 】AD如何使用嘉立创元器件的3D封装

1.下载3D封装 以STM32F407VGT6为例&#xff0c;进入嘉立创商城网站&#xff0c;找到需要的元器件封装 复制编号&#xff0c;打开嘉立创EDA&#xff0c;编译器选择专业版&#xff0c;新建工程&#xff0c;点击PCB1 复制编号在搜索框中&#xff0c;点击搜索&#xff0c;然后放置…

轨道力学:兰伯特问题

轨道力学&#xff1a;兰伯特问题 引言 在轨道力学中&#xff0c;兰伯特问题是指在已知两个位置矢量和它们之间的飞行时间的情况下&#xff0c;求解连接这两个位置的轨道路径问题。该问题以18世纪的数学家约翰海因里希兰伯特&#xff08;Johann Heinrich Lambert&#xff09;命…

计算机网络 第5章 运输层

计算机网络 &#xff08;第8版&#xff09; 第 5 章 传输层5.4 可靠传输的原理5.4.1 停止等待协议5.4.2 连续ARQ协议 5.5 TCP报文段的首部格式5.6 TCP可靠传输的实现5.6.1 以字节为单位的滑动窗口5.6.2 超时重传时间的选择 5.7 TCP的流量控制5.7.1 利用滑动窗口实现流量控制 5.…

【AI系统】EfficientNet 系列

EfficientNet 系列 本文主要介绍 EffiicientNet 系列&#xff0c;在之前的文章中&#xff0c;一般都是单独增加图像分辨率或增加网络深度或单独增加网络的宽度&#xff0c;来提高网络的准确率。而在 EfficientNet 系列论文中&#xff0c;会介绍使用网络搜索技术(NAS)去同时探索…