From 97426807e97e6dcf2525cd3106d0a958f3ac54b0 Mon Sep 17 00:00:00 2001 From: Matteo Bruni <176620+matteobruni@users.noreply.github.com> Date: Sun, 24 May 2026 02:43:59 +0200 Subject: [PATCH 1/2] fix: fixed behavior when cannon has maxDistance 0 docs: improved some comments and updated confetti website --- demo/stencil/www/index.html | 12 +---- engine/src/Core/Engine.ts | 12 +++-- interactions/external/cannon/src/Cannoner.ts | 51 ++++++++++++++----- .../cannon/src/Options/Classes/Cannon.ts | 2 +- .../external/cannon/src/index.lazy.ts | 3 +- interactions/external/cannon/src/index.ts | 2 +- pnpm-lock.yaml | 8 +-- websites/confetti/index.html | 25 --------- websites/confetti/src/cookie-consent.js | 50 +++++++++++------- websites/confetti/src/main.js | 15 +----- 10 files changed, 87 insertions(+), 93 deletions(-) diff --git a/demo/stencil/www/index.html b/demo/stencil/www/index.html index 1de24dee55b..8b4bfc6632b 100644 --- a/demo/stencil/www/index.html +++ b/demo/stencil/www/index.html @@ -1,10 +1,2 @@ - tsParticles Stencil Demo \ No newline at end of file + tsParticles Stencil Demo \ No newline at end of file diff --git a/engine/src/Core/Engine.ts b/engine/src/Core/Engine.ts index 680122ca8e1..56a36580f25 100644 --- a/engine/src/Core/Engine.ts +++ b/engine/src/Core/Engine.ts @@ -148,12 +148,18 @@ export class Engine { */ private _initialized = false; - /** The container instances */ + /** + * The container instances + * @returns the instances loaded in the engine + */ get items(): Container[] { return this._domArray; } - /** The engine version */ + /** + * The engine version + * @returns the version string + */ get version(): string { return __VERSION__; } @@ -172,7 +178,7 @@ export class Engine { */ /** * Checks if a plugin version matches the engine version - * @param pluginVersion + * @param pluginVersion - the version to check */ checkVersion(pluginVersion: string): void { if (this.version === pluginVersion) { diff --git a/interactions/external/cannon/src/Cannoner.ts b/interactions/external/cannon/src/Cannoner.ts index 3ed7518c2f7..3142a939996 100644 --- a/interactions/external/cannon/src/Cannoner.ts +++ b/interactions/external/cannon/src/Cannoner.ts @@ -95,16 +95,16 @@ interface CannonGesture { * The number of particles and their velocity scale with the drag length. * * Options live under `interactivity.modes.cannon`: - * - `spread` — half-angle spread in degrees around the launch angle (default 30) + * - `spread` — half-angle spread in degrees around the launch angle (default 30) * - `velocityFactor` — multiplier applied to drag length to obtain particle speed (default 10) * - `particleFactor` — how many particles per pixel of drag (default 0.2) - * - `minParticles` — minimum burst size regardless of drag length (default 5) - * - `maxParticles` — cap for burst size (default 200) - * - `drawVector` — whether to render the aiming line while dragging (default true) - * - `vectorColor` — CSS color for the aiming line (default "#ffffff80") + * - `minParticles` — minimum burst size regardless of drag length (default 5) + * - `maxParticles` — cap for burst size (default 200) + * - `drawVector` — whether to render the aiming line while dragging (default true) + * - `vectorColor` — CSS color for the aiming line (default "#ffffff80") */ export class Cannoner extends ExternalInteractorBase { - /** @inheritDoc */ + /** {@inheritDoc ExternalInteractorBase.maxDistance} */ readonly maxDistance = 0; private _data?: CannonData; @@ -117,18 +117,25 @@ export class Cannoner extends ExternalInteractorBase { private _lastDownPosition: ICoordinates | undefined = undefined; private _state: CannonState = CannonState.idle; - /** @inheritDoc */ + /** + * {@inheritDoc ExternalInteractorBase} + * @param container - + */ // eslint-disable-next-line @typescript-eslint/no-useless-constructor constructor(container: CannonContainer) { super(container); } - /** @inheritDoc */ + /** + * {@inheritDoc ExternalInteractorBase.clear} + * @param _particle - + * @param _delta - + */ clear(_particle: InteractivityParticle, _delta: IDelta): void { // nothing to clear per-particle } - /** @inheritDoc */ + /** {@inheritDoc ExternalInteractorBase.init} */ init(): void { const options = this.container.actualOptions.interactivity?.modes.cannon ?? new Cannon(); @@ -144,7 +151,11 @@ export class Cannoner extends ExternalInteractorBase { }; } - /** @inheritDoc */ + /** + * {@inheritDoc ExternalInteractorBase.interact} + * @param interactivityData - + * @param _delta - + */ interact(interactivityData: IInteractivityData, _delta: IDelta): void { const mouse = interactivityData.mouse, mousePos = mouse.position, @@ -176,7 +187,11 @@ export class Cannoner extends ExternalInteractorBase { } } - /** @inheritDoc */ + /** + * {@inheritDoc ExternalInteractorBase.isEnabled} + * @param interactivityData - + * @returns - + */ isEnabled(interactivityData: IInteractivityData): boolean { const { container } = this, events = container.actualOptions.interactivity?.events; @@ -197,7 +212,11 @@ export class Cannoner extends ExternalInteractorBase { return this._state !== CannonState.idle || interactivityData.mouse.clicking; } - /** @inheritDoc */ + /** + * {@inheritDoc ExternalInteractorBase.loadModeOptions} + * @param options - + * @param sources - + */ loadModeOptions( options: Modes & CannonMode, ...sources: RecursivePartial<(IModes & ICannonMode) | undefined>[] @@ -209,7 +228,11 @@ export class Cannoner extends ExternalInteractorBase { } } - /** @inheritDoc */ + /** + * {@inheritDoc ExternalInteractorBase.reset} + * @param _interactivityData - + * @param _particle - + */ reset(_interactivityData: IInteractivityData, _particle: InteractivityParticle): void { // nothing to reset } @@ -231,7 +254,7 @@ export class Cannoner extends ExternalInteractorBase { pxRatio = this.container.retina.pixelRatio, dragDist = getDistance(origin, current), // Clamp to maxDragDistance so visual feedback matches actual force - clampedDist = opts.maxDragDistance > none ? Math.min(dragDist, opts.maxDragDistance * pxRatio) : pxRatio, + clampedDist = opts.maxDragDistance > none ? Math.min(dragDist, opts.maxDragDistance * pxRatio) : dragDist, clampRatio = dragDist > minDistance ? clampedDist / dragDist : minDistance, clampedX = origin.x + (current.x - origin.x) * clampRatio, clampedY = origin.y + (current.y - origin.y) * clampRatio; diff --git a/interactions/external/cannon/src/Options/Classes/Cannon.ts b/interactions/external/cannon/src/Options/Classes/Cannon.ts index c459723f615..a0b695acd7c 100644 --- a/interactions/external/cannon/src/Options/Classes/Cannon.ts +++ b/interactions/external/cannon/src/Options/Classes/Cannon.ts @@ -31,7 +31,7 @@ export class Cannon implements ICannon { this.spread = 30; this.velocityFactor = 0.5; this.particleFactor = 0.2; - this.maxDragDistance = 200; + this.maxDragDistance = 0; this.minParticles = 5; this.maxParticles = 200; this.drawVector = true; diff --git a/interactions/external/cannon/src/index.lazy.ts b/interactions/external/cannon/src/index.lazy.ts index 97b4a719175..7b1a0d1203d 100644 --- a/interactions/external/cannon/src/index.lazy.ts +++ b/interactions/external/cannon/src/index.lazy.ts @@ -1,8 +1,7 @@ +export type { CannonContainer, CannonMode, ICannonMode } from "./Types.js"; import { type Engine } from "@tsparticles/engine/lazy"; import type { InteractivityEngine } from "@tsparticles/plugin-interactivity/lazy"; - export type { ICannon } from "./Options/Interfaces/ICannon.js"; -export type { CannonContainer, CannonMode, ICannonMode } from "./Types.js"; export { Cannon } from "./Options/Classes/Cannon.js"; declare const __VERSION__: string; diff --git a/interactions/external/cannon/src/index.ts b/interactions/external/cannon/src/index.ts index ff686450def..1890127bf89 100644 --- a/interactions/external/cannon/src/index.ts +++ b/interactions/external/cannon/src/index.ts @@ -1,9 +1,9 @@ +export type { CannonContainer, CannonMode, ICannonMode } from "./Types.js"; import { type InteractivityEngine, ensureInteractivityPluginLoaded } from "@tsparticles/plugin-interactivity"; export { Cannon } from "./Options/Classes/Cannon.js"; import { Cannoner } from "./Cannoner.js"; import { type Engine } from "@tsparticles/engine"; export type { ICannon } from "./Options/Interfaces/ICannon.js"; -export type { CannonContainer, CannonMode, ICannonMode } from "./Types.js"; declare const __VERSION__: string; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1c3989bbcf3..62a35f33091 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13268,7 +13268,7 @@ importers: version: 10.2.0(jiti@2.7.0) eslint-plugin-qwik: specifier: latest - version: 1.19.2(eslint@10.2.0(jiti@2.7.0))(typescript@6.0.2) + version: 1.20.0(eslint@10.2.0(jiti@2.7.0))(typescript@6.0.2) np: specifier: 11.0.3 version: 11.0.3(@types/node@25.6.0)(typescript@6.0.2) @@ -29558,8 +29558,8 @@ packages: peerDependencies: eslint: '>=8.38.0' - eslint-plugin-qwik@1.19.2: - resolution: {integrity: sha512-P0Xc/jzRmL9rPR1r3dk2zET1yqgnwLwAgMjTUNYIenPXhqgDWbW7bOapwDYhjDrRpVdnLv6jnomIvFGt47ytTQ==} + eslint-plugin-qwik@1.20.0: + resolution: {integrity: sha512-6XY7cGK5BDvtjHnIRcDp50dXKlr9+jnQCudxl9VrwRaJPzgKUNpVVSbo5tflTZkCJLj6/svulE5MVSn6zTsHYw==} engines: {node: '>=16.8.0 <18.0.0 || >=18.11'} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 @@ -65471,7 +65471,7 @@ snapshots: eslint: 10.2.1(jiti@2.7.0) requireindex: 1.2.0 - eslint-plugin-qwik@1.19.2(eslint@10.2.0(jiti@2.7.0))(typescript@6.0.2): + eslint-plugin-qwik@1.20.0(eslint@10.2.0(jiti@2.7.0))(typescript@6.0.2): dependencies: '@typescript-eslint/utils': 8.58.1(eslint@10.2.0(jiti@2.7.0))(typescript@6.0.2) eslint: 10.2.0(jiti@2.7.0) diff --git a/websites/confetti/index.html b/websites/confetti/index.html index 50db685507f..6441dd0bbce 100644 --- a/websites/confetti/index.html +++ b/websites/confetti/index.html @@ -20,31 +20,6 @@ - - - - - diff --git a/websites/confetti/src/cookie-consent.js b/websites/confetti/src/cookie-consent.js index 0a24e35e6e9..44f182a47ff 100644 --- a/websites/confetti/src/cookie-consent.js +++ b/websites/confetti/src/cookie-consent.js @@ -69,32 +69,44 @@ function initAnalytics() { return; } - loadScript( - 'google-analytics', - `https://www.googletagmanager.com/gtag/js?id=${GA_MEASUREMENT_ID}` - ); - window.dataLayer = window.dataLayer || []; window.gtag = function () { window.dataLayer.push(arguments); }; - const analyticsGranted = consent?.analytics; - const adsGranted = consent?.adsense; + const analyticsGranted = !!consent?.analytics; + const adsGranted = !!consent?.adsense; - // Consent Mode v2 default + // IMPORTANTISSIMO: + // default consent BEFORE loading GA window.gtag('consent', 'default', { ad_storage: adsGranted ? 'granted' : 'denied', analytics_storage: analyticsGranted ? 'granted' : 'denied', ad_user_data: adsGranted ? 'granted' : 'denied', ad_personalization: adsGranted ? 'granted' : 'denied', + + // cookieless improvements + wait_for_update: 500, }); + loadScript( + 'google-analytics', + `https://www.googletagmanager.com/gtag/js?id=${GA_MEASUREMENT_ID}` + ); + window.gtag('js', new Date()); window.gtag('config', GA_MEASUREMENT_ID, { send_page_view: true, + + // cookieless mode + client_storage: analyticsGranted ? 'cookie' : 'none', + + // aiuta attribution cookieless + url_passthrough: true, + + anonymize_ip: true, }); analyticsInitialized = true; @@ -107,10 +119,6 @@ function initAdSense() { window.adsbygoogle = window.adsbygoogle || []; - // NPA mode - window.adsbygoogle.requestNonPersonalizedAds = - consent?.adsense || !ADSENSE_NON_PERSONALIZED_ON_REJECT ? 0 : 1; - loadScript( 'adsense-script', `https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=${ADSENSE_CLIENT_ID}`, @@ -127,24 +135,28 @@ function updateConsentMode(activeConsent) { return; } + const analyticsGranted = !!activeConsent.analytics; + window.gtag('consent', 'update', { ad_storage: activeConsent.adsense ? 'granted' : 'denied', - analytics_storage: activeConsent.analytics ? 'granted' : 'denied', + analytics_storage: analyticsGranted ? 'granted' : 'denied', ad_user_data: activeConsent.adsense ? 'granted' : 'denied', ad_personalization: activeConsent.adsense ? 'granted' : 'denied', }); + + // switch runtime storage mode + window.gtag('set', { + client_storage: analyticsGranted ? 'cookie' : 'none', + }); } function applyConsent(activeConsent) { - if (activeConsent.analytics || ANALYTICS_COOKIELESS_ON_REJECT) { - initAnalytics(); - } + // analytics ALWAYS initialized + initAnalytics(); updateConsentMode(activeConsent); - if (activeConsent.adsense || ADSENSE_NON_PERSONALIZED_ON_REJECT) { - initAdSense(); - } + initAdSense(); } function closeBanner() { diff --git a/websites/confetti/src/main.js b/websites/confetti/src/main.js index dd205d3f4bc..e1b450ae3ed 100644 --- a/websites/confetti/src/main.js +++ b/websites/confetti/src/main.js @@ -61,20 +61,7 @@ const updateShareLinks = function () { }; const canTrackAnalytics = function () { - const consentApi = window.tsParticlesConfettiConsent; - - // fallback nel caso il consent script non sia ancora pronto - if (!consentApi) { - return true; - } - - const preferences = consentApi.get?.(); - - if (!preferences) { - return !!consentApi.allowsCookielessAnalytics; - } - - return !!preferences.analytics || !!consentApi.allowsCookielessAnalytics; + return !!window.gtag; }; const trackShare = function (platform) { From ff41736c5495c3fad004e707e1ec5df7cb1b9a80 Mon Sep 17 00:00:00 2001 From: Matteo Bruni <176620+matteobruni@users.noreply.github.com> Date: Sun, 24 May 2026 02:53:58 +0200 Subject: [PATCH 2/2] fix: fixed cannon options --- interactions/external/cannon/src/Options/Classes/Cannon.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/interactions/external/cannon/src/Options/Classes/Cannon.ts b/interactions/external/cannon/src/Options/Classes/Cannon.ts index a0b695acd7c..57a4b090ec6 100644 --- a/interactions/external/cannon/src/Options/Classes/Cannon.ts +++ b/interactions/external/cannon/src/Options/Classes/Cannon.ts @@ -64,6 +64,10 @@ export class Cannon implements ICannon { this.maxParticles = data.maxParticles; } + if (data.maxDragDistance !== undefined) { + this.maxDragDistance = data.maxDragDistance; + } + if (data.drawVector !== undefined) { this.drawVector = data.drawVector; }