粒子系统

我们可以与粒子系统 Proton结合来实现粒子精灵。

粒子系统 - 图1

粒子系统 - 图2

  1. const scene = new Scene('#particle', {
  2. viewport: 'auto',
  3. resolution: 'flex',
  4. });
  5. scene.container.style.background = '#16a085';
  6. const layer = scene.layer('bglayer', {
  7. handleEvent: true,
  8. autoRender: false,
  9. });
  10. const fglayer = scene.layer('fglayer', {
  11. handleEvent: false,
  12. });
  13. const logo = new Sprite('https://p0.ssl.qhimg.com/t01a72262146b87165f.png');
  14. scene.on('resolutionChange', _.debounce((evt) => {
  15. logo.attr({
  16. pos: layer.center,
  17. });
  18. main();
  19. }, 300));
  20. logo.attr({
  21. anchor: 0.5,
  22. pos: layer.center,
  23. scale: 0.5,
  24. });
  25. fglayer.append(logo);
  26. const proton = new Proton();
  27. const R = 280;
  28. let pointZone,
  29. emitter;
  30. main();
  31. tick();
  32. function main() {
  33. layer.clear();
  34. layer.off(['mousedown', 'mouseup', 'mousemove']);
  35. const num = Math.min(parseInt(0.5 * layer.resolution[0] / (1000 / 145), 10), 300);
  36. createProton(proton, num);
  37. setTimeout(addMouseEvent, 200);
  38. addRenderer();
  39. }
  40. function createProton(proton, num) {
  41. proton.destroy();
  42. emitter = new Proton.Emitter();
  43. emitter.rate = new Proton.Rate(new Proton.Span(num), new Proton.Span(0.1, 0.4));
  44. emitter.addInitialize(new Proton.Mass(1));
  45. emitter.addInitialize(new Proton.Radius(1, 5));
  46. emitter.addInitialize(new Proton.Life(Infinity));
  47. const [width, height] = layer.resolution;
  48. pointZone = new Proton.Position(new Proton.RectZone(0, 0, width, height));
  49. emitter.addInitialize(pointZone);
  50. emitter.addInitialize(new Proton.Velocity(new Proton.Span(1.2, 2.0), new Proton.Span(0, 360), 'polar'));
  51. emitter.addBehaviour(new Proton.Alpha(Proton.getSpan(0.2, 0.9)));
  52. emitter.addBehaviour(new Proton.Color('#ffffff'));
  53. emitter.addBehaviour(new Proton.CrossZone(new Proton.RectZone(0, 0, width, height), 'cross'));
  54. emitter.emit('once');
  55. emitter.damping = 0;
  56. proton.addEmitter(emitter);
  57. return proton;
  58. }
  59. function addRenderer() {
  60. const renderer = new ProtonRenderer(layer);
  61. renderer.onProtonUpdateAfter = function () {
  62. const context = layer.context;
  63. context.save();
  64. const particles = this.parent.emitters[0].particles;
  65. for(let i = 0; i < particles.length; i++) {
  66. for(let j = i + 1; j < particles.length; j++) {
  67. const pA = particles[i];
  68. const pB = particles[j];
  69. const dis = pA.p.distanceTo(pB.p);
  70. if(dis < R) {
  71. const alpha = (1 - dis / R) * 0.5;
  72. context.strokeStyle = `rgba(255,255,255,${alpha})`;
  73. context.beginPath();
  74. context.lineWidth = 2;
  75. context.moveTo(pA.p.x, pA.p.y);
  76. context.lineTo(pB.p.x, pB.p.y);
  77. context.closePath();
  78. context.stroke();
  79. }
  80. }
  81. }
  82. context.restore();
  83. };
  84. proton.addRenderer(renderer);
  85. }
  86. function addMouseEvent() {
  87. emitter.rate = new Proton.Rate(new Proton.Span(3), 0.5);
  88. emitter.removeInitialize(pointZone);
  89. layer.on('mousedown', (e) => {
  90. for(let i = 0; i < 3; i++) {
  91. emitter.particles[i].dead = true;
  92. }
  93. setTimeout(() => {
  94. emitter.p.x = e.x;
  95. emitter.p.y = e.y;
  96. emitter.emit('once');
  97. }, 60);
  98. });
  99. layer.on('mouseup', (e) => {
  100. emitter.stop();
  101. });
  102. layer.on('mousemove', (e) => {
  103. const p0 = emitter.particles[0];
  104. p0.radius = 0;
  105. const ease = 0.3;
  106. p0.p.x += (e.x - p0.p.x) * ease;
  107. p0.p.y += (e.y - p0.p.y) * ease;
  108. });
  109. }
  110. function tick() {
  111. requestAnimationFrame(tick);
  112. proton.update();
  113. }