Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug]: ng-tsparticles load engine with [particlesInit] gets called everytime #4151

Closed
1 task done
TheDelta opened this issue May 29, 2022 · 2 comments · Fixed by #4031
Closed
1 task done

[Bug]: ng-tsparticles load engine with [particlesInit] gets called everytime #4151

TheDelta opened this issue May 29, 2022 · 2 comments · Fixed by #4031
Assignees
Labels
bug Something isn't working triage

Comments

@TheDelta
Copy link

Contact Details

a.diller@dolphbit.de

What happened?

Just started today with this awesome library and after some struggling and digging through the documentation I eventually found an issue or I misunderstood something.

The following use case: I have two <ng-particles [particlesInit]="init"> components placed on a page.

The init function is the following and does partial loading of what I need.

  async init(engine: Engine): Promise<void> {
    if (
      engine.domArray.some((dom) => dom.id === (this as any).id)
    ) {
      // FIXME bugfix!
      return;
    }

    // see full: https://github.com/matteobruni/tsparticles/blob/main/bundles/full/src/index.ts
    // see slim: https://github.com/matteobruni/tsparticles/blob/main/bundles/slim/src/index.ts
    await loadBaseMover(engine);
    await loadParallaxMover(engine);
    await loadParticlesCollisionsInteraction(engine);
    await loadTextShape(engine);
    await loadLifeUpdater(engine);
    await loadOpacityUpdater(engine);
    await loadSizeUpdater(engine);
    await loadAngleUpdater(engine);
    await loadColorUpdater(engine);
    await loadOutModesUpdater(engine);
    await loadEmittersPlugin(engine);
  }

So this functions gets called two times and it works.
But one <ng-particles [particlesInit]="init" *ngIf="condition"> has a ngIf which is used to toggle visibility (not always needed).

Whenever I toggle it, the init function gets executed.
This will fill the engine.plugins.movers map for example with many entries of the same id.
My bugfix to check if the domArray has already the id will fix this issue.

I just noticed it by accident, because whenever I toggle and loadABC(engine) the other ng-particles does emit for some and places an particle on the canvas. (the particle count is 0 when I check it, but I would need to call container.canvas.clear(); to remove it) After further investigation it seems to be in relation with the multiple loading.

Am I misunderstanding something or is this a bug?
If the issue is still not clear, let me know. I try to create an example project.

I know documentation is probably the last thing, but it took me while to figure out everything I want. Docs are a little bit messy.
Eventually I find some time to contribute where I can. ❤️

tsParticles Version

2.0.6

Which library are you using?

Angular (ng-particles)

tsParticles Configuration

export const conficParticlesDefault: IParticlesProps = {
  fpsLimit: 60,
  detectRetina: true,
  pauseOnBlur: true, // Pauses the animations when the page isn't on foreground
  pauseOnOutsideViewport: true, // Pauses the animations when the element is out of the viewport
  motion: {
    disable: true, // If set to true, disables animations for users with prefer-reduced-motion enabled
  },
};

export const configCookiesBackground = Object.assign(
  {},
  conficParticlesDefault,
  {
    interactivity: {},
    particles: {
      opacity: {
        value: 0.6,
      },
      move: {
        direction: MoveDirection.outside,
        enable: true,
        outModes: {
          default: OutMode.bounce,
        },
        random: true,
        speed: {
          min: 2,
          max: 4,
        },
        straight: false,
      },
      number: {
        limit: 10,
        value: 10,
      },
      rotate: {
        direction: RotateDirection.random,
        random: true,
        animation: {
          enable: true,
          speed: 5,
        },
      },
      collisions: {
        enable: true,
        mode: CollisionMode.bounce,
        overlap: {
          enable: true, // prevent placing issue and warning spam
        },
      },
      shape: {
        type: 'character',
        character: {
          value: '🍪',
        },
      },
      size: {
        value: { min: 20, max: 50 },
      },
    },
    responsive: [
      {
        maxWidth: 520,
        mode: ResponsiveMode.canvas,
        options: {
          autoPlay: false,
          particles: {
            life: {
              count: 0,
              duration: 0,
            },
          },
        },
      },
      {
        maxWidth: 1000,
        mode: ResponsiveMode.canvas,
        options: {
          particles: {
            number: {
              limit: 5,
              value: 5,
            },
          },
        },
      },
    ],
  } as IParticlesProps & RecursivePartial<IEmitterOptions>
);

export const configCookiesCrumble = Object.assign({}, conficParticlesDefault, {
  autoPlay: false,
  motion: {
    disable: false, // override, because we want to show it
    reduce: {
      value: true,
    },
  },
  particles: {
    number: {
      value: 0,
    },
    collisions: {
      enable: false,
    },
  },
  duration: 5000,
  emitters: {
    rate: {
      delay: {
        max: 0.0001,
        min: 0.0001,
      },
      quantity: {
        max: 1,
        min: 1,
      },
    },
    life: {
      count: 2,
      duration: 0.1,
    },
    direction: MoveDirection.top,
    domId: 'cookie-spawn',
    fill: false,
    particles: {
      collisions: {
        enable: true,
        mode: CollisionMode.bounce,
        overlap: {
          enable: true, // prevent placing issue and warning spam
        },
      },
      number: {
        value: 0,
      },
      life: {
        duration: {
          value: 5,
          sync: true,
        },
      },
      move: {
        collisions: true,
        bounce: true,
        direction: MoveDirection.bottom,
        enable: true,
        outModes: {
          default: OutMode.bounce,
        },
        random: true,
        speed: 1,
        straight: false,
        gravity: {
          enable: true,
          acceleration: 10,
        },
      },
      shape: {
        type: 'character',
        character: {
          value: '🍪',
        },
      },
      size: {
        value: 15,
      },
    },
  },
} as IParticlesProps & RecursivePartial<IEmitterOptions>);

What browsers are you seeing the problem on?

Chrome

Relevant log output

No response

Code of Conduct

  • I agree to follow this project's Code of Conduct
@TheDelta TheDelta added bug Something isn't working triage labels May 29, 2022
@matteobruni
Copy link
Collaborator

It's definitely a bug, thanks for reporting that

@TheDelta
Copy link
Author

TheDelta commented May 29, 2022

Yeah probably, I went a bit through the source code and also created as promised a tiny example:

https://stackblitz.com/edit/ng-particles-load-engine-bug?file=src/app/app.component.ts

If you click the visible button, you notice that there appears a dolphin (but it shouldn't) and it also freezes / stutters a bit (I assume due to loading the engine multiple times)

If you enable the flag PREVENT_ENGINE_LOAD_BUG it won't happen and only load the engine once.
My fix in the bug report is therefore also garbage - validating the id if it exists already is wrong. It must be only loaded once as far as I understood. Otherwise the bug appears.

The issue with movers filling is another bug. It must be related in the way the container is created and handled

In Plugin.ts the map is initialized at this.movers = new Map<Container, IParticleMover[]>(); (https://github.com/matteobruni/tsparticles/blob/main/engine/src/Core/Utils/Plugins.ts#L92)

I assume the container reference changes, etc. because after debugging it, it will always set a new entry in the map.

https://github.com/matteobruni/tsparticles/blob/main/engine/src/Core/Utils/Plugins.ts#L92

    getMovers(container: Container, force = false): IParticleMover[] {
        let res = this.movers.get(container);
        if (!res || force) {
            res = [...this.moversInitializers.values()].map((t) => t(container));
            this.movers.set(container, res); // <<<---- container is always a different reference and will constantly set because it won't be found or through force flag
        }

        return res;
    }

Easy solution would be to use a key string (the id), idk if that would break the backwards compatibility though ;)

matteobruni added a commit that referenced this issue Jun 17, 2022
@matteobruni matteobruni linked a pull request Jun 17, 2022 that will close this issue
matteobruni added a commit that referenced this issue Jun 17, 2022
fix: tested fix for issue #4151 and it works, closes #4151
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Sep 20, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug Something isn't working triage
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants