SmokePartical.ts 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. // 定义Partical类
  2. import * as THREE from 'three';
  3. import gsap from 'gsap';
  4. export default class SmokePartical {
  5. range; // 粒子的分布半径
  6. center; // 粒子的分布中心
  7. life; // 粒子的存活时间,毫秒
  8. createTime; // 粒子创建时间
  9. updateTime; // 上次更新时间
  10. size; // 粒子大小
  11. opacityFactor; // 粒子透系数
  12. opacity = 0; //粒子透明度
  13. scale = 0; // 粒子缩放量
  14. scaleFactor; //粒子放大系数
  15. position; // 粒子位置
  16. speed; //粒子扩散速度
  17. normal; // 粒子法向
  18. pathArr = []; // 运动路径对象,包含路径、是否扩散等信息
  19. pathLifeRatio: number[] = [];
  20. updateFactor = 5;
  21. pathIndex = 0;
  22. isDie = false;
  23. currentPathAlpha = 0; // 当前路段的插值因素
  24. // 动画更新系数;
  25. constructor(range = 10, center = { x: 0, y: 0, z: 0 }, opacityFactor, scaleFactor, life, pathArr?) {
  26. this.range = range;
  27. this.center = center;
  28. this.life = life;
  29. this.createTime = Date.now();
  30. this.updateTime = Date.now();
  31. this.size = 15;
  32. // 粒子透明度,及系数
  33. this.opacityFactor = opacityFactor;
  34. // this.opacity = 1 * this.opacityFactor;
  35. // 粒子放大量,及放大系数
  36. this.scaleFactor = scaleFactor;
  37. this.position = {
  38. x: 0,
  39. y: 0,
  40. z: 0,
  41. };
  42. this.initDistance(pathArr);
  43. }
  44. initDistance(pathArr) {
  45. let totalLen = 0;
  46. const pathLen: number[] = [];
  47. for (let i = 0; i < pathArr.length; i++) {
  48. const obj = {
  49. path0: pathArr[i].path0.clone(),
  50. path1: pathArr[i].path1.clone(),
  51. isSpread: pathArr[i].isSpread,
  52. spreadDirection: pathArr[i].spreadDirection, // 1是由小变大,-1是由大变小
  53. spreadRang: pathArr[i]['spreadRang'] || 0,
  54. };
  55. if (obj.isSpread) {
  56. if (obj.spreadDirection >= 1) {
  57. let vec;
  58. if (Math.abs(obj.path0.y - obj.path1.y) > 3) {
  59. const len = obj.spreadRang ? obj.spreadRang : 8;
  60. vec = new THREE.Vector3(
  61. (Math.random() * 2 - 1) * 3 + obj.path1.x,
  62. Math.random() * len + obj.path1.y,
  63. (Math.random() * 2 - 1) * 3 + obj.path1.z
  64. );
  65. } else {
  66. vec = new THREE.Vector3(
  67. (Math.random() * 2 - 1) * 3 + obj.path1.x,
  68. (Math.random() * 2 - 1) * 3 + obj.path1.y,
  69. (Math.random() * 2 - 1) * 3 + obj.path1.z
  70. );
  71. }
  72. obj.path1.copy(vec);
  73. } else if (obj.spreadDirection == -1) {
  74. let vec;
  75. if (Math.abs(obj.path0.y - obj.path1.y) > 3) {
  76. vec = new THREE.Vector3(
  77. (Math.random() * 2 - 1) * 3 + obj.path0.x,
  78. Math.random() * 8 + obj.path0.y,
  79. (Math.random() * 2 - 1) * 3 + obj.path0.z
  80. );
  81. } else {
  82. vec = new THREE.Vector3(
  83. (Math.random() * 2 - 1) * 3 + obj.path0.x,
  84. (Math.random() * 2 - 1) * 3 + obj.path0.y,
  85. (Math.random() * 2 - 1) * 3 + obj.path0.z
  86. );
  87. }
  88. obj.path0.copy(vec);
  89. }
  90. } else if (obj.spreadDirection != 0) {
  91. const len = 1;
  92. const vec = new THREE.Vector3(
  93. (Math.random() * 2 - 1) * 3 * len + obj.path0.x,
  94. (Math.random() * 2 - 1) * obj.spreadDirection * len + obj.path0.y,
  95. (Math.random() * 2 - 1) * 3 * len + obj.path0.z
  96. );
  97. const vec1 = new THREE.Vector3(
  98. (Math.random() * 2 - 1) * 3 * len + obj.path1.x,
  99. (Math.random() * 2 - 1) * obj.spreadDirection * len + obj.path1.y,
  100. (Math.random() * 2 - 1) * 3 * len + obj.path1.z
  101. );
  102. obj.path0.copy(vec);
  103. obj.path1.copy(vec1);
  104. }
  105. this.pathArr.push(obj);
  106. const len = obj.path0.distanceTo(obj.path1);
  107. pathLen.push(len);
  108. totalLen += len;
  109. }
  110. pathLen.forEach((len) => {
  111. this.pathLifeRatio.push(len / totalLen);
  112. });
  113. }
  114. // 更新粒子
  115. update() {
  116. if (!this.pathArr || this.pathArr.length - 1 < this.pathIndex) {
  117. this.isDie = true;
  118. return;
  119. }
  120. let isFirst = false;
  121. if (this.currentPathAlpha == 0) {
  122. isFirst = true;
  123. }
  124. const pathArr = this.pathArr[this.pathIndex];
  125. const position = new THREE.Vector3(this.position.x, this.position.y, this.position.z);
  126. const life = this.pathLifeRatio[this.pathIndex] * this.life;
  127. this.currentPathAlpha += 1 / life;
  128. const currentPathAlpha = this.currentPathAlpha > 1 ? 1 : this.currentPathAlpha;
  129. position.lerpVectors(pathArr.path0, pathArr.path1, currentPathAlpha);
  130. if (position != undefined) {
  131. // 更新位置
  132. this.position.x = position.x;
  133. this.position.y = position.y;
  134. this.position.z = position.z;
  135. if (!pathArr.isSpread) {
  136. if (isFirst) this.opacity = this.opacityFactor * 0.5;
  137. if (isFirst) this.scale = ((0.3 * Math.random() + 0.7) / 2) * this.scaleFactor;
  138. } else {
  139. if (pathArr.spreadDirection == 1) {
  140. // 计算粒子透明度
  141. this.opacity = 1 - currentPathAlpha;
  142. this.opacity *= this.opacityFactor * 0.5;
  143. if (this.opacity < 0) this.opacity = 0;
  144. // 计算放大量
  145. this.scale = this.scaleFactor * (currentPathAlpha + 1 / 2);
  146. // if (this.scale > 1 + this.scaleFactor) this.scale = 1.5 * this.scaleFactor;
  147. } else {
  148. // 计算粒子透明度
  149. this.opacity = currentPathAlpha * currentPathAlpha * currentPathAlpha;
  150. // this.opacity = this.currentPathAlpha;
  151. this.opacity *= this.opacityFactor * 0.5;
  152. if (this.opacity < 0) this.opacity = 0;
  153. // 计算放大量
  154. this.scale = this.scaleFactor * (1 - currentPathAlpha + 1 / 2);
  155. }
  156. }
  157. if (this.currentPathAlpha >= 1) {
  158. ++this.pathIndex;
  159. this.currentPathAlpha = 0;
  160. }
  161. }
  162. }
  163. controlUpdate() {
  164. // gsap.to(this.speed, {
  165. // x: 250,
  166. // duration: 1,
  167. // ease: 'none',
  168. // repeat: -1,
  169. // });
  170. }
  171. }