// 定义Partical类 import * as THREE from 'three'; import gsap from 'gsap'; export default class SmokePartical { range; // 粒子的分布半径 center; // 粒子的分布中心 life; // 粒子的存活时间,毫秒 createTime; // 粒子创建时间 updateTime; // 上次更新时间 size; // 粒子大小 opacityFactor; // 粒子透系数 opacity = 0; //粒子透明度 scale = 0; // 粒子缩放量 scaleFactor; //粒子放大系数 position; // 粒子位置 speed; //粒子扩散速度 normal; // 粒子法向 pathArr = []; // 运动路径对象,包含路径、是否扩散等信息 pathLifeRatio: number[] = []; updateFactor = 5; pathIndex = 0; isDie = false; currentPathAlpha = 0; // 当前路段的插值因素 // 动画更新系数; constructor(range = 10, center = { x: 0, y: 0, z: 0 }, opacityFactor, scaleFactor, life, pathArr?) { this.range = range; this.center = center; this.life = life; this.createTime = Date.now(); this.updateTime = Date.now(); this.size = 15; // 粒子透明度,及系数 this.opacityFactor = opacityFactor; // this.opacity = 1 * this.opacityFactor; // 粒子放大量,及放大系数 this.scaleFactor = scaleFactor; this.position = { x: 0, y: 0, z: 0, }; this.initDistance(pathArr); } initDistance(pathArr) { let totalLen = 0; const pathLen: number[] = []; for (let i = 0; i < pathArr.length; i++) { const obj = { path0: pathArr[i].path0.clone(), path1: pathArr[i].path1.clone(), isSpread: pathArr[i].isSpread, spreadDirection: pathArr[i].spreadDirection, // 1是由小变大,-1是由大变小 spreadRang: pathArr[i]['spreadRang'] || 0, }; if (obj.isSpread) { if (obj.spreadDirection >= 1) { let vec; if (Math.abs(obj.path0.y - obj.path1.y) > 3) { const len = obj.spreadRang ? obj.spreadRang : 8; vec = new THREE.Vector3( (Math.random() * 2 - 1) * 3 + obj.path1.x, Math.random() * len + obj.path1.y, (Math.random() * 2 - 1) * 3 + obj.path1.z ); } else { vec = new THREE.Vector3( (Math.random() * 2 - 1) * 3 + obj.path1.x, (Math.random() * 2 - 1) * 3 + obj.path1.y, (Math.random() * 2 - 1) * 3 + obj.path1.z ); } obj.path1.copy(vec); } else if (obj.spreadDirection == -1) { let vec; if (Math.abs(obj.path0.y - obj.path1.y) > 3) { vec = new THREE.Vector3( (Math.random() * 2 - 1) * 3 + obj.path0.x, Math.random() * 8 + obj.path0.y, (Math.random() * 2 - 1) * 3 + obj.path0.z ); } else { vec = new THREE.Vector3( (Math.random() * 2 - 1) * 3 + obj.path0.x, (Math.random() * 2 - 1) * 3 + obj.path0.y, (Math.random() * 2 - 1) * 3 + obj.path0.z ); } obj.path0.copy(vec); } } else if (obj.spreadDirection != 0) { const len = 1; const vec = new THREE.Vector3( (Math.random() * 2 - 1) * 3 * len + obj.path0.x, (Math.random() * 2 - 1) * obj.spreadDirection * len + obj.path0.y, (Math.random() * 2 - 1) * 3 * len + obj.path0.z ); const vec1 = new THREE.Vector3( (Math.random() * 2 - 1) * 3 * len + obj.path1.x, (Math.random() * 2 - 1) * obj.spreadDirection * len + obj.path1.y, (Math.random() * 2 - 1) * 3 * len + obj.path1.z ); obj.path0.copy(vec); obj.path1.copy(vec1); } this.pathArr.push(obj); const len = obj.path0.distanceTo(obj.path1); pathLen.push(len); totalLen += len; } pathLen.forEach((len) => { this.pathLifeRatio.push(len / totalLen); }); } // 更新粒子 update() { if (!this.pathArr || this.pathArr.length - 1 < this.pathIndex) { this.isDie = true; return; } let isFirst = false; if (this.currentPathAlpha == 0) { isFirst = true; } const pathArr = this.pathArr[this.pathIndex]; const position = new THREE.Vector3(this.position.x, this.position.y, this.position.z); const life = this.pathLifeRatio[this.pathIndex] * this.life; this.currentPathAlpha += 1 / life; const currentPathAlpha = this.currentPathAlpha > 1 ? 1 : this.currentPathAlpha; position.lerpVectors(pathArr.path0, pathArr.path1, currentPathAlpha); if (position != undefined) { // 更新位置 this.position.x = position.x; this.position.y = position.y; this.position.z = position.z; if (!pathArr.isSpread) { if (isFirst) this.opacity = this.opacityFactor * 0.5; if (isFirst) this.scale = ((0.3 * Math.random() + 0.7) / 2) * this.scaleFactor; } else { if (pathArr.spreadDirection == 1) { // 计算粒子透明度 this.opacity = 1 - currentPathAlpha; this.opacity *= this.opacityFactor * 0.5; if (this.opacity < 0) this.opacity = 0; // 计算放大量 this.scale = this.scaleFactor * (currentPathAlpha + 1 / 2); // if (this.scale > 1 + this.scaleFactor) this.scale = 1.5 * this.scaleFactor; } else { // 计算粒子透明度 this.opacity = currentPathAlpha * currentPathAlpha * currentPathAlpha; // this.opacity = this.currentPathAlpha; this.opacity *= this.opacityFactor * 0.5; if (this.opacity < 0) this.opacity = 0; // 计算放大量 this.scale = this.scaleFactor * (1 - currentPathAlpha + 1 / 2); } } if (this.currentPathAlpha >= 1) { ++this.pathIndex; this.currentPathAlpha = 0; } } } controlUpdate() { // gsap.to(this.speed, { // x: 250, // duration: 1, // ease: 'none', // repeat: -1, // }); } }