用 vue3 实现新年快乐

news2025/1/4 7:32:09

提前祝福大家新年快乐,今天用一个新年快乐的教程来结束这一年。

看下效果
请添加图片描述

在这个案例中,我们使用了 vue3 ,有一个浮动的新年快乐的字体,然后有一堆从下到上的小粒子,在文字背后有一个模拟烟花绽放的效果。

环境搭建

我们使用 vite 来搭建一个 vue3 的项目,并且安装 three 的依赖。
通过下面的命令来创建一个 vite 项目

npm create vite

根据提示选择 vue 3 项目,然后安装 three.js

npm install three

运行项目

npm run dev

目录创建

├── src/
│   ├── components/
│   │   └── ThreeScene.vue   # 主要的 3D 场景组件
│   ├── utils/
│   │   └── fireworks.js     # 烟花效果管理
│   ├── App.vue              # 根组件
│   └── main.js              # 应用入口
└── index.html

其中 ThreeScene.vue 是主要的 3D 场景组件,fireworks.js 是烟花效果的管理。App.vue 是根组件,main.js 是应用入口。

ThreeScene.vue 核心代码解析

这段代码通过 Vue 3 和 Three.js 创建了一个具有动态效果的 3D 场景,包括:

  • 一个 3D 的 “新年快乐” 文字效果。
  • 色彩斑斓的粒子效果,模拟烟花。
  • 每个元素(文字、粒子和烟花)在动画循环中动态更新。

1. 模板部分 (<template>)

这个部分定义了一个 div 容器,ref="container" 用来在脚本中访问该元素,将 Three.js 渲染的 3D 内容添加到这个容器中。

```javascript
```

2. 脚本部分 (<script setup>)

引入依赖
import { ref, onMounted, onBeforeUnmount } from "vue";
import * as THREE from "three";
import { FireworksManager } from "../utils/fireworks";
import { FontLoader } from "three/examples/jsm/loaders/FontLoader";
import { TextGeometry } from "three/examples/jsm/geometries/TextGeometry";

  • ref:Vue 3 的响应式引用,用于访问 DOM 元素。
  • onMountedonBeforeUnmount:Vue 生命周期钩子,用于在组件挂载后和卸载前执行代码。
  • THREE:引入 Three.js 进行 3D 渲染。
  • FireworksManager:自定义的烟花效果管理器。
  • FontLoaderTextGeometry:用于加载和渲染 3D 字体。
变量声明
const container = ref(null);
let scene, camera, renderer, fireworksManager;
let textMesh;
let particleSystem, particleMaterial, particleGeometry;
let animationFrameId;
let particleColors;
  • scene:Three.js 场景对象。
  • camera:摄像机对象。
  • renderer:渲染器对象。
  • fireworksManager:烟花效果管理器。
  • textMesh:存储 3D 字体的 Mesh 对象。
  • particleSystem, particleMaterial, particleGeometry:存储粒子系统相关的对象。
  • animationFrameId:用于存储动画帧 ID,方便控制动画的停止。
  • particleColors:存储粒子颜色数据。
初始化函数(init)

初始化 Three.js 的核心组件,包括场景、相机、渲染器、光源、烟花效果以及粒子系统。

const init = () => {
  // 初始化场景
  scene = new THREE.Scene();
  scene.background = new THREE.Color(0x000000);

  // 初始化相机
  camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
  camera.position.set(0, 20, 100);
  camera.lookAt(0, 0, 0);

  // 初始化渲染器
  renderer = new THREE.WebGLRenderer({ antialias: true });
  renderer.setSize(window.innerWidth, window.innerHeight);
  container.value.appendChild(renderer.domElement);

  // 添加环境光和方向光
  const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
  scene.add(ambientLight);

  const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
  directionalLight.position.set(5, 5, 5);
  scene.add(directionalLight);

  // 初始化烟花效果
  fireworksManager = new FireworksManager(scene);

  // 添加3D文字
  addText();

  // 添加五颜六色的粒子效果
  addParticles();

  // 绑定窗口调整事件
  window.addEventListener("resize", onWindowResize);
};

  • scene:创建一个 Three.js 场景对象,并设置背景为黑色。
  • camera:创建一个透视相机,设置相机的视角和位置。
  • renderer:创建一个 WebGL 渲染器并添加到页面容器中。
  • ambientLightdirectionalLight:添加环境光和方向光,以增加场景的亮度。
  • fireworksManager:初始化烟花效果管理器。
  • addTextaddParticles:调用函数分别添加 3D 文本和粒子系统。
  • onWindowResize:监听页面的窗口大小变化,调整渲染器和相机。
3D文字添加 (addText)
const addText = () => {
  const loader = new FontLoader();
  loader.load("/fonts/FangSong_Regular.json", (font) => {
    const textGeometry = new TextGeometry("新年快乐", {
      font: font,
      size: 14,
      height: 3,
      curveSegments: 36,
      bevelEnabled: true,
      bevelThickness: 0.4,
      bevelSize: 0.3,
      bevelSegments: 10,
    });
    ...
  });
};

粒子系统添加(addParticles)

生成一个粒子系统,每个粒子具有不同的颜色和随机位置,粒子系统被添加到场景中。

const addParticles = () => {
  particleGeometry = new THREE.BufferGeometry();
  const particleCount = 1000;
  const positions = [];
  particleColors = [];
  for (let i = 0; i < particleCount; i++) {
    positions.push((Math.random() - 0.5) * 200, Math.random() * 100, (Math.random() - 0.5) * 200);
    const color = new THREE.Color(Math.random(), Math.random(), Math.random());
    particleColors.push(color.r, color.g, color.b);
  }
  ...
};
动画函数 (animate)

这个函数用于不断更新场景中的元素,并执行渲染操作。

const animate = () => {
  animationFrameId = requestAnimationFrame(animate);
  if (Math.random() < 0.08) {
    fireworksManager.addFirework();
  }
  fireworksManager.update();
  ...
  renderer.render(scene, camera);
};

窗口调整事件 (onWindowResize)

监听页面的窗口尺寸变化,动态调整渲染器的大小和相机的视角。

const onWindowResize = () => {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
};

生命周期钩子

onMounted 钩子中,初始化 Three.js 场景,并开始动画。并在 onBeforeUnmount 钩子中,清理资源,取消动画帧请求和移除事件监听器。

onMounted(() => {
  init();
  animate();
});

onBeforeUnmount(() => {
  cancelAnimationFrame(animationFrameId);
  window.removeEventListener("resize", onWindowResize);
  if (container.value) {
    container.value.removeChild(renderer.domElement);
  }
  scene.dispose();
  particleGeometry.dispose();
  particleMaterial.dispose();
  fireworksManager.dispose();
});

样式部分 (<style scoped>)
.scene-container {
  width: 100vw;
  height: 100vh;
  position: fixed;
  top: 0;
  left: 0;
  overflow: hidden;
}

.scene-container 样式设置了容器的宽度和高度为全屏,并确保其位置固定,不会因滚动条而变化,同时隐藏溢出的内容。

fireworks.js 核心代码解析

这段代码的核心是创建并管理多个烟花实例,烟花分为上升阶段和爆炸阶段。通过 Firework 类管理每个烟花的状态、粒子效果及其更新逻辑。FireworksManager 类用于管理多个烟花实例,并在每一帧更新它们的状态。

1. getRandomColor() - 随机生成颜色

getRandomColor() {
  const colors = [
    new THREE.Color(0xff0000), // 红色
    new THREE.Color(0x00ff00), // 绿色
    new THREE.Color(0x0000ff), // 蓝色
    // 更多颜色...
  ];
  return colors[Math.floor(Math.random() * colors.length)];
}

2. createTrail() - 创建烟花上升阶段的拖尾效果

createTrail() {
  const trailGeometry = new THREE.SphereGeometry(0.2, 8, 8);
  const trailMaterial = new THREE.MeshBasicMaterial({
    color: this.mesh.material.color,
    transparent: true,
    opacity: 0.6,
  });
  const trail = new THREE.Mesh(trailGeometry, trailMaterial);
  trail.position.copy(this.mesh.position);
  trail.userData.life = 0.5;
  this.trails.push(trail);
  this.scene.add(trail);
}

3. createExplosionPattern() - 创建爆炸模式

根据爆炸的不同模式(球形、环形、柳树形等),计算每个粒子的速度方向,并生成爆炸效果。

createExplosionPattern(baseVelocity, count, pattern) {
  const velocities = [];
  switch (pattern) {
    case "sphere":
      // 球形爆炸模式
      for (let i = 0; i < count; i++) {
        const phi = Math.acos(-1 + (2 * i) / count);
        const theta = Math.sqrt(count * Math.PI) * phi;
        const velocity = new THREE.Vector3();
        velocity.x = Math.cos(theta) * Math.sin(phi);
        velocity.y = Math.sin(theta) * Math.sin(phi);
        velocity.z = Math.cos(phi);
        velocity.multiplyScalar(baseVelocity * (1 + Math.random()));
        velocities.push(velocity);
      }
      break;
    // 其他模式的处理
  }
  return velocities;
}

4. explode() - 烟花爆炸效果

explode() {
  const baseColor = this.getRandomColor();
  // 初始化粒子位置
  const positions = this.particlePositions;
  this.particleVelocities = [];

  for (let i = 0; i < this.particleCount; i++) {
    // 设置粒子位置和速度
    positions[i3] = this.mesh.position.x;
    positions[i3 + 1] = this.mesh.position.y;
    positions[i3 + 2] = this.mesh.position.z;
    // 更多粒子计算...
  }

  // 创建粒子系统
  const particleMaterial = new THREE.PointsMaterial({
    size: 0.3,
    color: baseColor,
    transparent: true,
    opacity: 0.8,
    blending: THREE.AdditiveBlending,
    sizeAttenuation: true,
    vertexColors: true,
  });

  this.particles = new THREE.Points(this.particleGeometry, particleMaterial);
  this.scene.add(this.particles);
  this.scene.remove(this.mesh); // 移除烟花球体
  this.isExploded = true;
}

5. update() - 更新烟花状态

该方法用于更新烟花的上升阶段和爆炸阶段,处理烟花的运动、拖尾、爆炸等效果。

update() {
  if (!this.isExploded) {
    // 上升阶段的更新
    this.mesh.position.add(this.velocity);
    this.velocity.y -= 0.15; // 重力

    if (Math.random() < 0.3) {
      this.createTrail();
    }

    // 更新拖尾
    for (let i = this.trails.length - 1; i >= 0; i--) {
      const trail = this.trails[i];
      trail.userData.life -= 0.05;
      trail.material.opacity = trail.userData.life;

      if (trail.userData.life <= 0) {
        this.scene.remove(trail);
        this.trails.splice(i, 1);
      }
    }

    if (this.mesh.position.y < 20 && this.velocity.y < 0) {
      this.explode(); // 爆炸判断
    }
  } else if (this.particles) {
    // 爆炸阶段的更新
    const positions = this.particleGeometry.attributes.position.array;
    let alive = false;

    for (let i = 0; i < this.particleCount; i++) {
      // 更新粒子位置
      positions[i3] += velocity.x;
      positions[i3 + 1] += velocity.y;
      positions[i3 + 2] += velocity.z;
    }

    // 消失效果
    this.particles.material.opacity *= 0.985;
    this.particles.material.size *= 0.99;

    this.particleGeometry.attributes.position.needsUpdate = true;

    if (this.particles.material.opacity > 0.01) {
      alive = true;
    }

    if (!alive) {
      this.scene.remove(this.particles);
      this.particles = null;
    }

    return alive;
  }

  return this.trails.length > 0 || !this.isExploded;
}

总结

基于 Vue 3 和 Three.js 的互动 3D 场景,结合了烟花效果、粒子系统和 3D 文字。首先,初始化了一个 Three.js 场景、相机和渲染器,并添加了基础的光源。

通过 FireworksManager 管理烟花效果,随机生成并更新烟花。代码使用 FontLoader 加载字体并创建 3D 文字,并通过 TextGeometry 渲染带有发光效果的文字,添加到场景中。

粒子效果通过 BufferGeometry 管理粒子的位置和颜色,实现了随机分布的彩色粒子上升效果。

在动画部分,文字上下浮动,粒子随着时间上升并重置,增强了动态效果。

同时,绑定了窗口调整事件,确保在窗口大小变化时重新计算场景尺寸。

组件销毁时,通过 onBeforeUnmount 清理资源,移除事件监听器并释放内存,避免潜在的内存泄漏。

代码

github

https://github.com/calmound/threejs-demo/tree/main/happy

gitee

https://gitee.com/calmound/threejs-demo/tree/main/happy

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

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

相关文章

QT集成IntelRealSense双目摄像头2,集成OpenGL

上一篇文章写了如何把IntelRealSense摄像头的SDK集成到QT项目&#xff0c;并成功采集数据&#xff0c;在没有用OpenCV的情况下完成色彩数据&#xff0c;以及深度数据的显示。 具体地址&#xff1a;https://blog.csdn.net/qujia121qu/article/details/144734163 本次主要写如何…

【模块一】kubernetes容器编排进阶实战之kubernetes pod Affinity与pod antiaffinity

pod Affinity与pod antiaffinity Pod Affinity与anti-affinity简介&#xff1a; Pod亲和性与反亲和性可以基于已经在node节点上运行的Pod的标签来约束新创建的Pod可以调度到的 目的节点&#xff0c;注意不是基于node上的标签而是使用的已经运行在node上的pod标签匹配。 其规则…

从百度云网盘下载数据到矩池云网盘或者服务器内

本教程教大家如何快速将百度云网盘数据集或者模型代码文件下载到矩池云网盘或者服务器硬盘上。 本教程使用到了一个开源工具 BaiduPCS-Go&#xff0c;官方地址 &#xff1a; https://github.com/qjfoidnh/BaiduPCS-Go 这个工具可以实现“仿 Linux shell 文件处理命令的百度网…

使用maven-mvnd替换maven大大提升编译打包速度

先上结论&#xff01;&#xff01;&#xff01; 多模块清理并打包提升&#xff1a;约3.5倍 多模块不清理打包提升&#xff1a;约5.5倍 单模块提升&#xff1a;约2倍 从计算结果来看&#xff0c;多模块提升的效率更高。在使用mvnd package打包多模块式&#xff0c;可在控制台…

LeetCode - 初级算法 数组(删除排序数组中的重复项)

免责声明:本文来源于个人知识与公开资料,仅用于学术交流。 删除排序数组中的重复项 这篇文章讨论如何从一个非严格递增的数组 nums 中删除重复的元素,使每个元素只出现一次,并返回新数组的长度。因为数组是排序的,只要是相同的肯定是挨着的,所以我们需要遍历所有数组,然…

2024 年度总结

时光荏苒&#xff0c;2024 年即将画上句号&#xff0c;回顾这一年的写博历程&#xff0c;有付出、有收获、有成长&#xff0c;也有诸多值得回味与反思的瞬间。 一、内容创作 主题涉猎&#xff1a;这一年&#xff0c;我致力于探索多样化的主题&#xff0c;以满足不同读者群体的…

基于STM32位单片机的腕式运动体力监测装置设计

本设计基于STM32位单片机的腕式运动体力状态诊断系统装置。本系统内的使用的STM32单片机包含了心率检测电路、呼吸频率检测电路、OLED液晶显示电路、电源电路、蓝牙电路。通过心率传感器以及手指脉搏波动放大过后发送给比较器&#xff0c;结果处理后发送给单片机进行信息的收集…

LunarVim安装

LunarVim以其丰富的功能和灵活的定制性&#xff0c;迅速在Nvim用户中流行开来。它不仅提供了一套完善的默认配置&#xff0c;还允许用户根据自己的需求进行深度定制。无论是自动补全、内置终端、文件浏览器&#xff0c;还是模糊查找、LSP支持、代码检测、格式化和调试&#xff…

前端超大缓存IndexDB、入门及实际使用

文章目录 往期回顾项目实战初始化表获取列表新增表的数据项获取详情根据ID获取详情根据其他字段获取详情 删除数据 总结 往期回顾 在之前的文章中&#xff0c;我们介绍了IndexDB vs Cookies vs Session这几个的对比&#xff0c;但是没有做实际项目的演示&#xff0c;今天我们用…

面试题解,JVM的运行时数据区

一、请简述JVM运行时数据区的组成结构及各部分作用 总览 从线程持有的权限来看 线程私有区 虚拟机栈 虚拟机栈是一个栈结构&#xff0c;由许多个栈帧组成&#xff0c;一个方法分配一个栈帧&#xff0c;线程每执行一个方法时都会有一个栈帧入栈&#xff0c;方法执行结束后栈帧…

WAV文件双轨PCM格式详细说明及C语言解析示例

WAV文件双轨PCM格式详细说明及C语言解析示例 一、WAV文件双轨PCM格式详细说明1. WAV文件基本结构2. PCM编码方式3. 双轨PCM格式详细说明二、C语言解析WAV文件的代码示例代码说明一、WAV文件双轨PCM格式详细说明 WAV文件是一种用于存储未压缩音频数据的文件格式,广泛应用于音频…

QT------模型/视图

一、模型/视图结构概述 基本原理&#xff1a; Qt 的模型/视图&#xff08;Model/View&#xff09;架构将数据的存储和显示分离&#xff0c;提高了代码的可维护性和复用性。模型&#xff08;Model&#xff09;&#xff1a;负责存储和管理数据&#xff0c;提供数据的访问接口&am…

vue3+ts+element-plus 表单el-form取消回车默认提交

问题描述&#xff1a;在表单el-form中的el-input中按回车后&#xff0c;页面会刷新&#xff0c;url也会改变&#xff0c; 回车前&#xff1a; 回车后&#xff1a; 相关代码&#xff1a; 解决方法1&#xff1a;在 el-form 上阻止默认的 submit 事件&#xff0c;增加 submit.pre…

掌握大数据处理利器:Flink 知识点全面总结【上】

1.Flink的特点 Apache Flink 是一个框架和分布式处理引擎&#xff0c;用于对无界和有界数据流进行状态计算。 Flink主要特点如下&#xff1a; 高吞吐和低延迟。每秒处理数百万个事件&#xff0c;毫秒级延迟。结果的准确性。Flink提供了事件时间(event--time)和处理时间(proces…

国产数据库-崖山使用介绍

本文档基于崖山数据库23.3 个人版本&#xff0c;单机&#xff08;主备&#xff09;部署模式的情况下的使用介绍。 数据库实例状态&#xff1a; NOMOUNT&#xff1a;仅读取参数文件&#xff0c;不加载数据库 MOUNT&#xff1a;读取控制文件&#xff0c;加载数据库&#xff…

Pytest基础01: 入门demo脚本

目录 1 Pytest接口测试 1.1 最简单版hello world 1.2 pytest.ini 2 pytest兼容unittest 3 封装pytest执行入口 1 Pytest接口测试 Pyest是一个可以用于接口测试的强大框架&#xff0c;开源社区也有非常多的pytest插件。 按江湖传统&#xff0c;学习一个新语言或者新框架&…

如何在没有 iCloud 的情况下将数据从 iPhone 传输到 iPhone

概括 您可能会遇到将数据从 iPhone 转移到 iPhone 的情况&#xff0c;尤其是当您获得新的 iPhone 15/14 时&#xff0c;您会很兴奋并希望将数据转移到它。 使用iCloud最终可以做到这一点&#xff0c;但它的缺点也不容忽视&#xff0c;阻碍了你选择它。例如&#xff0c;您需要…

streamlit、shiny、gradio、fastapi四个web APP平台体验

streamlit、shiny、gradio、fastapi四个web APP平台体验 经常被问的问题就是&#xff1a;web APP平台哪个好&#xff1f;该用哪个&#xff1f;刚开始只有用streamlit和shiny&#xff0c;最近体验了一下gradio和fastapi&#xff0c;今天根据自己的体会尝试着回答一下。 使用R语…

HTML5滑块(Slider)

HTML5 的滑块&#xff08;Slider&#xff09;控件允许用户通过拖动滑块来选择数值。以下是如何实现一个简单的滑块组件的详细说明。 HTML5 滑块组件 1. 基本结构 使用 <input type"range"> 元素可以创建一个滑块。下面是基本实现的代码示例&#xff1a; <…

探索 .idea 文件夹:Java Maven 工程的隐形守护者

一、.idea文件夹深度解析&#xff1a;IntelliJ IDEA项目配置的核心 在Java Maven工程的开发环境中&#xff0c;.idea文件夹扮演着举足轻重的角色。这是IntelliJ IDEA项目特有的一个配置文件夹&#xff0c;它包含了项目所需的各种配置信息&#xff0c;以确保项目能够在不同的开发…