当前位置: 首页 > news >正文

vue+three.js 五彩烟花效果封装+加载字体

        嗨,我是小路。今天主要和大家分享的主题是“vue+three.js 五彩烟花效果封装+加载字体”。        

在做烟花的基础的基础上,加上文字字体,这是很多背景版常用的基础极端。如节日庆典。今天主要是准备一个简单的端午节快乐的字体面板。

1.FontLoader、TextGeometry的加载

注意:在three.js中,在加载字体的时候,不能按照库包里面位置进行加载,需要加载成如下:

import { FontLoader } from "three/addons/loaders/FontLoader.js";
import { TextGeometry } from "three/addons/geometries/TextGeometry.js";

2.字体json的加载

注意:在three.js中,在加载字体的时候,需要将字体转换成json格式;当然也可以用three.js提供的字体库;

3.烟花类的封装以及颜色的改变

注意:在进行方法封装时,可以将类通过export default进行封装,同时传入scene,这样保证整个屏幕在创建烟花之后,能在屏幕上加载出来;同时在材料中,进行颜色设置;去除顶点颜色配置,并设置默认的颜色;

二、实例代码

<template><div class="pageBox"><div class="leftBox" ref="leftRef"></div></div></template>
<script setup>
import { onMounted, reactive, ref } from 'vue';
import * as THREE from 'three';
// 引入轨道控制器扩展库OrbitControls.js
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { FontLoader } from "three/addons/loaders/FontLoader.js";
import { TextGeometry } from "three/addons/geometries/TextGeometry.js";
import Firework from "../utils/Firework"const leftRef = ref();
// 定义相机输出画布的尺寸(单位:像素px)
let width = window.innerWidth; //宽度
let height = window.innerHeight; //高度
// 创建3D场景对象Scene
const scene = new THREE.Scene();
//设置背景色
scene.background = new THREE.Color(0x002244);const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000);
camera.position.z = 5;// 创建渲染器对象
const renderer = new THREE.WebGLRenderer();const createLight = () => {const pointLight = new THREE.PointLight(0xffffff, 0.8);pointLight.position.set(10, 10, 10);scene.add(pointLight);// 添加光源和背景等(可选)const ambientLight = new THREE.AmbientLight(0xffffff, 0.8); // 环境光scene.add(ambientLight);
}
let currentText;//生成的字体模型const createFont = (text = "端午节快乐",size = 1,height = 0.4,//字体的宽度bevel = false) => {if (currentText) {scene.remove(currentText);}const fontLoader = new FontLoader();fontLoader.load('./fonts/FangSong_Regular.json',function(font){const textGeometry = new TextGeometry(text,{font: font,size: size,height: height,curveSegments: 32,bevelEnabled: bevel,bevelThickness: 0.05,bevelSize: 0.02,bevelOffset: 0,bevelSegments: 8,});textGeometry.center();const material = new THREE.MeshStandardMaterial({color: 0xffaa00,//设置字体颜色metalness: 0.3,//金属性贴图roughness: 0.8,//粗糙程度});currentText = new THREE.Mesh(textGeometry, material);scene.add(currentText);})
}const fireworks = [];
/*** 创建随机位置的烟花* 在场景的合理范围内随机选择位置*/
const createRandomFirework = () => {const x = (Math.random() * 2 - 1) * 30; // x范围:-30到30const y = (Math.random() * 2 - 1) * 25; // y范围:-25到25const z = (Math.random() * 2 - 1) * 25; // y范围:-25到25fireworks.push(new Firework(x, y, z,scene));
}const renderFirework = ()=>{// 有25%的概率生成新烟花if (Math.random() < 0.25) {createRandomFirework();}// 更新所有烟花,移除已经消失的烟花for (let i = fireworks.length - 1; i >= 0; i--) {const alive = fireworks[i].update(scene);if (!alive) {fireworks[i].dispose(scene);fireworks.splice(i, 1);}}
}onMounted(() => {createLight();initData()//添加相机空间const controls = new OrbitControls(camera, renderer.domElement);// 如果OrbitControls改变了相机参数,重新调用渲染器渲染三维场景controls.addEventListener('change', function () {renderer.render(scene, camera); //执行渲染操作});//监听鼠标、键盘事件renderer.setSize(width, height); //设置three.js渲染区域的尺寸(像素px)//将innerHTML置空,避免append重复添加渲染leftRef.value.innerHTML = ''leftRef.value.append(renderer.domElement);})
const initData = () => {//创建字体createFont();render();
}
function render() {requestAnimationFrame(render);renderFirework();renderer.render(scene, camera);
}</script>
<style scoped lang="less">
.pageBox {width: 100%;height: 100vh;padding: 0;margin: 0;display: flex;justify-content: space-between;align-items: center;.rightBox {width: 100%;height: 100%;}
}
</style>
/*** 烟花粒子类* 负责创建和管理单个烟花的所有粒子*/
import * as THREE from 'three';
/*** 烟花粒子类* 负责创建和管理单个烟花的所有粒子*/
export default class Firework {constructor(x, y, z,scene) {// 初始化属性this.particles = []; // 粒子数组this.geometry = new THREE.BufferGeometry(); // 粒子几何体this.count = 10000; // 粒子数量this.positions = new Float32Array(this.count * 3); // 粒子位置数组this.velocities = []; // 粒子速度数组this.colors = new Float32Array(this.count * 3); // 粒子颜色数组this.sizes = new Float32Array(this.count); // 粒子大小数组this.life = new Float32Array(this.count); // 粒子生命周期数组this.scene = scene;//定义可以渲染的颜色this.colorArr = [0xfff44ff,0xfff33ff,0xff00fff,0xff34fff,0xff45fff,0xfff33ff,0xfff22ff,0xfffff00,0xfffffff,0xfffff11]// 初始化每个粒子for (let i = 0; i < this.count; i++) {// 使用球面坐标系计算粒子初始方向const phi = Math.random() * Math.PI * 2; // 水平角度const theta = Math.random() * Math.PI; // 垂直角度const velocity = 2 + Math.random() * 2; // 随机速度// 计算粒子速度向量this.velocities.push(velocity * Math.sin(theta) * Math.cos(phi), // x方向速度velocity * Math.sin(theta) * Math.sin(phi), // y方向速度velocity * Math.cos(theta) // z方向速度);// 设置粒子初始位置this.positions[i * 3] = x; // x坐标this.positions[i * 3 + 1] = y; // y坐标this.positions[i * 3 + 2] = z; // z坐标// 设置粒子颜色(红色为主,带随机变化)this.colors[i * 3] = 1.0; // 红色通道this.colors[i * 3 + 1] = Math.random() * 0.2; // 绿色通道this.colors[i * 3 + 2] = Math.random() * 0.2; // 蓝色通道// 初始化粒子大小和生命值this.sizes[i] = 0.3; // 初始大小this.life[i] = 1.0; // 初始生命值}// 设置几何体属性this.geometry.setAttribute("position",new THREE.BufferAttribute(this.positions, 3));this.geometry.setAttribute("color",new THREE.BufferAttribute(this.colors, 3));this.geometry.setAttribute("size",new THREE.BufferAttribute(this.sizes, 1));// 创建粒子材质const material = new THREE.PointsMaterial({size: 0.3, // 粒子大小vertexColors: false, // 启用顶点颜色blending: THREE.AdditiveBlending, // 使用加法混合transparent: true, // 启用透明opacity: 0.8, // 设置透明度color:this.colorArr[Math.floor(Math.random()*10)]//生成随机颜色});// 创建粒子系统并添加到场景this.points = new THREE.Points(this.geometry, material);this.scene.add(this.points);}//生成随机颜色createColor(){let num = Math.floor(Math.random()*100)%16num.toString()let b = '0x'+(Math.floor(Math.random() * 0xFFFFFF).toString(16).padStart(6, '0')).toUpperCase();b = b.toUpperCase()console.log('color',b);return '0x'+b;}// 更新烟花状态update() {let alive = false;for (let i = 0; i < this.count; i++) {if (this.life[i] > 0) {alive = true;// 根据速度更新位置this.positions[i * 3] += this.velocities[i * 3] * 0.1;this.positions[i * 3 + 1] += this.velocities[i * 3 + 1] * 0.1;this.positions[i * 3 + 2] += this.velocities[i * 3 + 2] * 0.1;// 添加重力效果this.velocities[i * 3 + 1] -= 0.05;// 更新生命值和大小this.life[i] -= 0.015;this.sizes[i] = this.life[i] * 0.3;}}// 标记属性需要更新this.geometry.attributes.position.needsUpdate = true;this.geometry.attributes.size.needsUpdate = true;return alive; // 返回是否还有活着的粒子}// 清理烟花资源dispose() {this.scene.remove(this.points); // 从场景中移除this.geometry.dispose(); // 释放几何体this.points.material.dispose(); // 释放材质}
}

三、总结

       宁可十年不将军,不可一日不拱卒。预祝大家端午节快乐!

都看到这里了,记得【点赞】+【关注】哟。

http://www.xdnf.cn/news/554689.html

相关文章:

  • AI一周事件(2025年5月13日-5月19日)
  • 外部因素导致的 ADC误差来源分析
  • 苍穹外卖04 新增菜品菜品分页查询删除菜品修改菜品
  • C语言经典面试题及答案100道
  • 思维模型和法则
  • WHAT - CSS 中的 min-width
  • HarmonyOS5云服务技术分享--自有账号对接AGC认证
  • 每日算法 -【Swift 算法】寻找两个有序数组的中位数(O(log(m+n)))详细讲解版
  • 电商虚拟户:重构资金管理逻辑,解锁高效归集与智能分账新范式
  • YOLO12改进-模块-引入Cascaded Group Attention(CGA)模块 提升小目标检测和复杂场景下的定位精度。
  • 一道并发的面试题,控制并发数量
  • Spring的AOP在什么场景下会失效?
  • 贝叶斯优化+CNN+LSTM=小论文创新点
  • 物联网(IoT)智能项目全景指南:技术构架、实现细节与应用实践
  • Oracle如何解决LATCH:CACHE BUFFERS CHAINS
  • java接口自动化初识
  • 保证数据库 + redis在读写分离场景中事务的一致性
  • 985,成立人工智能学院
  • Java高频面试之并发编程-19
  • 第50天-使用Python+Qt+DeepSeek开发AI运势测算
  • 基于springboot3 VUE3 火车订票系统前后端分离项目适合新手学习的项目包含 智能客服 换乘算法
  • 当前主流的传输技术(如OTN、IP-RAN、FlexE等)
  • C++STL之string
  • 产业互联网+三融战略:重构企业增长密码
  • 人工智能+:职业技能培训的元命题与能力重构
  • Linux 正则表达式 扩展正则表达式 gawk
  • 101个α因子#8
  • Java DTO 深度解析
  • JUC编程monitor、锁膨胀以及相关关键字
  • 定时从接口下载数据到本地