diff --git a/src/features/particles/Particles.vue b/src/features/particles/Particles.vue index 57feb7e..afe40be 100644 --- a/src/features/particles/Particles.vue +++ b/src/features/particles/Particles.vue @@ -1,11 +1,19 @@ +onMounted, + + diff --git a/src/features/particles/particles.tsx b/src/features/particles/particles.tsx index 010bb36..4f460a9 100644 --- a/src/features/particles/particles.tsx +++ b/src/features/particles/particles.tsx @@ -1,39 +1,101 @@ -import ParticlesComponent from "./Particles.vue"; +import ParticlesComponent from "features/particles/Particles.vue"; +import { Container } from "tsparticles-engine"; import { IEmitter } from "tsparticles-plugin-emitters/Options/Interfaces/IEmitter"; import { EmitterInstance } from "tsparticles-plugin-emitters/EmitterInstance"; import { EmitterContainer } from "tsparticles-plugin-emitters/EmitterContainer"; import { Ref, shallowRef } from "vue"; -import { registerGameComponent } from "game/settings"; -import { jsx } from "features/feature"; +import { Component, GatherProps, getUniqueID, Replace, setDefault } from "features/feature"; +import { createLazyProxy } from "util/proxies"; -registerGameComponent(jsx(() => )); +export const ParticlesType = Symbol("Particles"); -const containerRef: Ref = shallowRef(null); +export interface ParticlesOptions { + fullscreen?: boolean; + zIndex?: number; + onContainerResized?: (boundingRect: DOMRect) => void; +} -let emittersToAdd: { - resolve: (value: EmitterInstance | PromiseLike) => void; - options: IEmitter & { particles: Required["particles"] }; -}[] = []; +export interface BaseParticles { + id: string; + containerRef: Ref; + addEmitter: ( + options: IEmitter & { particles: Required["particles"] } + ) => Promise; + removeEmitter: (emitter: EmitterInstance) => void; + type: typeof ParticlesType; + [Component]: typeof ParticlesComponent; + [GatherProps]: () => Record; +} -export function addEmitter( - options: IEmitter & { particles: Required["particles"] } -): Promise { - if (containerRef.value) { - // TODO why does addEmitter require a position parameter - return Promise.resolve(containerRef.value.addEmitter(options)); +export type Particles = Replace< + T & BaseParticles, + { + fullscreen: undefined extends T["fullscreen"] ? true : T["fullscreen"]; + zIndex: undefined extends T["zIndex"] ? 1 : T["zIndex"]; } - return new Promise(resolve => { - emittersToAdd.push({ resolve, options }); +>; + +export type GenericParticles = Replace< + Particles, + { + fullscreen: boolean; + zIndex: number; + } +>; + +export function createParticles( + optionsFunc: () => T & ThisType> +): Particles { + return createLazyProxy(() => { + const particles: T & Partial = optionsFunc(); + particles.id = getUniqueID("particles-"); + particles.type = ParticlesType; + particles[Component] = ParticlesComponent; + + particles.containerRef = shallowRef(null); + particles.addEmitter = ( + options: IEmitter & { particles: Required["particles"] } + ): Promise => { + const genericParticles = particles as GenericParticles; + if (genericParticles.containerRef.value) { + // TODO why does addEmitter require a position parameter + return Promise.resolve(genericParticles.containerRef.value.addEmitter(options)); + } + return new Promise(resolve => { + emittersToAdd.push({ resolve, options }); + }); + }; + particles.removeEmitter = (emitter: EmitterInstance) => { + // TODO I can't find a proper way to remove an emitter without accessing private functions + emitter.emitters.removeEmitter(emitter); + }; + + let emittersToAdd: { + resolve: (value: EmitterInstance | PromiseLike) => void; + options: IEmitter & { particles: Required["particles"] }; + }[] = []; + + function onInit(container: EmitterContainer & Container) { + (particles as GenericParticles).containerRef.value = container; + emittersToAdd.forEach(({ resolve, options }) => resolve(container.addEmitter(options))); + emittersToAdd = []; + } + + setDefault(particles, "fullscreen", true); + setDefault(particles, "zIndex", 1); + particles.onContainerResized = particles.onContainerResized?.bind(particles); + + particles[GatherProps] = function (this: GenericParticles) { + const { id, fullscreen, zIndex, onContainerResized } = this; + return { + id, + fullscreen, + zIndex, + onContainerResized, + onInit + }; + }; + + return particles as unknown as Particles; }); } - -export function removeEmitter(emitter: EmitterInstance) { - // TODO I can't find a proper way to remove an emitter without accessing private functions - emitter.emitters.removeEmitter(emitter); -} - -function onInit(container: EmitterContainer) { - containerRef.value = container; - emittersToAdd.forEach(({ resolve, options }) => resolve(container.addEmitter(options))); - emittersToAdd = []; -}