You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

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