当前位置: 首页 > 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(); // 释放材质}
}

三、总结

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

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

相关文章:

  • Go语言实战:使用 excelize 实现多层复杂Excel表头导出教程
  • idea 插件开发自动发布到 nexus 私服中(脚本实例)
  • 随记1-LLM多轮对话的陷阱
  • LTX-Videov本地部署教程:时空扩散+多尺度渲染,重塑AI视频研究范式
  • 至此(day1-day4)代码详解(ai辅助整理)
  • Python代码加密与发布方案详解
  • 计算机图形学Games101笔记--几何
  • leetcode字符串篇【公共前缀】:14-最长公共前缀
  • NebulaGraph学习笔记-SessionPool之Session not existed
  • 常见高速电路设计与信号完整性核心概念
  • SVA 断言16.9 Sequence operations序列运算翻译笔记(12)
  • 香港科技大学(广州)智能制造理学硕士招生宣讲会——深圳大学专场
  • Nextjs App Router 开发指南
  • leetcode 找到字符串中所有字母异位词 java
  • 百度网盘加速补丁v7.14.1.6使用指南|PC不限速下载实操教程
  • 你知道mysql的索引下推么?
  • Doris高性能读能力与实时性实现原理
  • 【优秀三方库研读】在 quill 开源库中 QUILL_MAGIC_SEPARATOR 的作用是什么,解决了什么问题
  • 【Java】封装在 Java 中是怎样实现的?
  • 基于springboot的网上学校超市商城系统【附源码】
  • 淡出政治舞台?马斯克称将削减政治开支:已经做得够多了
  • 牛市早报|央行:加力支持提振消费、稳定外贸等领域,用好用足存量增量政策
  • 梅花奖在上海丨好戏在上海不缺观众,冷门剧种叫好又叫座
  • 媒体:演员黄杨钿甜耳环事件仍有三大疑问待解
  • 媒体报道一民企投资400万运营出租车4年未获批,广西隆林县回应
  • 波兰总统选举投票开始,将是对亲欧路线的一次严峻考验