Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.
 
 
 

1852 рядки
57 KiB

  1. /*! pixi-particles 1.6.7 */
  2. /**
  3. * @module Pixi Particles
  4. * @namespace window
  5. */
  6. /**
  7. * Add methods to Array
  8. * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
  9. * @class Array
  10. */
  11. /**
  12. * Shuffles the array
  13. * @method shuffle
  14. * @return {Array} The array, for chaining calls.
  15. */
  16. if (!Array.prototype.shuffle) {
  17. // In EcmaScript 5 specs and browsers that support it you can use the Object.defineProperty
  18. // to make it not enumerable set the enumerable property to false
  19. Object.defineProperty(Array.prototype, 'shuffle', {
  20. enumerable: false,
  21. writable: false,
  22. value: function () {
  23. for (var j, x, i = this.length; i; j = Math.floor(Math.random() * i), x = this[--i], this[i] = this[j], this[j] = x);
  24. return this;
  25. }
  26. });
  27. }
  28. /**
  29. * Get a random item from an array
  30. * @method random
  31. * @return {*} The random item
  32. */
  33. if (!Array.prototype.random) {
  34. Object.defineProperty(Array.prototype, 'random', {
  35. enumerable: false,
  36. writable: false,
  37. value: function () {
  38. return this[Math.floor(Math.random() * this.length)];
  39. }
  40. });
  41. }
  42. /**
  43. * @module Pixi Particles
  44. * @namespace PIXI.particles
  45. */
  46. (function () {
  47. // Check for window, fallback to global
  48. let global = typeof window !== 'undefined' ? window : GLOBAL;
  49. // Define PIXI Flash namespace
  50. let particles = {};
  51. // Export for Node-compatible environments like Electron
  52. if (typeof module !== 'undefined' && module.exports) {
  53. // Attempt to require the pixi module
  54. if (typeof PIXI === 'undefined') {
  55. // Include the Pixi.js module
  56. require('pixi.js');
  57. }
  58. // Export the module
  59. module.exports = particles;
  60. }
  61. // If we're in the browser make sure PIXI is available
  62. else if (typeof PIXI === 'undefined') {
  63. if (true)
  64. throw 'pixi-particles requires pixi.js to be loaded first';
  65. else
  66. throw 'Requires pixi.js';
  67. }
  68. // Assign to global namespace
  69. global.PIXI.particles = global.PIXI.particles || particles;
  70. })();
  71. /**
  72. * @module Pixi Particles
  73. * @namespace PIXI.particles
  74. */
  75. (function (PIXI, undefined) {
  76. let BLEND_MODES = PIXI.BLEND_MODES || PIXI.blendModes;
  77. let Texture = PIXI.Texture;
  78. /**
  79. * Contains helper functions for particles and emitters to use.
  80. * @class ParticleUtils
  81. * @static
  82. */
  83. let ParticleUtils = {};
  84. let DEG_TO_RADS = ParticleUtils.DEG_TO_RADS = Math.PI / 180;
  85. ParticleUtils.useAPI3 = false;
  86. // avoid the string replacement of '"1.6.7"'
  87. let version = PIXI['VER' + 'SION']; // jshint ignore:line
  88. if (version && parseInt(version.substring(0, version.indexOf('.'))) >= 3)
  89. ParticleUtils.useAPI3 = true;
  90. let empty = ParticleUtils.EMPTY_TEXTURE = null;
  91. if (ParticleUtils.useAPI3) {
  92. empty = ParticleUtils.EMPTY_TEXTURE = Texture.EMPTY;
  93. //prevent any events from being used on the empty texture, as well as destruction of it
  94. //v4 of Pixi does this, but doing it again won't hurt
  95. empty.on = empty.destroy = empty.once = empty.emit = function () {};
  96. } else {
  97. let canvas = document.createElement('canvas');
  98. canvas.width = canvas.height = 1;
  99. empty = ParticleUtils.EMPTY_TEXTURE = PIXI.Texture.fromCanvas(canvas);
  100. //have the particle not render, even though we have an empty canvas that would be
  101. //safe to render
  102. empty.baseTexture.hasLoaded = false;
  103. //prevent any events from being used on the empty texture, as well as destruction of it
  104. empty.on = empty.destroy = empty.once = empty.emit = function () {};
  105. }
  106. /**
  107. * Rotates a point by a given angle.
  108. * @method rotatePoint
  109. * @param {Number} angle The angle to rotate by in degrees
  110. * @param {PIXI.Point} p The point to rotate around 0,0.
  111. * @static
  112. */
  113. ParticleUtils.rotatePoint = function (angle, p) {
  114. if (!angle) return;
  115. angle *= DEG_TO_RADS;
  116. let s = Math.sin(angle);
  117. let c = Math.cos(angle);
  118. let xnew = p.x * c - p.y * s;
  119. let ynew = p.x * s + p.y * c;
  120. p.x = xnew;
  121. p.y = ynew;
  122. };
  123. /**
  124. * Combines separate color components (0-255) into a single uint color.
  125. * @method combineRGBComponents
  126. * @param {uint} r The red value of the color
  127. * @param {uint} g The green value of the color
  128. * @param {uint} b The blue value of the color
  129. * @return {uint} The color in the form of 0xRRGGBB
  130. * @static
  131. */
  132. ParticleUtils.combineRGBComponents = function (r, g, b /*, a*/ ) {
  133. return /*a << 24 |*/ r << 16 | g << 8 | b;
  134. };
  135. /**
  136. * Reduces the point to a length of 1.
  137. * @method normalize
  138. * @static
  139. * @param {PIXI.Point} point The point to normalize
  140. */
  141. ParticleUtils.normalize = function (point) {
  142. if ((point.x != 0) || (point.y != 0)) {
  143. let oneOverLen = 1 / ParticleUtils.length(point);
  144. point.x *= oneOverLen;
  145. point.y *= oneOverLen;
  146. }
  147. };
  148. /**
  149. * Multiplies the x and y values of this point by a value.
  150. * @method scaleBy
  151. * @static
  152. * @param {PIXI.Point} point The point to scaleBy
  153. * @param value {Number} The value to scale by.
  154. */
  155. ParticleUtils.scaleBy = function (point, value) {
  156. point.x *= value;
  157. point.y *= value;
  158. };
  159. /**
  160. * Returns the length (or magnitude) of this point.
  161. * @method length
  162. * @static
  163. * @param {PIXI.Point} point The point to measure length
  164. * @return The length of this point.
  165. */
  166. ParticleUtils.length = function (point) {
  167. return Math.sqrt(point.x * point.x + point.y * point.y);
  168. };
  169. /**
  170. * Converts a hex string from "#AARRGGBB", "#RRGGBB", "0xAARRGGBB", "0xRRGGBB",
  171. * "AARRGGBB", or "RRGGBB" to an array of ints of 0-255 or Numbers from 0-1, as
  172. * [r, g, b, (a)].
  173. * @method hexToRGB
  174. * @param {String} color The input color string.
  175. * @param {Array} output An array to put the output in. If omitted, a new array is created.
  176. * @return The array of numeric color values.
  177. * @static
  178. */
  179. ParticleUtils.hexToRGB = function (color, output) {
  180. if (output)
  181. output.length = 0;
  182. else
  183. output = [];
  184. if (color.charAt(0) == '#')
  185. color = color.substr(1);
  186. else if (color.indexOf('0x') === 0)
  187. color = color.substr(2);
  188. let alpha;
  189. if (color.length == 8) {
  190. alpha = color.substr(0, 2);
  191. color = color.substr(2);
  192. }
  193. output.push(parseInt(color.substr(0, 2), 16)); //Red
  194. output.push(parseInt(color.substr(2, 2), 16)); //Green
  195. output.push(parseInt(color.substr(4, 2), 16)); //Blue
  196. if (alpha)
  197. output.push(parseInt(alpha, 16));
  198. return output;
  199. };
  200. /**
  201. * Generates a custom ease function, based on the GreenSock custom ease, as demonstrated
  202. * by the related tool at http://www.greensock.com/customease/.
  203. * @method generateEase
  204. * @param {Array} segments An array of segments, as created by
  205. * http://www.greensock.com/customease/.
  206. * @return {Function} A function that calculates the percentage of change at
  207. * a given point in time (0-1 inclusive).
  208. * @static
  209. */
  210. ParticleUtils.generateEase = function (segments) {
  211. let qty = segments.length;
  212. let oneOverQty = 1 / qty;
  213. /*
  214. * Calculates the percentage of change at a given point in time (0-1 inclusive).
  215. * @param {Number} time The time of the ease, 0-1 inclusive.
  216. * @return {Number} The percentage of the change, 0-1 inclusive (unless your
  217. * ease goes outside those bounds).
  218. */
  219. let simpleEase = function (time) {
  220. let t, s;
  221. let i = (qty * time) | 0; //do a quick floor operation
  222. t = (time - (i * oneOverQty)) * qty;
  223. s = segments[i] || segments[qty - 1];
  224. return (s.s + t * (2 * (1 - t) * (s.cp - s.s) + t * (s.e - s.s)));
  225. };
  226. return simpleEase;
  227. };
  228. /**
  229. * Gets a blend mode, ensuring that it is valid.
  230. * @method getBlendMode
  231. * @param {String} name The name of the blend mode to get.
  232. * @return {int} The blend mode as specified in the PIXI.blendModes enumeration.
  233. * @static
  234. */
  235. ParticleUtils.getBlendMode = function (name) {
  236. if (!name) return BLEND_MODES.NORMAL;
  237. name = name.toUpperCase();
  238. while (name.indexOf(' ') >= 0)
  239. name = name.replace(' ', '_');
  240. return BLEND_MODES[name] || BLEND_MODES.NORMAL;
  241. };
  242. PIXI.particles.ParticleUtils = ParticleUtils;
  243. })(PIXI);
  244. /**
  245. * @module Pixi Particles
  246. * @namespace PIXI.particles
  247. */
  248. (function (PIXI, undefined) {
  249. let ParticleUtils = PIXI.particles.ParticleUtils;
  250. let Sprite = PIXI.Sprite;
  251. let useAPI3 = ParticleUtils.useAPI3;
  252. /**
  253. * An individual particle image. You shouldn't have to deal with these.
  254. * @class Particle
  255. * @constructor
  256. * @param {Emitter} emitter The emitter that controls this particle.
  257. */
  258. let Particle = function (emitter) {
  259. //start off the sprite with a blank texture, since we are going to replace it
  260. //later when the particle is initialized. Pixi v2 requires a texture, v3 supplies a
  261. //blank texture for us.
  262. if (useAPI3)
  263. Sprite.call(this);
  264. else
  265. Sprite.call(this, ParticleUtils.EMPTY_TEXTURE);
  266. /**
  267. * The emitter that controls this particle.
  268. * @property {Emitter} emitter
  269. */
  270. this.emitter = emitter;
  271. //particles should be centered
  272. this.anchor.x = this.anchor.y = 0.5;
  273. /**
  274. * The velocity of the particle. Speed may change, but the angle also
  275. * contained in velocity is constant.
  276. * @property {PIXI.Point} velocity
  277. */
  278. this.velocity = new PIXI.Point();
  279. this.direction = emitter.direction;
  280. /**
  281. * The maximum lifetime of this particle, in seconds.
  282. * @property {Number} maxLife
  283. */
  284. this.maxLife = 0;
  285. /**
  286. * The current age of the particle, in seconds.
  287. * @property {Number} age
  288. */
  289. this.age = 0;
  290. /**
  291. * A simple easing function to be applied to all properties that
  292. * are being interpolated.
  293. * @property {Function} ease
  294. */
  295. this.ease = null;
  296. /**
  297. * Extra data that the emitter passes along for custom particles.
  298. * @property {Object} extraData
  299. */
  300. this.extraData = null;
  301. /**
  302. * The alpha of the particle at the start of its life.
  303. * @property {Number} startAlpha
  304. */
  305. this.startAlpha = 0;
  306. /**
  307. * The alpha of the particle at the end of its life.
  308. * @property {Number} endAlpha
  309. */
  310. this.endAlpha = 0;
  311. /**
  312. * The speed of the particle at the start of its life.
  313. * @property {Number} startSpeed
  314. */
  315. this.startSpeed = 0;
  316. /**
  317. * The speed of the particle at the end of its life.
  318. * @property {Number} endSpeed
  319. */
  320. this.endSpeed = 0;
  321. /**
  322. * Acceleration to apply to the particle.
  323. * @property {PIXI.Point} accleration
  324. */
  325. this.acceleration = new PIXI.Point();
  326. /**
  327. * The scale of the particle at the start of its life.
  328. * @property {Number} startScale
  329. */
  330. this.startScale = 0;
  331. /**
  332. * The scale of the particle at the start of its life.
  333. * @property {Number} endScale
  334. */
  335. this.endScale = 0;
  336. /**
  337. * The tint of the particle at the start of its life.
  338. * @property {Array} startColor
  339. */
  340. this.startColor = null;
  341. /**
  342. * The red tint of the particle at the start of its life.
  343. * This is pulled from startColor in init().
  344. * @property {uint} _sR
  345. * @private
  346. */
  347. this._sR = 0;
  348. /**
  349. * The green tint of the particle at the start of its life.
  350. * This is pulled from startColor in init().
  351. * @property {uint} _sG
  352. * @private
  353. */
  354. this._sG = 0;
  355. /**
  356. * The blue tint of the particle at the start of its life.
  357. * This is pulled from startColor in init().
  358. * @property {uint} _sB
  359. * @private
  360. */
  361. this._sB = 0;
  362. /**
  363. * The tint of the particle at the start of its life.
  364. * @property {Array} endColor
  365. */
  366. this.endColor = null;
  367. /**
  368. * The red tint of the particle at the end of its life.
  369. * This is pulled from endColor in init().
  370. * @property {uint} _eR
  371. * @private
  372. */
  373. this._eR = 0;
  374. /**
  375. * The green tint of the particle at the end of its life.
  376. * This is pulled from endColor in init().
  377. * @property {uint} _sG
  378. * @private
  379. */
  380. this._eG = 0;
  381. /**
  382. * The blue tint of the particle at the end of its life.
  383. * This is pulled from endColor in init().
  384. * @property {uint} _sB
  385. * @private
  386. */
  387. this._eB = 0;
  388. /**
  389. * If alpha should be interpolated at all.
  390. * @property {Boolean} _doAlpha
  391. * @private
  392. */
  393. this._doAlpha = false;
  394. /**
  395. * If scale should be interpolated at all.
  396. * @property {Boolean} _doScale
  397. * @private
  398. */
  399. this._doScale = false;
  400. /**
  401. * If speed should be interpolated at all.
  402. * @property {Boolean} _doSpeed
  403. * @private
  404. */
  405. this._doSpeed = false;
  406. /**
  407. * If acceleration should be handled at all. _doSpeed is mutually exclusive with this,
  408. * and _doSpeed gets priority.
  409. * @property {Boolean} _doAcceleration
  410. * @private
  411. */
  412. this._doAcceleration = false;
  413. /**
  414. * If color should be interpolated at all.
  415. * @property {Boolean} _doColor
  416. * @private
  417. */
  418. this._doColor = false;
  419. /**
  420. * If normal movement should be handled. Subclasses wishing to override movement
  421. * can set this to false in init().
  422. * @property {Boolean} _doNormalMovement
  423. * @private
  424. */
  425. this._doNormalMovement = false;
  426. /**
  427. * One divided by the max life of the particle, saved for slightly faster math.
  428. * @property {Number} _oneOverLife
  429. * @private
  430. */
  431. this._oneOverLife = 0;
  432. /**
  433. * Reference to the next particle in the list.
  434. * @property {Particle} next
  435. * @private
  436. */
  437. this.next = null;
  438. /**
  439. * Reference to the previous particle in the list.
  440. * @property {Particle} prev
  441. * @private
  442. */
  443. this.prev = null;
  444. //save often used functions on the instance instead of the prototype for better speed
  445. this.init = this.init;
  446. this.Particle_init = this.Particle_init;
  447. this.update = this.update;
  448. this.Particle_update = this.Particle_update;
  449. this.applyArt = this.applyArt;
  450. this.kill = this.kill;
  451. };
  452. // Reference to the prototype
  453. let p = Particle.prototype = Object.create(Sprite.prototype);
  454. /**
  455. * Initializes the particle for use, based on the properties that have to
  456. * have been set already on the particle.
  457. * @method init
  458. */
  459. /**
  460. * A reference to init, so that subclasses can access it without the penalty of Function.call()
  461. * @method Particle_init
  462. * @private
  463. */
  464. p.init = p.Particle_init = function () {
  465. //reset the age
  466. this.age = 0;
  467. //set up the velocity based on the start speed and rotation
  468. this.velocity.x = this.startSpeed;
  469. this.velocity.y = 0;
  470. ParticleUtils.rotatePoint(this.rotation, this.velocity);
  471. //convert rotation to Radians from Degrees
  472. this.rotation *= ParticleUtils.DEG_TO_RADS;
  473. //convert rotation speed to Radians from Degrees
  474. this.rotationSpeed *= ParticleUtils.DEG_TO_RADS;
  475. //set alpha to inital alpha
  476. this.alpha = this.startAlpha;
  477. //set scale to initial scale
  478. this.scale.x = this.scale.y = this.startScale;
  479. //determine start and end color values
  480. if (this.startColor) {
  481. this._sR = this.startColor[0];
  482. this._sG = this.startColor[1];
  483. this._sB = this.startColor[2];
  484. if (this.endColor) {
  485. this._eR = this.endColor[0];
  486. this._eG = this.endColor[1];
  487. this._eB = this.endColor[2];
  488. }
  489. }
  490. //figure out what we need to interpolate
  491. this._doAlpha = this.startAlpha != this.endAlpha;
  492. this._doSpeed = this.startSpeed != this.endSpeed;
  493. this._doScale = this.startScale != this.endScale;
  494. this._doColor = !!this.endColor;
  495. this._doAcceleration = this.acceleration.x !== 0 || this.acceleration.y !== 0;
  496. //_doNormalMovement can be cancelled by subclasses
  497. this._doNormalMovement = this._doSpeed || this.startSpeed !== 0 || this._doAcceleration;
  498. //save our lerp helper
  499. this._oneOverLife = 1 / this.maxLife;
  500. //set the inital color
  501. this.tint = ParticleUtils.combineRGBComponents(this._sR, this._sG, this._sB);
  502. //ensure visibility
  503. this.visible = true;
  504. this.rawScale = {
  505. x: this.scale.x,
  506. y: this.scale.y
  507. };
  508. this.rawPosition = {
  509. x: this.position.x,
  510. y: this.position.y
  511. };
  512. };
  513. /**
  514. * Sets the texture for the particle. This can be overridden to allow
  515. * for an animated particle.
  516. * @method applyArt
  517. * @param {PIXI.Texture} art The texture to set.
  518. */
  519. p.applyArt = function (art) {
  520. if (useAPI3) {
  521. //remove warning on PIXI 3
  522. this.texture = art || ParticleUtils.EMPTY_TEXTURE;
  523. } else
  524. this.setTexture(art || ParticleUtils.EMPTY_TEXTURE);
  525. };
  526. /**
  527. * Updates the particle.
  528. * @method update
  529. * @param {Number} delta Time elapsed since the previous frame, in __seconds__.
  530. * @return {Number} The standard interpolation multiplier (0-1) used for all relevant particle
  531. * properties. A value of -1 means the particle died of old age instead.
  532. */
  533. /**
  534. * A reference to update so that subclasses can access the original without the overhead
  535. * of Function.call().
  536. * @method Particle_update
  537. * @param {Number} delta Time elapsed since the previous frame, in __seconds__.
  538. * @return {Number} The standard interpolation multiplier (0-1) used for all relevant particle
  539. * properties. A value of -1 means the particle died of old age instead.
  540. * @private
  541. */
  542. p.update = p.Particle_update = function (delta) {
  543. //increase age
  544. this.age += delta;
  545. //recycle particle if it is too old
  546. if (this.age >= this.maxLife) {
  547. this.kill();
  548. return -1;
  549. }
  550. //determine our interpolation value
  551. let lerp = this.age * this._oneOverLife; //lifetime / maxLife;
  552. if (this.ease) {
  553. if (this.ease.length == 4) {
  554. //the t, b, c, d parameters that some tween libraries use
  555. //(time, initial value, end value, duration)
  556. lerp = this.ease(lerp, 0, 1, 1);
  557. } else {
  558. //the simplified version that we like that takes
  559. //one parameter, time from 0-1. TweenJS eases provide this usage.
  560. lerp = this.ease(lerp);
  561. }
  562. }
  563. //interpolate alpha
  564. if (this._doAlpha)
  565. this.alpha = (this.endAlpha - this.startAlpha) * lerp + this.startAlpha;
  566. //interpolate scale
  567. if (this._doScale) {
  568. let scale = (this.endScale - this.startScale) * lerp + this.startScale;
  569. this.rawScale.x = this.rawScale.y = scale;
  570. this.scale.x = this.scale.y = ~~(scale / 2) * 2;
  571. }
  572. //handle movement
  573. if (this._doNormalMovement) {
  574. //interpolate speed
  575. if (this._doSpeed) {
  576. let speed = (this.endSpeed - this.startSpeed) * lerp + this.startSpeed;
  577. ParticleUtils.normalize(this.velocity);
  578. ParticleUtils.scaleBy(this.velocity, speed);
  579. } else if (this._doAcceleration) {
  580. this.velocity.x += this.acceleration.x * delta;
  581. this.velocity.y += this.acceleration.y * delta;
  582. }
  583. //adjust position based on velocity
  584. let dir = this.direction;
  585. if (dir) {
  586. let dx = dir.x;
  587. let dy = dir.y;
  588. let vx = this.velocity.x;
  589. let vy = this.velocity.y;
  590. if ((vy > 0) && (dy < 0))
  591. vy *= -1;
  592. this.rawPosition.x += dx * vx * delta;
  593. this.rawPosition.y += dy * vy * delta;
  594. } else {
  595. this.rawPosition.x += this.velocity.x * delta;
  596. this.rawPosition.y += this.velocity.y * delta;
  597. }
  598. this.position.x = ~~(this.rawPosition.x / 2) * 2;
  599. this.position.y = ~~(this.rawPosition.y / 2) * 2;
  600. }
  601. //interpolate color
  602. if (this._doColor) {
  603. let r = (this._eR - this._sR) * lerp + this._sR;
  604. let g = (this._eG - this._sG) * lerp + this._sG;
  605. let b = (this._eB - this._sB) * lerp + this._sB;
  606. this.tint = ParticleUtils.combineRGBComponents(r, g, b);
  607. }
  608. //update rotation
  609. if (this.rotationSpeed !== 0)
  610. this.rotation += this.rotationSpeed * delta;
  611. else if (this.acceleration) {
  612. if (this.allowRotation)
  613. this.rotation = Math.atan2(this.velocity.y, this.velocity.x); // + Math.PI / 2;
  614. else
  615. this.rotation = 0;
  616. }
  617. return lerp;
  618. };
  619. /**
  620. * Kills the particle, removing it from the display list
  621. * and telling the emitter to recycle it.
  622. * @method kill
  623. */
  624. p.kill = function () {
  625. this.emitter.recycle(this);
  626. };
  627. p.Sprite_Destroy = Sprite.prototype.destroy;
  628. /**
  629. * Destroys the particle, removing references and preventing future use.
  630. * @method destroy
  631. */
  632. p.destroy = function () {
  633. if (this.parent)
  634. this.parent.removeChild(this);
  635. if (this.Sprite_Destroy)
  636. this.Sprite_Destroy();
  637. this.emitter = this.velocity = this.startColor = this.endColor = this.ease =
  638. this.next = this.prev = null;
  639. };
  640. /**
  641. * Checks over the art that was passed to the Emitter's init() function, to do any special
  642. * modifications to prepare it ahead of time.
  643. * @method parseArt
  644. * @static
  645. * @param {Array} art The array of art data. For Particle, it should be an array of Textures.
  646. * Any strings in the array will be converted to Textures via
  647. * Texture.fromImage().
  648. * @return {Array} The art, after any needed modifications.
  649. */
  650. Particle.parseArt = function (art) {
  651. //convert any strings to Textures.
  652. let i;
  653. for (i = art.length; i >= 0; --i) {
  654. if (typeof art[i] === 'string')
  655. art[i] = PIXI.Texture.from(art[i]);
  656. }
  657. //particles from different base textures will be slower in WebGL than if they
  658. //were from one spritesheet
  659. if (true) {
  660. for (i = art.length - 1; i > 0; --i) {
  661. if (art[i].baseTexture != art[i - 1].baseTexture) {
  662. if (window.console)
  663. console.warn('PixiParticles: using particle textures from different images may hinder performance in WebGL');
  664. break;
  665. }
  666. }
  667. }
  668. return art;
  669. };
  670. /**
  671. * Parses extra emitter data to ensure it is set up for this particle class.
  672. * Particle does nothing to the extra data.
  673. * @method parseData
  674. * @static
  675. * @param {Object} extraData The extra data from the particle config.
  676. * @return {Object} The parsed extra data.
  677. */
  678. Particle.parseData = function (extraData) {
  679. return extraData;
  680. };
  681. PIXI.particles.Particle = Particle;
  682. })(PIXI);
  683. /**
  684. * @module Pixi Particles
  685. * @namespace PIXI.particles
  686. */
  687. (function (PIXI, undefined) {
  688. let ParticleUtils = PIXI.particles.ParticleUtils,
  689. Particle = PIXI.particles.Particle,
  690. ParticleContainer = PIXI.ParticleContainer;
  691. /**
  692. * A particle emitter.
  693. * @class Emitter
  694. * @constructor
  695. * @param {PIXI.DisplayObjectContainer} particleParent The display object to add the
  696. * particles to.
  697. * @param {Array|PIXI.Texture|String} [particleImages] A texture or array of textures to use
  698. * for the particles. Strings will be turned
  699. * into textures via Texture.fromImage().
  700. * @param {Object} [config] A configuration object containing settings for the emitter.
  701. * @param {Boolean} [config.emit=true] If config.emit is explicitly passed as false, the Emitter
  702. * will start disabled.
  703. */
  704. let Emitter = function (particleParent, particleImages, config) {
  705. this.chance = config.chance || null;
  706. this.allowRotation = config.allowRotation || false;
  707. this.randomColor = config.randomColor || false;
  708. this.randomScale = config.randomScale || false;
  709. this.randomSpeed = config.randomSpeed || false;
  710. this.blendMode = config.blendMode;
  711. /**
  712. * The constructor used to create new particles. The default is
  713. * the built in particle class.
  714. * @property {Function} _particleConstructor
  715. * @private
  716. */
  717. this._particleConstructor = Particle;
  718. //properties for individual particles
  719. /**
  720. * An array of PIXI Texture objects.
  721. * @property {Array} particleImages
  722. */
  723. this.particleImages = null;
  724. /**
  725. * The starting alpha of all particles.
  726. * @property {Number} startAlpha
  727. * @default 1
  728. */
  729. this.startAlpha = 1;
  730. /**
  731. * The ending alpha of all particles.
  732. * @property {Number} endAlpha
  733. * @default 1
  734. */
  735. this.endAlpha = 1;
  736. /**
  737. * The starting speed of all particles.
  738. * @property {Number} startSpeed
  739. * @default 0
  740. */
  741. this.startSpeed = 0;
  742. /**
  743. * The ending speed of all particles.
  744. * @property {Number} endSpeed
  745. * @default 0
  746. */
  747. this.endSpeed = 0;
  748. /**
  749. * Acceleration to apply to particles. Using this disables
  750. * any interpolation of particle speed. If the particles do
  751. * not have a rotation speed, then they will be rotated to
  752. * match the direction of travel.
  753. * @property {PIXI.Point} acceleration
  754. * @default null
  755. */
  756. this.acceleration = null;
  757. /**
  758. * The starting scale of all particles.
  759. * @property {Number} startScale
  760. * @default 1
  761. */
  762. this.startScale = 1;
  763. /**
  764. * The ending scale of all particles.
  765. * @property {Number} endScale
  766. * @default 1
  767. */
  768. this.endScale = 1;
  769. /**
  770. * A minimum multiplier for the scale of a particle at both start and
  771. * end. A value between minimumScaleMultiplier and 1 is randomly generated
  772. * and multiplied with startScale and endScale to provide the actual
  773. * startScale and endScale for each particle.
  774. * @property {Number} minimumScaleMultiplier
  775. * @default 1
  776. */
  777. this.minimumScaleMultiplier = 1;
  778. /**
  779. * The starting color of all particles, as red, green, and blue uints from 0-255.
  780. * @property {Array} startColor
  781. */
  782. this.startColor = null;
  783. /**
  784. * The ending color of all particles, as red, green, and blue uints from 0-255.
  785. * @property {Array} endColor
  786. */
  787. this.endColor = null;
  788. /**
  789. * The minimum lifetime for a particle, in seconds.
  790. * @property {Number} minLifetime
  791. */
  792. this.minLifetime = 0;
  793. /**
  794. * The maximum lifetime for a particle, in seconds.
  795. * @property {Number} maxLifetime
  796. */
  797. this.maxLifetime = 0;
  798. /**
  799. * The minimum start rotation for a particle, in degrees. This value
  800. * is ignored if the spawn type is "burst" or "arc".
  801. * @property {Number} minStartRotation
  802. */
  803. this.minStartRotation = 0;
  804. /**
  805. * The maximum start rotation for a particle, in degrees. This value
  806. * is ignored if the spawn type is "burst" or "arc".
  807. * @property {Number} maxStartRotation
  808. */
  809. this.maxStartRotation = 0;
  810. /**
  811. * The minimum rotation speed for a particle, in degrees per second.
  812. * This only visually spins the particle, it does not change direction of movement.
  813. * @property {Number} minRotationSpeed
  814. */
  815. this.minRotationSpeed = 0;
  816. /**
  817. * The maximum rotation speed for a particle, in degrees per second.
  818. * This only visually spins the particle, it does not change direction of movement.
  819. * @property {Number} maxRotationSpeed
  820. */
  821. this.maxRotationSpeed = 0;
  822. /**
  823. * The blend mode for all particles, as named by PIXI.blendModes.
  824. * @property {int} particleBlendMode
  825. */
  826. this.particleBlendMode = 0;
  827. /**
  828. * An easing function for nonlinear interpolation of values. Accepts a single
  829. * parameter of time as a value from 0-1, inclusive. Expected outputs are values
  830. * from 0-1, inclusive.
  831. * @property {Function} customEase
  832. */
  833. this.customEase = null;
  834. /**
  835. * Extra data for use in custom particles. The emitter doesn't look inside, but
  836. * passes it on to the particle to use in init().
  837. * @property {Object} extraData
  838. */
  839. this.extraData = null;
  840. //properties for spawning particles
  841. /**
  842. * Time between particle spawns in seconds.
  843. * @property {Number} _frequency
  844. * @private
  845. */
  846. this._frequency = 1;
  847. /**
  848. * Maximum number of particles to keep alive at a time. If this limit
  849. * is reached, no more particles will spawn until some have died.
  850. * @property {int} maxParticles
  851. * @default 1000
  852. */
  853. this.maxParticles = 1000;
  854. /**
  855. * The amount of time in seconds to emit for before setting emit to false.
  856. * A value of -1 is an unlimited amount of time.
  857. * @property {Number} emitterLifetime
  858. * @default -1
  859. */
  860. this.emitterLifetime = -1;
  861. /**
  862. * Position at which to spawn particles, relative to the emitter's owner's origin.
  863. * For example, the flames of a rocket travelling right might have a spawnPos
  864. * of {x:-50, y:0}.
  865. * to spawn at the rear of the rocket.
  866. * To change this, use updateSpawnPos().
  867. * @property {PIXI.Point} spawnPos
  868. * @readOnly
  869. */
  870. this.spawnPos = null;
  871. /**
  872. * How the particles will be spawned. Valid types are "point", "rectangle",
  873. * "circle", "burst", "ring".
  874. * @property {String} spawnType
  875. * @readOnly
  876. */
  877. this.spawnType = null;
  878. /**
  879. * A reference to the emitter function specific to the spawn type.
  880. * @property {Function} _spawnFunc
  881. * @private
  882. */
  883. this._spawnFunc = null;
  884. /**
  885. * A rectangle relative to spawnPos to spawn particles inside if the spawn type is "rect".
  886. * @property {PIXI.Rectangle} spawnRect
  887. */
  888. this.spawnRect = null;
  889. /**
  890. * A circle relative to spawnPos to spawn particles inside if the spawn type is "circle".
  891. * @property {PIXI.Circle} spawnCircle
  892. */
  893. this.spawnCircle = null;
  894. /**
  895. * Number of particles to spawn each wave in a burst.
  896. * @property {int} particlesPerWave
  897. * @default 1
  898. */
  899. this.particlesPerWave = 1;
  900. /**
  901. * Spacing between particles in a burst. 0 gives a random angle for each particle.
  902. * @property {Number} particleSpacing
  903. * @default 0
  904. */
  905. this.particleSpacing = 0;
  906. /**
  907. * Angle at which to start spawning particles in a burst.
  908. * @property {Number} angleStart
  909. * @default 0
  910. */
  911. this.angleStart = 0;
  912. /**
  913. * Rotation of the emitter or emitter's owner in degrees. This is added to
  914. * the calculated spawn angle.
  915. * To change this, use rotate().
  916. * @property {Number} rotation
  917. * @default 0
  918. * @readOnly
  919. */
  920. this.rotation = 0;
  921. /**
  922. * The world position of the emitter's owner, to add spawnPos to when
  923. * spawning particles. To change this, use updateOwnerPos().
  924. * @property {PIXI.Point} ownerPos
  925. * @default {x:0, y:0}
  926. * @readOnly
  927. */
  928. this.ownerPos = null;
  929. /**
  930. * The origin + spawnPos in the previous update, so that the spawn position
  931. * can be interpolated to space out particles better.
  932. * @property {PIXI.Point} _prevEmitterPos
  933. * @private
  934. */
  935. this._prevEmitterPos = null;
  936. /**
  937. * If _prevEmitterPos is valid, to prevent interpolation on the first update
  938. * @property {Boolean} _prevPosIsValid
  939. * @private
  940. * @default false
  941. */
  942. this._prevPosIsValid = false;
  943. /**
  944. * If either ownerPos or spawnPos has changed since the previous update.
  945. * @property {Boolean} _posChanged
  946. */
  947. this._posChanged = false;
  948. /**
  949. * If the parent is a ParticleContainer from Pixi V3
  950. * @property {Boolean} _parentIsPC
  951. * @private
  952. */
  953. this._parentIsPC = false;
  954. /**
  955. * The display object to add particles to.
  956. * @property {PIXI.DisplayObjectContainer} _parent
  957. * @private
  958. */
  959. this._parent = null;
  960. /**
  961. * If particles should be added at the back of the display list instead of the front.
  962. * @property {Boolean} addAtBack
  963. */
  964. this.addAtBack = false;
  965. /**
  966. * The current number of active particles.
  967. * @property {Number} particleCount
  968. * @readOnly
  969. */
  970. this.particleCount = 0;
  971. /**
  972. * If particles should be emitted during update() calls. Setting this to false
  973. * stops new particles from being created, but allows existing ones to die out.
  974. * @property {Boolean} _emit
  975. * @private
  976. */
  977. this._emit = false;
  978. /**
  979. * The timer for when to spawn particles in seconds, where numbers less
  980. * than 0 mean that particles should be spawned.
  981. * @property {Number} _spawnTimer
  982. * @private
  983. */
  984. this._spawnTimer = 0;
  985. /**
  986. * The life of the emitter in seconds.
  987. * @property {Number} _emitterLife
  988. * @private
  989. */
  990. this._emitterLife = -1;
  991. /**
  992. * The particles that are active and on the display list. This is the first particle in a
  993. * linked list.
  994. * @property {Particle} _activeParticlesFirst
  995. * @private
  996. */
  997. this._activeParticlesFirst = null;
  998. /**
  999. * The particles that are active and on the display list. This is the last particle in a
  1000. * linked list.
  1001. * @property {Particle} _activeParticlesLast
  1002. * @private
  1003. */
  1004. this._activeParticlesLast = null;
  1005. /**
  1006. * The particles that are not currently being used. This is the first particle in a
  1007. * linked list.
  1008. * @property {Particle} _poolFirst
  1009. * @private
  1010. */
  1011. this._poolFirst = null;
  1012. /**
  1013. * The original config object that this emitter was initialized with.
  1014. * @property {Object} _origConfig
  1015. * @private
  1016. */
  1017. this._origConfig = null;
  1018. /**
  1019. * The original particle image data that this emitter was initialized with.
  1020. * @property {PIXI.Texture|Array|String} _origArt
  1021. * @private
  1022. */
  1023. this._origArt = null;
  1024. //set the initial parent
  1025. this.parent = particleParent;
  1026. if (particleImages && config)
  1027. this.init(particleImages, config);
  1028. //save often used functions on the instance instead of the prototype for better speed
  1029. this.recycle = this.recycle;
  1030. this.update = this.update;
  1031. this.rotate = this.rotate;
  1032. this.updateSpawnPos = this.updateSpawnPos;
  1033. this.updateOwnerPos = this.updateOwnerPos;
  1034. };
  1035. // Reference to the prototype
  1036. let p = Emitter.prototype = {};
  1037. let helperPoint = new PIXI.Point();
  1038. /**
  1039. * Time between particle spawns in seconds. If this value is not a number greater than 0,
  1040. * it will be set to 1 (particle per second) to prevent infinite loops.
  1041. * @property {Number} frequency
  1042. */
  1043. Object.defineProperty(p, 'frequency', {
  1044. get: function () {
  1045. return this._frequency;
  1046. },
  1047. set: function (value) {
  1048. //do some error checking to prevent infinite loops
  1049. if (typeof value === 'number' && value > 0)
  1050. this._frequency = value;
  1051. else
  1052. this._frequency = 1;
  1053. }
  1054. });
  1055. /**
  1056. * The constructor used to create new particles. The default is
  1057. * the built in Particle class. Setting this will dump any active or
  1058. * pooled particles, if the emitter has already been used.
  1059. * @property {Function} particleConstructor
  1060. */
  1061. Object.defineProperty(p, 'particleConstructor', {
  1062. get: function () {
  1063. return this._particleConstructor;
  1064. },
  1065. set: function (value) {
  1066. if (value != this._particleConstructor) {
  1067. this._particleConstructor = value;
  1068. //clean up existing particles
  1069. this.cleanup();
  1070. //scrap all the particles
  1071. for (let particle = this._poolFirst; particle; particle = particle.next)
  1072. particle.destroy();
  1073. this._poolFirst = null;
  1074. //re-initialize the emitter so that the new constructor can do anything it needs to
  1075. if (this._origConfig && this._origArt)
  1076. this.init(this._origArt, this._origConfig);
  1077. }
  1078. }
  1079. });
  1080. /**
  1081. * The display object to add particles to. Settings this will dump any active particles.
  1082. * @property {PIXI.DisplayObjectContainer} parent
  1083. */
  1084. Object.defineProperty(p, 'parent', {
  1085. get: function () {
  1086. return this._parent;
  1087. },
  1088. set: function (value) {
  1089. //if our previous parent was a ParticleContainer, then we need to remove
  1090. //pooled particles from it
  1091. if (this._parentIsPC) {
  1092. for (let particle = this._poolFirst; particle; particle = particle.next) {
  1093. if (particle.parent)
  1094. particle.parent.removeChild(particle);
  1095. }
  1096. }
  1097. this.cleanup();
  1098. this._parent = value;
  1099. this._parentIsPC = ParticleContainer && value && value instanceof ParticleContainer;
  1100. }
  1101. });
  1102. /**
  1103. * Sets up the emitter based on the config settings.
  1104. * @method init
  1105. * @param {Array|PIXI.Texture} art A texture or array of textures to use for the particles.
  1106. * @param {Object} config A configuration object containing settings for the emitter.
  1107. */
  1108. p.init = function (art, config) {
  1109. if (!art || !config)
  1110. return;
  1111. //clean up any existing particles
  1112. this.cleanup();
  1113. //store the original config and particle images, in case we need to re-initialize
  1114. //when the particle constructor is changed
  1115. this._origConfig = config;
  1116. this._origArt = art;
  1117. //set up the array of data, also ensuring that it is an array
  1118. art = Array.isArray(art) ? art.slice() : [art];
  1119. //run the art through the particle class's parsing function
  1120. let partClass = this._particleConstructor;
  1121. this.particleImages = partClass.parseArt ? partClass.parseArt(art) : art;
  1122. ///////////////////////////
  1123. // Particle Properties //
  1124. ///////////////////////////
  1125. //set up the alpha
  1126. if (config.alpha) {
  1127. this.startAlpha = config.alpha.start;
  1128. this.endAlpha = config.alpha.end;
  1129. } else
  1130. this.startAlpha = this.endAlpha = 1;
  1131. //set up the speed
  1132. if (config.speed) {
  1133. this.startSpeed = config.speed.start;
  1134. this.endSpeed = config.speed.end;
  1135. } else
  1136. this.startSpeed = this.endSpeed = 0;
  1137. //set up acceleration
  1138. let acceleration = config.acceleration;
  1139. if (acceleration && (acceleration.x || acceleration.y)) {
  1140. this.endSpeed = this.startSpeed;
  1141. this.acceleration = new PIXI.Point(acceleration.x, acceleration.y);
  1142. } else
  1143. this.acceleration = new PIXI.Point();
  1144. //set up the scale
  1145. if (config.scale) {
  1146. this.startScale = config.scale.start;
  1147. this.endScale = config.scale.end;
  1148. this.minimumScaleMultiplier = config.scale.minimumScaleMultiplier || 1;
  1149. } else
  1150. this.startScale = this.endScale = this.minimumScaleMultiplier = 1;
  1151. //set up the color
  1152. if (config.color) {
  1153. if (!this.randomColor)
  1154. this.startColor = ParticleUtils.hexToRGB(config.color.start);
  1155. else {
  1156. this.startColor = config.color.start.map(function (s) {
  1157. return ParticleUtils.hexToRGB(s);
  1158. });
  1159. }
  1160. //if it's just one color, only use the start color
  1161. if (config.color.start != config.color.end) {
  1162. if (!this.randomColor)
  1163. this.endColor = ParticleUtils.hexToRGB(config.color.end);
  1164. else {
  1165. this.endColor = config.color.end.map(function (e) {
  1166. return ParticleUtils.hexToRGB(e);
  1167. });
  1168. }
  1169. } else
  1170. this.endColor = null;
  1171. }
  1172. //set up the start rotation
  1173. if (config.startRotation) {
  1174. this.minStartRotation = config.startRotation.min;
  1175. this.maxStartRotation = config.startRotation.max;
  1176. } else
  1177. this.minStartRotation = this.maxStartRotation = 0;
  1178. //set up the rotation speed
  1179. if (config.rotationSpeed) {
  1180. this.minRotationSpeed = config.rotationSpeed.min;
  1181. this.maxRotationSpeed = config.rotationSpeed.max;
  1182. } else
  1183. this.minRotationSpeed = this.maxRotationSpeed = 0;
  1184. //set up the lifetime
  1185. this.minLifetime = config.lifetime.min;
  1186. this.maxLifetime = config.lifetime.max;
  1187. //get the blend mode
  1188. this.particleBlendMode = ParticleUtils.getBlendMode(config.blendMode);
  1189. //use the custom ease if provided
  1190. if (config.ease) {
  1191. this.customEase = typeof config.ease === 'function' ?
  1192. config.ease :
  1193. ParticleUtils.generateEase(config.ease);
  1194. } else
  1195. this.customEase = null;
  1196. //set up the extra data, running it through the particle class's parseData function.
  1197. if (partClass.parseData)
  1198. this.extraData = partClass.parseData(config.extraData);
  1199. else
  1200. this.extraData = config.extraData || null;
  1201. //////////////////////////
  1202. // Emitter Properties //
  1203. //////////////////////////
  1204. //reset spawn type specific settings
  1205. this.spawnRect = this.spawnCircle = null;
  1206. this.particlesPerWave = 1;
  1207. this.particleSpacing = 0;
  1208. this.angleStart = 0;
  1209. let spawnCircle;
  1210. //determine the spawn function to use
  1211. switch (config.spawnType) {
  1212. case 'rect':
  1213. this.spawnType = 'rect';
  1214. this._spawnFunc = this._spawnRect;
  1215. var spawnRect = config.spawnRect;
  1216. this.spawnRect = new PIXI.Rectangle(spawnRect.x, spawnRect.y, spawnRect.w, spawnRect.h);
  1217. break;
  1218. case 'circle':
  1219. this.spawnType = 'circle';
  1220. this._spawnFunc = this._spawnCircle;
  1221. spawnCircle = config.spawnCircle;
  1222. this.spawnCircle = new PIXI.Circle(spawnCircle.x, spawnCircle.y, spawnCircle.r);
  1223. break;
  1224. case 'ring':
  1225. this.spawnType = 'ring';
  1226. this._spawnFunc = this._spawnRing;
  1227. spawnCircle = config.spawnCircle;
  1228. this.spawnCircle = new PIXI.Circle(spawnCircle.x, spawnCircle.y, spawnCircle.r);
  1229. this.spawnCircle.minRadius = spawnCircle.minR;
  1230. break;
  1231. case 'burst':
  1232. this.spawnType = 'burst';
  1233. this._spawnFunc = this._spawnBurst;
  1234. this.particlesPerWave = config.particlesPerWave;
  1235. this.particleSpacing = config.particleSpacing;
  1236. this.angleStart = config.angleStart ? config.angleStart : 0;
  1237. break;
  1238. case 'point':
  1239. this.spawnType = 'point';
  1240. this._spawnFunc = this._spawnPoint;
  1241. break;
  1242. default:
  1243. this.spawnType = 'point';
  1244. this._spawnFunc = this._spawnPoint;
  1245. break;
  1246. }
  1247. this.direction = config.direction;
  1248. //set the spawning frequency
  1249. this.frequency = config.frequency;
  1250. //set the emitter lifetime
  1251. this.emitterLifetime = config.emitterLifetime || -1;
  1252. //set the max particles
  1253. this.maxParticles = config.maxParticles > 0 ? config.maxParticles : 1000;
  1254. //determine if we should add the particle at the back of the list or not
  1255. this.addAtBack = !!config.addAtBack;
  1256. //reset the emitter position and rotation variables
  1257. this.rotation = 0;
  1258. this.ownerPos = new PIXI.Point();
  1259. this.spawnPos = new PIXI.Point(config.pos.x, config.pos.y);
  1260. this._prevEmitterPos = this.spawnPos.clone();
  1261. //previous emitter position is invalid and should not be used for interpolation
  1262. this._prevPosIsValid = false;
  1263. //start emitting
  1264. this._spawnTimer = 0;
  1265. this.emit = config.emit === undefined ? true : !!config.emit;
  1266. };
  1267. /**
  1268. * Recycles an individual particle.
  1269. * @method recycle
  1270. * @param {Particle} particle The particle to recycle.
  1271. */
  1272. p.recycle = function (particle) {
  1273. if (particle.next)
  1274. particle.next.prev = particle.prev;
  1275. if (particle.prev)
  1276. particle.prev.next = particle.next;
  1277. if (particle == this._activeParticlesLast)
  1278. this._activeParticlesLast = particle.prev;
  1279. if (particle == this._activeParticlesFirst)
  1280. this._activeParticlesFirst = particle.next;
  1281. //add to pool
  1282. particle.prev = null;
  1283. particle.next = this._poolFirst;
  1284. this._poolFirst = particle;
  1285. //remove child from display, or make it invisible if it is in a ParticleContainer
  1286. if (this._parentIsPC) {
  1287. particle.alpha = 0;
  1288. particle.visible = false;
  1289. } else if (particle.parent)
  1290. particle.parent.removeChild(particle);
  1291. //decrease count
  1292. --this.particleCount;
  1293. };
  1294. /**
  1295. * Sets the rotation of the emitter to a new value.
  1296. * @method rotate
  1297. * @param {Number} newRot The new rotation, in degrees.
  1298. */
  1299. p.rotate = function (newRot) {
  1300. if (this.rotation == newRot) return;
  1301. //caclulate the difference in rotation for rotating spawnPos
  1302. let diff = newRot - this.rotation;
  1303. this.rotation = newRot;
  1304. //rotate spawnPos
  1305. ParticleUtils.rotatePoint(diff, this.spawnPos);
  1306. //mark the position as having changed
  1307. this._posChanged = true;
  1308. };
  1309. /**
  1310. * Changes the spawn position of the emitter.
  1311. * @method updateSpawnPos
  1312. * @param {Number} x The new x value of the spawn position for the emitter.
  1313. * @param {Number} y The new y value of the spawn position for the emitter.
  1314. */
  1315. p.updateSpawnPos = function (x, y) {
  1316. this._posChanged = true;
  1317. this.spawnPos.x = x;
  1318. this.spawnPos.y = y;
  1319. };
  1320. /**
  1321. * Changes the position of the emitter's owner. You should call this if you are adding
  1322. * particles to the world display object that your emitter's owner is moving around in.
  1323. * @method updateOwnerPos
  1324. * @param {Number} x The new x value of the emitter's owner.
  1325. * @param {Number} y The new y value of the emitter's owner.
  1326. */
  1327. p.updateOwnerPos = function (x, y) {
  1328. this._posChanged = true;
  1329. this.ownerPos.x = x;
  1330. this.ownerPos.y = y;
  1331. };
  1332. /**
  1333. * Prevents emitter position interpolation in the next update.
  1334. * This should be used if you made a major position change of your emitter's owner
  1335. * that was not normal movement.
  1336. * @method resetPositionTracking
  1337. */
  1338. p.resetPositionTracking = function () {
  1339. this._prevPosIsValid = false;
  1340. };
  1341. /**
  1342. * If particles should be emitted during update() calls. Setting this to false
  1343. * stops new particles from being created, but allows existing ones to die out.
  1344. * @property {Boolean} emit
  1345. */
  1346. Object.defineProperty(p, 'emit', {
  1347. get: function () {
  1348. return this._emit;
  1349. },
  1350. set: function (value) {
  1351. this._emit = !!value;
  1352. this._emitterLife = this.emitterLifetime;
  1353. }
  1354. });
  1355. /**
  1356. * Updates all particles spawned by this emitter and emits new ones.
  1357. * @method update
  1358. * @param {Number} delta Time elapsed since the previous frame, in __seconds__.
  1359. */
  1360. p.update = function (delta) {
  1361. let r = [];
  1362. //if we don't have a parent to add particles to, then don't do anything.
  1363. //this also works as a isDestroyed check
  1364. if (!this._parent) return;
  1365. //update existing particles
  1366. let i, particle, next;
  1367. for (particle = this._activeParticlesFirst; particle; particle = next) {
  1368. next = particle.next;
  1369. particle.update(delta);
  1370. }
  1371. let prevX, prevY;
  1372. //if the previous position is valid, store these for later interpolation
  1373. if (this._prevPosIsValid) {
  1374. prevX = this._prevEmitterPos.x;
  1375. prevY = this._prevEmitterPos.y;
  1376. }
  1377. //store current position of the emitter as local variables
  1378. let curX = this.ownerPos.x + this.spawnPos.x;
  1379. let curY = this.ownerPos.y + this.spawnPos.y;
  1380. //spawn new particles
  1381. if (this.emit) {
  1382. //decrease spawn timer
  1383. this._spawnTimer -= delta;
  1384. //while _spawnTimer < 0, we have particles to spawn
  1385. if (this.chance) {
  1386. this._spawnTimer = 1;
  1387. if (Math.random() < this.chance)
  1388. this._spawnTimer = 0;
  1389. }
  1390. while (this._spawnTimer <= 0) {
  1391. //determine if the emitter should stop spawning
  1392. if (this._emitterLife > 0) {
  1393. this._emitterLife -= this._frequency;
  1394. if (this._emitterLife <= 0) {
  1395. this._spawnTimer = 0;
  1396. this._emitterLife = 0;
  1397. this.emit = false;
  1398. break;
  1399. }
  1400. }
  1401. //determine if we have hit the particle limit
  1402. if (this.particleCount >= this.maxParticles) {
  1403. this._spawnTimer += this._frequency;
  1404. continue;
  1405. }
  1406. //determine the particle lifetime
  1407. var lifetime;
  1408. if (this.minLifetime == this.maxLifetime)
  1409. lifetime = this.minLifetime;
  1410. else
  1411. lifetime = Math.random() * (this.maxLifetime - this.minLifetime) + this.minLifetime;
  1412. //only make the particle if it wouldn't immediately destroy itself
  1413. if (-this._spawnTimer < lifetime) {
  1414. //If the position has changed and this isn't the first spawn,
  1415. //interpolate the spawn position
  1416. var emitPosX, emitPosY;
  1417. if (this._prevPosIsValid && this._posChanged) {
  1418. //1 - _spawnTimer / delta, but _spawnTimer is negative
  1419. let lerp = 1 + this._spawnTimer / delta;
  1420. emitPosX = (curX - prevX) * lerp + prevX;
  1421. emitPosY = (curY - prevY) * lerp + prevY;
  1422. } else //otherwise just set to the spawn position
  1423. {
  1424. emitPosX = curX;
  1425. emitPosY = curY;
  1426. }
  1427. //create enough particles to fill the wave (non-burst types have a wave of 1)
  1428. i = 0;
  1429. for (let len = Math.min(this.particlesPerWave, this.maxParticles - this.particleCount); i < len; ++i) {
  1430. //create particle
  1431. var p;
  1432. if (this._poolFirst) {
  1433. p = this._poolFirst;
  1434. this._poolFirst = this._poolFirst.next;
  1435. p.next = null;
  1436. } else
  1437. p = new this.particleConstructor(this);
  1438. r.push(p);
  1439. //set a random texture if we have more than one
  1440. if (this.particleImages.length > 1)
  1441. p.applyArt(this.particleImages.random());
  1442. else {
  1443. //if they are actually the same texture, a standard particle
  1444. //will quit early from the texture setting in setTexture().
  1445. p.applyArt(this.particleImages[0]);
  1446. }
  1447. //set up the start and end values
  1448. p.startAlpha = this.startAlpha;
  1449. p.endAlpha = this.endAlpha;
  1450. if (!this.randomSpeed) {
  1451. p.startSpeed = this.startSpeed;
  1452. p.endSpeed = this.endSpeed;
  1453. } else {
  1454. let startSpeed = this.startSpeed;
  1455. p.startSpeed = startSpeed.min + ~~(Math.random() * (startSpeed.max - startSpeed.min));
  1456. let endSpeed = this.endSpeed;
  1457. p.endSpeed = endSpeed.min + ~~(Math.random() * (endSpeed.max - endSpeed.min));
  1458. }
  1459. p.acceleration.x = this.acceleration.x;
  1460. p.acceleration.y = this.acceleration.y;
  1461. if (this.minimumScaleMultiplier != 1) {
  1462. let rand = Math.random() * (1 - this.minimumScaleMultiplier) + this.minimumScaleMultiplier;
  1463. p.startScale = this.startScale * rand;
  1464. p.endScale = this.endScale * rand;
  1465. } else if (!this.randomScale) {
  1466. p.startScale = this.startScale;
  1467. p.endScale = this.endScale;
  1468. } else {
  1469. let startScale = this.startScale;
  1470. p.startScale = startScale.min + ~~(Math.random() * (startScale.max - startScale.min));
  1471. let endScale = this.endScale;
  1472. p.endScale = endScale.min + ~~(Math.random() * (endScale.max - endScale.min));
  1473. }
  1474. if (!this.randomColor) {
  1475. p.startColor = this.startColor;
  1476. p.endColor = this.endColor;
  1477. } else {
  1478. let startColor = this.startColor;
  1479. p.startColor = startColor[~~(Math.random() * startColor.length)];
  1480. let endColor = this.endColor;
  1481. p.endColor = endColor[~~(Math.random() * endColor.length)];
  1482. }
  1483. //randomize the rotation speed
  1484. if (this.minRotationSpeed == this.maxRotationSpeed)
  1485. p.rotationSpeed = this.minRotationSpeed;
  1486. else
  1487. p.rotationSpeed = Math.random() * (this.maxRotationSpeed - this.minRotationSpeed) + this.minRotationSpeed;
  1488. //set up the lifetime
  1489. p.maxLife = lifetime;
  1490. //set the blend mode
  1491. p.blendMode = this.particleBlendMode;
  1492. //set the custom ease, if any
  1493. p.ease = this.customEase;
  1494. //set the extra data, if any
  1495. p.extraData = this.extraData;
  1496. //call the proper function to handle rotation and position of particle
  1497. this._spawnFunc(p, emitPosX, emitPosY, i);
  1498. //initialize particle
  1499. p.init();
  1500. if (!this.allowRotation)
  1501. p.rotation = 0;
  1502. //update the particle by the time passed, so the particles are spread out properly
  1503. if (!this.chance)
  1504. p.update(-this._spawnTimer); //we want a positive delta, because a negative delta messes things up
  1505. //add the particle to the display list
  1506. if (!this._parentIsPC || !p.parent) {
  1507. if (this.addAtBack)
  1508. this._parent.addChildAt(p, 0);
  1509. else
  1510. this._parent.addChild(p);
  1511. } else {
  1512. //kind of hacky, but performance friendly
  1513. //shuffle children to correct place
  1514. let children = this._parent.children;
  1515. //avoid using splice if possible
  1516. if (children[0] == p)
  1517. children.shift();
  1518. else if (children[children.length - 1] == p)
  1519. children.pop();
  1520. else {
  1521. let index = children.indexOf(p);
  1522. children.splice(index, 1);
  1523. }
  1524. if (this.addAtBack)
  1525. children.unshift(p);
  1526. else
  1527. children.push(p);
  1528. }
  1529. //add particle to list of active particles
  1530. if (this._activeParticlesLast) {
  1531. this._activeParticlesLast.next = p;
  1532. p.prev = this._activeParticlesLast;
  1533. this._activeParticlesLast = p;
  1534. } else
  1535. this._activeParticlesLast = this._activeParticlesFirst = p;
  1536. ++this.particleCount;
  1537. }
  1538. }
  1539. //increase timer and continue on to any other particles that need to be created
  1540. this._spawnTimer += this._frequency;
  1541. }
  1542. }
  1543. //if the position changed before this update, then keep track of that
  1544. if (this._posChanged) {
  1545. this._prevEmitterPos.x = curX;
  1546. this._prevEmitterPos.y = curY;
  1547. this._prevPosIsValid = true;
  1548. this._posChanged = false;
  1549. }
  1550. return r;
  1551. };
  1552. /**
  1553. * Positions a particle for a point type emitter.
  1554. * @method _spawnPoint
  1555. * @private
  1556. * @param {Particle} p The particle to position and rotate.
  1557. * @param {Number} emitPosX The emitter's x position
  1558. * @param {Number} emitPosY The emitter's y position
  1559. * @param {int} i The particle number in the current wave. Not used for this function.
  1560. */
  1561. p._spawnPoint = function (p, emitPosX, emitPosY, i) {
  1562. //set the initial rotation/direction of the particle based on
  1563. //starting particle angle and rotation of emitter
  1564. if (this.minStartRotation == this.maxStartRotation)
  1565. p.rotation = this.minStartRotation + this.rotation;
  1566. else
  1567. p.rotation = Math.random() * (this.maxStartRotation - this.minStartRotation) + this.minStartRotation + this.rotation;
  1568. //drop the particle at the emitter's position
  1569. p.position.x = emitPosX;
  1570. p.position.y = emitPosY;
  1571. };
  1572. /**
  1573. * Positions a particle for a rectangle type emitter.
  1574. * @method _spawnRect
  1575. * @private
  1576. * @param {Particle} p The particle to position and rotate.
  1577. * @param {Number} emitPosX The emitter's x position
  1578. * @param {Number} emitPosY The emitter's y position
  1579. * @param {int} i The particle number in the current wave. Not used for this function.
  1580. */
  1581. p._spawnRect = function (p, emitPosX, emitPosY, i) {
  1582. //set the initial rotation/direction of the particle based on starting
  1583. //particle angle and rotation of emitter
  1584. if (this.minStartRotation == this.maxStartRotation)
  1585. p.rotation = this.minStartRotation + this.rotation;
  1586. else
  1587. p.rotation = Math.random() * (this.maxStartRotation - this.minStartRotation) + this.minStartRotation + this.rotation;
  1588. //place the particle at a random point in the rectangle
  1589. helperPoint.x = Math.random() * this.spawnRect.width + this.spawnRect.x;
  1590. helperPoint.y = Math.random() * this.spawnRect.height + this.spawnRect.y;
  1591. if (this.rotation !== 0)
  1592. ParticleUtils.rotatePoint(this.rotation, helperPoint);
  1593. p.position.x = emitPosX + helperPoint.x;
  1594. p.position.y = emitPosY + helperPoint.y;
  1595. };
  1596. /**
  1597. * Positions a particle for a circle type emitter.
  1598. * @method _spawnCircle
  1599. * @private
  1600. * @param {Particle} p The particle to position and rotate.
  1601. * @param {Number} emitPosX The emitter's x position
  1602. * @param {Number} emitPosY The emitter's y position
  1603. * @param {int} i The particle number in the current wave. Not used for this function.
  1604. */
  1605. p._spawnCircle = function (p, emitPosX, emitPosY, i) {
  1606. //set the initial rotation/direction of the particle based on starting
  1607. //particle angle and rotation of emitter
  1608. if (this.minStartRotation == this.maxStartRotation)
  1609. p.rotation = this.minStartRotation + this.rotation;
  1610. else {
  1611. p.rotation = Math.random() * (this.maxStartRotation - this.minStartRotation) +
  1612. this.minStartRotation + this.rotation;
  1613. }
  1614. //place the particle at a random radius in the circle
  1615. helperPoint.x = Math.random() * this.spawnCircle.radius;
  1616. helperPoint.y = 0;
  1617. //rotate the point to a random angle in the circle
  1618. ParticleUtils.rotatePoint(Math.random() * 360, helperPoint);
  1619. //offset by the circle's center
  1620. helperPoint.x += this.spawnCircle.x;
  1621. helperPoint.y += this.spawnCircle.y;
  1622. //rotate the point by the emitter's rotation
  1623. if (this.rotation !== 0)
  1624. ParticleUtils.rotatePoint(this.rotation, helperPoint);
  1625. //set the position, offset by the emitter's position
  1626. p.position.x = emitPosX + helperPoint.x;
  1627. p.position.y = emitPosY + helperPoint.y;
  1628. };
  1629. /**
  1630. * Positions a particle for a ring type emitter.
  1631. * @method _spawnRing
  1632. * @private
  1633. * @param {Particle} p The particle to position and rotate.
  1634. * @param {Number} emitPosX The emitter's x position
  1635. * @param {Number} emitPosY The emitter's y position
  1636. * @param {int} i The particle number in the current wave. Not used for this function.
  1637. */
  1638. p._spawnRing = function (p, emitPosX, emitPosY, i) {
  1639. let spawnCircle = this.spawnCircle;
  1640. //set the initial rotation/direction of the particle based on starting
  1641. //particle angle and rotation of emitter
  1642. if (this.minStartRotation == this.maxStartRotation)
  1643. p.rotation = this.minStartRotation + this.rotation;
  1644. else {
  1645. p.rotation = Math.random() * (this.maxStartRotation - this.minStartRotation) +
  1646. this.minStartRotation + this.rotation;
  1647. }
  1648. //place the particle at a random radius in the ring
  1649. if (spawnCircle.minRadius == spawnCircle.radius) {
  1650. helperPoint.x = Math.random() * (spawnCircle.radius - spawnCircle.minRadius) +
  1651. spawnCircle.minRadius;
  1652. } else
  1653. helperPoint.x = spawnCircle.radius;
  1654. helperPoint.y = 0;
  1655. //rotate the point to a random angle in the circle
  1656. let angle = Math.random() * 360;
  1657. p.rotation += angle;
  1658. ParticleUtils.rotatePoint(angle, helperPoint);
  1659. //offset by the circle's center
  1660. helperPoint.x += this.spawnCircle.x;
  1661. helperPoint.y += this.spawnCircle.y;
  1662. //rotate the point by the emitter's rotation
  1663. if (this.rotation !== 0)
  1664. ParticleUtils.rotatePoint(this.rotation, helperPoint);
  1665. //set the position, offset by the emitter's position
  1666. p.position.x = emitPosX + helperPoint.x;
  1667. p.position.y = emitPosY + helperPoint.y;
  1668. };
  1669. /**
  1670. * Positions a particle for a burst type emitter.
  1671. * @method _spawnBurst
  1672. * @private
  1673. * @param {Particle} p The particle to position and rotate.
  1674. * @param {Number} emitPosX The emitter's x position
  1675. * @param {Number} emitPosY The emitter's y position
  1676. * @param {int} i The particle number in the current wave.
  1677. */
  1678. p._spawnBurst = function (p, emitPosX, emitPosY, i) {
  1679. //set the initial rotation/direction of the particle based on spawn
  1680. //angle and rotation of emitter
  1681. if (this.particleSpacing === 0)
  1682. p.rotation = Math.random() * 360;
  1683. else
  1684. p.rotation = this.angleStart + (this.particleSpacing * i) + this.rotation;
  1685. //drop the particle at the emitter's position
  1686. p.position.x = emitPosX;
  1687. p.position.y = emitPosY;
  1688. };
  1689. /**
  1690. * Kills all active particles immediately.
  1691. * @method cleanup
  1692. */
  1693. p.cleanup = function () {
  1694. let particle, next;
  1695. for (particle = this._activeParticlesFirst; particle; particle = next) {
  1696. next = particle.next;
  1697. this.recycle(particle);
  1698. if (particle.parent)
  1699. particle.parent.removeChild(particle);
  1700. }
  1701. this._activeParticlesFirst = this._activeParticlesLast = null;
  1702. this.particleCount = 0;
  1703. };
  1704. /**
  1705. * Destroys the emitter and all of its particles.
  1706. * @method destroy
  1707. */
  1708. p.destroy = function () {
  1709. //puts all active particles in the pool, and removes them from the particle parent
  1710. this.cleanup();
  1711. //wipe the pool clean
  1712. let next;
  1713. for (let particle = this._poolFirst; particle; particle = next) {
  1714. //store next value so we don't lose it in our destroy call
  1715. next = particle.next;
  1716. particle.destroy();
  1717. }
  1718. this._poolFirst = this._parent = this.particleImages = this.spawnPos = this.ownerPos =
  1719. this.startColor = this.endColor = this.customEase = null;
  1720. };
  1721. PIXI.particles.Emitter = Emitter;
  1722. })(PIXI);
  1723. (function (undefined) {
  1724. // Check for window, fallback to global
  1725. let global = typeof window !== 'undefined' ? window : GLOBAL;
  1726. // Deprecate support for the cloudkid namespace
  1727. if (typeof cloudkid === 'undefined')
  1728. global.cloudkid = {};
  1729. // Get classes from the PIXI.particles namespace
  1730. Object.defineProperties(global.cloudkid, {
  1731. AnimatedParticle: {
  1732. get: function () {
  1733. if (true)
  1734. console.warn('cloudkid namespace is deprecated, please use PIXI.particles');
  1735. return PIXI.particles.AnimatedParticle;
  1736. }
  1737. },
  1738. Emitter: {
  1739. get: function () {
  1740. if (true)
  1741. console.warn('cloudkid namespace is deprecated, please use PIXI.particles');
  1742. return PIXI.particles.Emitter;
  1743. }
  1744. },
  1745. Particle: {
  1746. get: function () {
  1747. if (true)
  1748. console.warn('cloudkid namespace is deprecated, please use PIXI.particles');
  1749. return PIXI.particles.Particle;
  1750. }
  1751. },
  1752. ParticleUtils: {
  1753. get: function () {
  1754. if (true)
  1755. console.warn('cloudkid namespace is deprecated, please use PIXI.particles');
  1756. return PIXI.particles.ParticleUtils;
  1757. }
  1758. },
  1759. PathParticle: {
  1760. get: function () {
  1761. if (true)
  1762. console.warn('cloudkid namespace is deprecated, please use PIXI.particles');
  1763. return PIXI.particles.PathParticle;
  1764. }
  1765. }
  1766. });
  1767. })();