tsparticles / vue3

Vue.js tsParticles official component
MIT License
125 stars 8 forks source link

Feature request: allow hoisting tsparticles-engine's `init()` function #30

Closed Joepocalyptic closed 1 year ago

Joepocalyptic commented 1 year ago

Hello! I have a use case where I'm attempting to load multiple tsParticles instances on the same page with Vue, some of which being conditionally rendered. Unfortunately, when a second <Particles> component is rendered on the page, it triggers tsParticles#init() despite the engine already being initialized by a previous component, which forcefully refreshes any existing instances. As such, I had to write my own implementation which hoisted tsParticles#init() to code which ran on initial load and rewrite the component to skip this step.

It would be helpful if there was a feature (such as a boolean prop) to disable the tsParticles#init() line so that this library natively supports this use case. However, wouldn't it make more sense in a future version for this to be reversed such that there's a prop which executes tsParticles#init() for those only using one instance? Components are expected to be completely isolated from their outer scopes and reusable, and polluting global page state on mount doesn't seem like a sensible default.

On another note, I have some unrelated suggestions:

I'd be willing to implement these potential changes for use with a next major version so as to not break backwards compatibility, if that's alright with you.

matteobruni commented 1 year ago

This is the init function you are talking about

https://github.com/matteobruni/tsparticles/blob/5f69847a830f2f1909c74e51be85cb3dd710e416/engine/src/Core/Engine.ts#L531-L537

It literally does nothing.

I don't remember where multiple instances of the Particles component were already discussed, but the particlesInit function of the component must be set only in one of them, otherwise the full engine will be reloaded.

Example:

<Particles id="tsparticles1" :particlesInit="particlesInit" :options="options1" />
<Particles id="tsparticles2" :options="options2" />

The single particlesInit function must initialize everything needed by both instances.

Anyway, I never recommend two instances since they are heavier than a single one with an emitter that spawns particles at needs.

You can find samples like there on CodePen, like this

Joepocalyptic commented 1 year ago

My apologies for the misunderstanding of the init function; it looks like it's really the loadSlim or similar function that does all the work.

You mentioned that it's possible to use one instance with multiple emitters, but I'm not sure if this is possible when you have two separate containers which each need their own emitter configuration (i.e. two separate divs on separate parts of the page).

At its core, I feel like this is a documentation issue. The way tsParticles is built seems to require that the engine is only loaded once, but this can create some edge cases when you don't know for sure which component will be loaded first and both are capable of being present in the DOM at once. I feel as though this could be more explicitly represented in this repo's README or similar.

This admittedly might not ring true for everyone, but at first glance to me, the particlesInit prop seems more like an event where you load that specific component as opposed to being a convenience function which receives the tsParticles instance. In addition to potentially clearer documentation about particlesInit being optional, I believe it would help if the particlesLoaded event used Vue's @event syntax for clearer differentiation between this function and the particlesLoaded event (like the Svelte module does already).

Thank you for your help!

matteobruni commented 1 year ago

Yes, with separate containers it's not possible to use a single instance with emitters. You can still create a single particlesInit function checking if it's already been called, passing that function to both components. My recommendation is to never have two loaded instances in the same page, I'm not documenting that since it's not a best practice. particlesInit were an event but that was a mistake since that function must be called before the tsParticles.load function, otherwise plugins will not be loaded and every plugin loaded after the tsParticles.load function will refresh the container, definitely not the best scenario. The particlesInit function will probably disappear in the next major, for a function called once per application. It's something I'm still thinking to do. particlesLoaded can be an event, if you want to submit a PR for that, you are welcome to submit it.

Joepocalyptic commented 1 year ago

Sent the PR. Thanks again!