@tsparticles/engine 4.0.0-beta.16 → 4.0.0-beta.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/browser/Core/CanvasManager.js +73 -36
- package/browser/Core/Engine.js +11 -5
- package/browser/Core/Particle.js +1 -3
- package/browser/Core/ParticlesManager.js +157 -78
- package/browser/Core/Retina.js +2 -3
- package/browser/Core/Utils/EventListeners.js +1 -1
- package/cjs/Core/CanvasManager.js +73 -36
- package/cjs/Core/Engine.js +11 -5
- package/cjs/Core/Particle.js +1 -3
- package/cjs/Core/ParticlesManager.js +157 -78
- package/cjs/Core/Retina.js +2 -3
- package/cjs/Core/Utils/EventListeners.js +1 -1
- package/esm/Core/CanvasManager.js +73 -36
- package/esm/Core/Engine.js +11 -5
- package/esm/Core/Particle.js +1 -3
- package/esm/Core/ParticlesManager.js +157 -78
- package/esm/Core/Retina.js +2 -3
- package/esm/Core/Utils/EventListeners.js +1 -1
- package/package.json +1 -1
- package/report.html +1 -1
- package/scripts/install.js +321 -220
- package/tsparticles.engine.js +245 -126
- package/tsparticles.engine.min.js +1 -1
- package/types/Core/CanvasManager.d.ts +3 -2
- package/types/Core/Container.d.ts +1 -2
- package/types/Core/Interfaces/IContainerPlugin.d.ts +7 -8
- package/types/Core/Interfaces/IDrawParticleParams.d.ts +1 -2
- package/types/Core/Interfaces/ILoadParams.d.ts +1 -1
- package/types/Core/Interfaces/IParticleUpdater.d.ts +1 -2
- package/types/Core/Interfaces/IShapeDrawData.d.ts +1 -2
- package/types/Core/ParticlesManager.d.ts +11 -5
- package/types/Core/RenderManager.d.ts +2 -3
- package/types/Core/Utils/PluginManager.d.ts +5 -6
- package/types/Options/Classes/Options.d.ts +1 -2
- package/types/Utils/CanvasUtils.d.ts +4 -5
- package/types/Utils/LogUtils.d.ts +1 -2
- package/types/Utils/Utils.d.ts +1 -2
- package/types/export-types.d.ts +0 -1
- package/browser/Types/CanvasContextType.js +0 -1
- package/cjs/Types/CanvasContextType.js +0 -1
- package/esm/Types/CanvasContextType.js +0 -1
- package/types/Types/CanvasContextType.d.ts +0 -1
|
@@ -2,6 +2,25 @@ import { cloneStyle, getFullScreenStyle, safeMatchMedia, safeMutationObserver }
|
|
|
2
2
|
import { defaultZoom, generatedAttribute, half } from "./Utils/Constants.js";
|
|
3
3
|
import { getStyleFromRgb, rangeColorToRgb } from "../Utils/ColorUtils.js";
|
|
4
4
|
import { RenderManager } from "./RenderManager.js";
|
|
5
|
+
const transferredCanvases = new WeakMap(), getTransferredCanvas = (canvas) => {
|
|
6
|
+
const transferredCanvas = transferredCanvases.get(canvas);
|
|
7
|
+
if (transferredCanvas) {
|
|
8
|
+
return transferredCanvas;
|
|
9
|
+
}
|
|
10
|
+
if (typeof canvas.transferControlToOffscreen !== "function") {
|
|
11
|
+
throw new TypeError("OffscreenCanvas is required but not supported by this browser");
|
|
12
|
+
}
|
|
13
|
+
try {
|
|
14
|
+
const offscreenCanvas = canvas.transferControlToOffscreen();
|
|
15
|
+
transferredCanvases.set(canvas, offscreenCanvas);
|
|
16
|
+
return offscreenCanvas;
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
throw new TypeError("OffscreenCanvas transfer failed");
|
|
20
|
+
}
|
|
21
|
+
}, isHtmlCanvasElement = (canvas) => {
|
|
22
|
+
return typeof HTMLCanvasElement !== "undefined" && canvas instanceof HTMLCanvasElement;
|
|
23
|
+
};
|
|
5
24
|
function setStyle(canvas, style, important = false) {
|
|
6
25
|
if (!style) {
|
|
7
26
|
return;
|
|
@@ -32,8 +51,9 @@ function setStyle(canvas, style, important = false) {
|
|
|
32
51
|
}
|
|
33
52
|
}
|
|
34
53
|
export class CanvasManager {
|
|
35
|
-
|
|
54
|
+
domElement;
|
|
36
55
|
render;
|
|
56
|
+
renderCanvas;
|
|
37
57
|
size;
|
|
38
58
|
zoom = defaultZoom;
|
|
39
59
|
_container;
|
|
@@ -68,9 +88,10 @@ export class CanvasManager {
|
|
|
68
88
|
destroy() {
|
|
69
89
|
this.stop();
|
|
70
90
|
if (this._generated) {
|
|
71
|
-
const element = this.
|
|
91
|
+
const element = this.domElement;
|
|
72
92
|
element?.remove();
|
|
73
|
-
this.
|
|
93
|
+
this.domElement = undefined;
|
|
94
|
+
this.renderCanvas = undefined;
|
|
74
95
|
}
|
|
75
96
|
else {
|
|
76
97
|
this._resetOriginalStyle();
|
|
@@ -103,16 +124,17 @@ export class CanvasManager {
|
|
|
103
124
|
this._initStyle();
|
|
104
125
|
this.initBackground();
|
|
105
126
|
this._safeMutationObserver(obs => {
|
|
106
|
-
|
|
127
|
+
const element = this.domElement;
|
|
128
|
+
if (!element || !(element instanceof Node)) {
|
|
107
129
|
return;
|
|
108
130
|
}
|
|
109
|
-
obs.observe(
|
|
131
|
+
obs.observe(element, { attributes: true });
|
|
110
132
|
});
|
|
111
133
|
this.initPlugins();
|
|
112
134
|
this.render.init();
|
|
113
135
|
}
|
|
114
136
|
initBackground() {
|
|
115
|
-
const { _container } = this, options = _container.actualOptions, background = options.background, element = this.
|
|
137
|
+
const { _container } = this, options = _container.actualOptions, background = options.background, element = this.domElement;
|
|
116
138
|
if (!element) {
|
|
117
139
|
return;
|
|
118
140
|
}
|
|
@@ -137,21 +159,30 @@ export class CanvasManager {
|
|
|
137
159
|
}
|
|
138
160
|
}
|
|
139
161
|
loadCanvas(canvas) {
|
|
140
|
-
if (this._generated && this.
|
|
141
|
-
this.
|
|
162
|
+
if (this._generated && this.domElement) {
|
|
163
|
+
this.domElement.remove();
|
|
164
|
+
}
|
|
165
|
+
const container = this._container, domCanvas = isHtmlCanvasElement(canvas) ? canvas : undefined;
|
|
166
|
+
this.domElement = domCanvas;
|
|
167
|
+
this._generated = domCanvas ? domCanvas.dataset[generatedAttribute] === "true" : false;
|
|
168
|
+
this.renderCanvas = domCanvas ? getTransferredCanvas(domCanvas) : canvas;
|
|
169
|
+
const domElement = this.domElement;
|
|
170
|
+
if (domElement) {
|
|
171
|
+
domElement.ariaHidden = "true";
|
|
172
|
+
this._originalStyle = cloneStyle(domElement.style);
|
|
173
|
+
}
|
|
174
|
+
const standardSize = this._standardSize, renderCanvas = this.renderCanvas;
|
|
175
|
+
if (domElement) {
|
|
176
|
+
standardSize.height = domElement.offsetHeight;
|
|
177
|
+
standardSize.width = domElement.offsetWidth;
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
standardSize.height = renderCanvas.height;
|
|
181
|
+
standardSize.width = renderCanvas.width;
|
|
142
182
|
}
|
|
143
|
-
const container = this._container;
|
|
144
|
-
this._generated =
|
|
145
|
-
generatedAttribute in canvas.dataset ? canvas.dataset[generatedAttribute] === "true" : this._generated;
|
|
146
|
-
this.element = canvas;
|
|
147
|
-
this.element.ariaHidden = "true";
|
|
148
|
-
this._originalStyle = cloneStyle(this.element.style);
|
|
149
|
-
const standardSize = this._standardSize;
|
|
150
|
-
standardSize.height = canvas.offsetHeight;
|
|
151
|
-
standardSize.width = canvas.offsetWidth;
|
|
152
183
|
const pxRatio = this._container.retina.pixelRatio, retinaSize = this.size;
|
|
153
|
-
|
|
154
|
-
|
|
184
|
+
renderCanvas.height = retinaSize.height = standardSize.height * pxRatio;
|
|
185
|
+
renderCanvas.width = retinaSize.width = standardSize.width * pxRatio;
|
|
155
186
|
const canSupportHdrQuery = safeMatchMedia("(color-gamut: p3)");
|
|
156
187
|
this.render.setContextSettings({
|
|
157
188
|
alpha: true,
|
|
@@ -159,42 +190,48 @@ export class CanvasManager {
|
|
|
159
190
|
desynchronized: true,
|
|
160
191
|
willReadFrequently: false,
|
|
161
192
|
});
|
|
162
|
-
this.render.setContext(
|
|
193
|
+
this.render.setContext(renderCanvas.getContext("2d", this.render.settings));
|
|
163
194
|
this._safeMutationObserver(obs => {
|
|
164
195
|
obs.disconnect();
|
|
165
196
|
});
|
|
166
197
|
container.retina.init();
|
|
167
198
|
this.initBackground();
|
|
168
199
|
this._safeMutationObserver(obs => {
|
|
169
|
-
|
|
200
|
+
const element = this.domElement;
|
|
201
|
+
if (!element || !(element instanceof Node)) {
|
|
170
202
|
return;
|
|
171
203
|
}
|
|
172
|
-
obs.observe(
|
|
204
|
+
obs.observe(element, { attributes: true });
|
|
173
205
|
});
|
|
174
206
|
}
|
|
175
207
|
resize() {
|
|
176
|
-
|
|
208
|
+
const element = this.domElement;
|
|
209
|
+
if (!element) {
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
const container = this._container, renderCanvas = this.renderCanvas;
|
|
213
|
+
if (renderCanvas === undefined) {
|
|
177
214
|
return false;
|
|
178
215
|
}
|
|
179
|
-
const
|
|
180
|
-
width:
|
|
181
|
-
height:
|
|
216
|
+
const currentSize = container.canvas._standardSize, newSize = {
|
|
217
|
+
width: element.offsetWidth,
|
|
218
|
+
height: element.offsetHeight,
|
|
182
219
|
}, pxRatio = container.retina.pixelRatio, retinaSize = {
|
|
183
220
|
width: newSize.width * pxRatio,
|
|
184
221
|
height: newSize.height * pxRatio,
|
|
185
222
|
};
|
|
186
223
|
if (newSize.height === currentSize.height &&
|
|
187
224
|
newSize.width === currentSize.width &&
|
|
188
|
-
retinaSize.height ===
|
|
189
|
-
retinaSize.width ===
|
|
225
|
+
retinaSize.height === renderCanvas.height &&
|
|
226
|
+
retinaSize.width === renderCanvas.width) {
|
|
190
227
|
return false;
|
|
191
228
|
}
|
|
192
229
|
const oldSize = { ...currentSize };
|
|
193
230
|
currentSize.height = newSize.height;
|
|
194
231
|
currentSize.width = newSize.width;
|
|
195
232
|
const canvasSize = this.size;
|
|
196
|
-
|
|
197
|
-
|
|
233
|
+
renderCanvas.width = canvasSize.width = retinaSize.width;
|
|
234
|
+
renderCanvas.height = canvasSize.height = retinaSize.height;
|
|
198
235
|
if (this._container.started) {
|
|
199
236
|
container.particles.setResizeFactor({
|
|
200
237
|
width: currentSize.width / oldSize.width,
|
|
@@ -204,7 +241,7 @@ export class CanvasManager {
|
|
|
204
241
|
return true;
|
|
205
242
|
}
|
|
206
243
|
setPointerEvents(type) {
|
|
207
|
-
const element = this.
|
|
244
|
+
const element = this.domElement;
|
|
208
245
|
if (!element) {
|
|
209
246
|
return;
|
|
210
247
|
}
|
|
@@ -223,7 +260,7 @@ export class CanvasManager {
|
|
|
223
260
|
this.render.stop();
|
|
224
261
|
}
|
|
225
262
|
async windowResize() {
|
|
226
|
-
if (!this.
|
|
263
|
+
if (!this.domElement || !this.resize()) {
|
|
227
264
|
return;
|
|
228
265
|
}
|
|
229
266
|
const container = this._container, needsRefresh = container.updateActualOptions();
|
|
@@ -239,7 +276,7 @@ export class CanvasManager {
|
|
|
239
276
|
}
|
|
240
277
|
};
|
|
241
278
|
_initStyle = () => {
|
|
242
|
-
const element = this.
|
|
279
|
+
const element = this.domElement, options = this._container.actualOptions;
|
|
243
280
|
if (!element) {
|
|
244
281
|
return;
|
|
245
282
|
}
|
|
@@ -261,7 +298,7 @@ export class CanvasManager {
|
|
|
261
298
|
}
|
|
262
299
|
};
|
|
263
300
|
_repairStyle = () => {
|
|
264
|
-
const element = this.
|
|
301
|
+
const element = this.domElement;
|
|
265
302
|
if (!element) {
|
|
266
303
|
return;
|
|
267
304
|
}
|
|
@@ -281,7 +318,7 @@ export class CanvasManager {
|
|
|
281
318
|
});
|
|
282
319
|
};
|
|
283
320
|
_resetOriginalStyle = () => {
|
|
284
|
-
const element = this.
|
|
321
|
+
const element = this.domElement, originalStyle = this._originalStyle;
|
|
285
322
|
if (!element || !originalStyle) {
|
|
286
323
|
return;
|
|
287
324
|
}
|
|
@@ -294,7 +331,7 @@ export class CanvasManager {
|
|
|
294
331
|
callback(this._mutationObserver);
|
|
295
332
|
};
|
|
296
333
|
_setFullScreenStyle = () => {
|
|
297
|
-
const element = this.
|
|
334
|
+
const element = this.domElement;
|
|
298
335
|
if (!element) {
|
|
299
336
|
return;
|
|
300
337
|
}
|
package/cjs/Core/Engine.js
CHANGED
|
@@ -32,7 +32,7 @@ const getCanvasFromContainer = (domContainer) => {
|
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
else {
|
|
35
|
-
const existingCanvases = domContainer.getElementsByTagName(canvasTag), foundCanvas = existingCanvases
|
|
35
|
+
const existingCanvases = domContainer.getElementsByTagName(canvasTag), foundCanvas = existingCanvases.item(canvasFirstIndex);
|
|
36
36
|
if (foundCanvas) {
|
|
37
37
|
canvasEl = foundCanvas;
|
|
38
38
|
canvasEl.dataset[generatedAttribute] = generatedFalse;
|
|
@@ -69,7 +69,7 @@ export class Engine {
|
|
|
69
69
|
return this._domArray;
|
|
70
70
|
}
|
|
71
71
|
get version() {
|
|
72
|
-
return "4.0.0-beta.
|
|
72
|
+
return "4.0.0-beta.17";
|
|
73
73
|
}
|
|
74
74
|
addEventListener(type, listener) {
|
|
75
75
|
this._eventDispatcher.addEventListener(type, listener);
|
|
@@ -100,7 +100,11 @@ export class Engine {
|
|
|
100
100
|
}
|
|
101
101
|
async load(params) {
|
|
102
102
|
await this.init();
|
|
103
|
-
|
|
103
|
+
let domSourceElement;
|
|
104
|
+
if (typeof HTMLElement !== "undefined" && params.element instanceof HTMLElement) {
|
|
105
|
+
domSourceElement = params.element;
|
|
106
|
+
}
|
|
107
|
+
const { Container } = await import("./Container.js"), id = params.id ?? domSourceElement?.id ?? `tsparticles${Math.floor(getRandom() * loadRandomFactor).toString()}`, { index, url } = params, options = url ? await getDataFromUrl({ fallback: params.options, url, index }) : params.options, currentOptions = itemFromSingleOrMultiple(options, index), { items } = this, oldIndex = items.findIndex(v => v.id.description === id), newItem = new Container({
|
|
104
108
|
dispatchCallback: (eventType, args) => {
|
|
105
109
|
this.dispatchEvent(eventType, args);
|
|
106
110
|
},
|
|
@@ -127,8 +131,10 @@ export class Engine {
|
|
|
127
131
|
else {
|
|
128
132
|
items.push(newItem);
|
|
129
133
|
}
|
|
130
|
-
const
|
|
131
|
-
|
|
134
|
+
const sourceCanvas = typeof OffscreenCanvas !== "undefined" && params.element instanceof OffscreenCanvas
|
|
135
|
+
? params.element
|
|
136
|
+
: getCanvasFromContainer(getDomContainer(id, domSourceElement));
|
|
137
|
+
newItem.canvas.loadCanvas(sourceCanvas);
|
|
132
138
|
await newItem.start();
|
|
133
139
|
return newItem;
|
|
134
140
|
}
|
package/cjs/Core/Particle.js
CHANGED
|
@@ -242,8 +242,6 @@ export class Particle {
|
|
|
242
242
|
this._initPosition(position);
|
|
243
243
|
this.initialVelocity = this._calculateVelocity();
|
|
244
244
|
this.velocity = this.initialVelocity.copy();
|
|
245
|
-
const particles = container.particles;
|
|
246
|
-
particles.setLastZIndex(this.position.z);
|
|
247
245
|
this.zIndexFactor = this.position.z / container.zLayers;
|
|
248
246
|
this.sides = 24;
|
|
249
247
|
let effectDrawer, shapeDrawer;
|
|
@@ -393,7 +391,7 @@ export class Particle {
|
|
|
393
391
|
return color;
|
|
394
392
|
};
|
|
395
393
|
_initPosition = position => {
|
|
396
|
-
const container = this._container, zIndexValue = getRangeValue(this.options.zIndex.value), initialPosition = this._calcPosition(position, clamp(zIndexValue, minZ, container.zLayers));
|
|
394
|
+
const container = this._container, zIndexValue = Math.floor(getRangeValue(this.options.zIndex.value)), initialPosition = this._calcPosition(position, clamp(zIndexValue, minZ, container.zLayers));
|
|
397
395
|
if (!initialPosition) {
|
|
398
396
|
throw new Error("a valid position cannot be found for particle");
|
|
399
397
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { countOffset, defaultDensityFactor, defaultRemoveQuantity, deleteCount,
|
|
1
|
+
import { countOffset, defaultDensityFactor, defaultRemoveQuantity, deleteCount, double, empty, minCount, minIndex, minLimit, one, spatialHashGridCellSize, squareExp, } from "./Utils/Constants.js";
|
|
2
2
|
import { EventType } from "../Enums/Types/EventType.js";
|
|
3
3
|
import { LimitMode } from "../Enums/Modes/LimitMode.js";
|
|
4
4
|
import { Particle } from "./Particle.js";
|
|
@@ -12,10 +12,8 @@ export class ParticlesManager {
|
|
|
12
12
|
_container;
|
|
13
13
|
_groupLimits;
|
|
14
14
|
_limit;
|
|
15
|
-
_maxZIndex;
|
|
16
|
-
_minZIndex;
|
|
17
|
-
_needsSort;
|
|
18
15
|
_nextId;
|
|
16
|
+
_particleBuckets;
|
|
19
17
|
_particleResetPlugins;
|
|
20
18
|
_particleUpdatePlugins;
|
|
21
19
|
_pluginManager;
|
|
@@ -24,19 +22,17 @@ export class ParticlesManager {
|
|
|
24
22
|
_postUpdatePlugins;
|
|
25
23
|
_resizeFactor;
|
|
26
24
|
_updatePlugins;
|
|
27
|
-
|
|
25
|
+
_zBuckets;
|
|
28
26
|
constructor(pluginManager, container) {
|
|
29
27
|
this._pluginManager = pluginManager;
|
|
30
28
|
this._container = container;
|
|
31
29
|
this._nextId = 0;
|
|
32
30
|
this._array = [];
|
|
33
|
-
this._zArray = [];
|
|
34
31
|
this._pool = [];
|
|
35
32
|
this._limit = 0;
|
|
36
33
|
this._groupLimits = new Map();
|
|
37
|
-
this.
|
|
38
|
-
this.
|
|
39
|
-
this._maxZIndex = 0;
|
|
34
|
+
this._particleBuckets = new Map();
|
|
35
|
+
this._zBuckets = this._createBuckets(this._container.zLayers);
|
|
40
36
|
this.grid = new SpatialHashGrid(spatialHashGridCellSize);
|
|
41
37
|
this.checkParticlePositionPlugins = [];
|
|
42
38
|
this._particleResetPlugins = [];
|
|
@@ -80,7 +76,7 @@ export class ParticlesManager {
|
|
|
80
76
|
return;
|
|
81
77
|
}
|
|
82
78
|
this._array.push(particle);
|
|
83
|
-
this.
|
|
79
|
+
this._insertParticleIntoBucket(particle);
|
|
84
80
|
this._nextId++;
|
|
85
81
|
this._container.dispatchEvent(EventType.particleAdded, {
|
|
86
82
|
particle,
|
|
@@ -94,12 +90,14 @@ export class ParticlesManager {
|
|
|
94
90
|
}
|
|
95
91
|
clear() {
|
|
96
92
|
this._array = [];
|
|
97
|
-
this.
|
|
93
|
+
this._particleBuckets.clear();
|
|
94
|
+
this._resetBuckets(this._container.zLayers);
|
|
98
95
|
}
|
|
99
96
|
destroy() {
|
|
100
97
|
this._array = [];
|
|
101
98
|
this._pool.length = 0;
|
|
102
|
-
this.
|
|
99
|
+
this._particleBuckets.clear();
|
|
100
|
+
this._zBuckets = [];
|
|
103
101
|
this.checkParticlePositionPlugins = [];
|
|
104
102
|
this._particleResetPlugins = [];
|
|
105
103
|
this._particleUpdatePlugins = [];
|
|
@@ -108,8 +106,14 @@ export class ParticlesManager {
|
|
|
108
106
|
this._updatePlugins = [];
|
|
109
107
|
}
|
|
110
108
|
drawParticles(delta) {
|
|
111
|
-
for (
|
|
112
|
-
|
|
109
|
+
for (let i = this._zBuckets.length - one; i >= minIndex; i--) {
|
|
110
|
+
const bucket = this._zBuckets[i];
|
|
111
|
+
if (!bucket) {
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
for (const particle of bucket) {
|
|
115
|
+
particle.draw(delta);
|
|
116
|
+
}
|
|
113
117
|
}
|
|
114
118
|
}
|
|
115
119
|
filter(condition) {
|
|
@@ -123,15 +127,14 @@ export class ParticlesManager {
|
|
|
123
127
|
}
|
|
124
128
|
async init() {
|
|
125
129
|
const container = this._container, options = container.actualOptions;
|
|
126
|
-
this._minZIndex = 0;
|
|
127
|
-
this._maxZIndex = 0;
|
|
128
|
-
this._needsSort = false;
|
|
129
130
|
this.checkParticlePositionPlugins = [];
|
|
130
131
|
this._updatePlugins = [];
|
|
131
132
|
this._particleUpdatePlugins = [];
|
|
132
133
|
this._postUpdatePlugins = [];
|
|
133
134
|
this._particleResetPlugins = [];
|
|
134
135
|
this._postParticleUpdatePlugins = [];
|
|
136
|
+
this._particleBuckets.clear();
|
|
137
|
+
this._resetBuckets(container.zLayers);
|
|
135
138
|
this.grid = new SpatialHashGrid(spatialHashGridCellSize * container.retina.pixelRatio);
|
|
136
139
|
for (const plugin of container.plugins) {
|
|
137
140
|
if (plugin.redrawInit) {
|
|
@@ -232,79 +235,25 @@ export class ParticlesManager {
|
|
|
232
235
|
}
|
|
233
236
|
this._applyDensity(options.particles, pluginsCount);
|
|
234
237
|
}
|
|
235
|
-
setLastZIndex(zIndex) {
|
|
236
|
-
this._needsSort ||= zIndex >= this._maxZIndex || (zIndex > this._minZIndex && zIndex < this._maxZIndex);
|
|
237
|
-
}
|
|
238
238
|
setResizeFactor(factor) {
|
|
239
239
|
this._resizeFactor = factor;
|
|
240
240
|
}
|
|
241
241
|
update(delta) {
|
|
242
|
-
const particlesToDelete = new Set();
|
|
243
242
|
this.grid.clear();
|
|
244
243
|
for (const plugin of this._updatePlugins) {
|
|
245
244
|
plugin.update?.(delta);
|
|
246
245
|
}
|
|
247
|
-
const
|
|
248
|
-
for (const particle of this._array) {
|
|
249
|
-
if (resizeFactor && !particle.ignoresResizeRatio) {
|
|
250
|
-
particle.position.x *= resizeFactor.width;
|
|
251
|
-
particle.position.y *= resizeFactor.height;
|
|
252
|
-
particle.initialPosition.x *= resizeFactor.width;
|
|
253
|
-
particle.initialPosition.y *= resizeFactor.height;
|
|
254
|
-
}
|
|
255
|
-
particle.ignoresResizeRatio = false;
|
|
256
|
-
for (const plugin of this._particleResetPlugins) {
|
|
257
|
-
plugin.particleReset?.(particle);
|
|
258
|
-
}
|
|
259
|
-
for (const plugin of this._particleUpdatePlugins) {
|
|
260
|
-
if (particle.destroyed) {
|
|
261
|
-
break;
|
|
262
|
-
}
|
|
263
|
-
plugin.particleUpdate?.(particle, delta);
|
|
264
|
-
}
|
|
265
|
-
if (particle.destroyed) {
|
|
266
|
-
particlesToDelete.add(particle);
|
|
267
|
-
continue;
|
|
268
|
-
}
|
|
269
|
-
this.grid.insert(particle);
|
|
270
|
-
}
|
|
246
|
+
const particlesToDelete = this._updateParticlesPhase1(delta);
|
|
271
247
|
for (const plugin of this._postUpdatePlugins) {
|
|
272
248
|
plugin.postUpdate?.(delta);
|
|
273
249
|
}
|
|
274
|
-
|
|
275
|
-
if (particle.destroyed) {
|
|
276
|
-
particlesToDelete.add(particle);
|
|
277
|
-
continue;
|
|
278
|
-
}
|
|
279
|
-
for (const updater of this._container.particleUpdaters) {
|
|
280
|
-
updater.update(particle, delta);
|
|
281
|
-
}
|
|
282
|
-
if (!particle.destroyed && !particle.spawning) {
|
|
283
|
-
for (const plugin of this._postParticleUpdatePlugins) {
|
|
284
|
-
plugin.postParticleUpdate?.(particle, delta);
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
else if (particle.destroyed) {
|
|
288
|
-
particlesToDelete.add(particle);
|
|
289
|
-
}
|
|
290
|
-
}
|
|
250
|
+
this._updateParticlesPhase2(delta, particlesToDelete);
|
|
291
251
|
if (particlesToDelete.size) {
|
|
292
252
|
for (const particle of particlesToDelete) {
|
|
293
253
|
this.remove(particle);
|
|
294
254
|
}
|
|
295
255
|
}
|
|
296
256
|
delete this._resizeFactor;
|
|
297
|
-
if (this._needsSort) {
|
|
298
|
-
const zArray = this._zArray;
|
|
299
|
-
zArray.sort((a, b) => b.position.z - a.position.z || a.id - b.id);
|
|
300
|
-
const firstItem = zArray[minIndex], lastItem = zArray[zArray.length - lengthOffset];
|
|
301
|
-
if (!firstItem || !lastItem) {
|
|
302
|
-
return;
|
|
303
|
-
}
|
|
304
|
-
this._maxZIndex = firstItem.position.z;
|
|
305
|
-
this._minZIndex = lastItem.position.z;
|
|
306
|
-
this._needsSort = false;
|
|
307
|
-
}
|
|
308
257
|
}
|
|
309
258
|
_addToPool = (...particles) => {
|
|
310
259
|
this._pool.push(...particles);
|
|
@@ -334,13 +283,52 @@ export class ParticlesManager {
|
|
|
334
283
|
this.removeQuantity(particlesCount - particlesNumber, group);
|
|
335
284
|
}
|
|
336
285
|
};
|
|
286
|
+
_createBuckets = (zLayers) => {
|
|
287
|
+
const bucketCount = Math.max(Math.floor(zLayers), one);
|
|
288
|
+
return Array.from({ length: bucketCount }, () => []);
|
|
289
|
+
};
|
|
290
|
+
_getBucketIndex = (zIndex) => {
|
|
291
|
+
const maxBucketIndex = this._zBuckets.length - one;
|
|
292
|
+
if (maxBucketIndex <= minIndex) {
|
|
293
|
+
return minIndex;
|
|
294
|
+
}
|
|
295
|
+
return Math.min(Math.max(Math.floor(zIndex), minIndex), maxBucketIndex);
|
|
296
|
+
};
|
|
297
|
+
_getParticleInsertIndex = (bucket, particleId) => {
|
|
298
|
+
let start = minIndex, end = bucket.length;
|
|
299
|
+
while (start < end) {
|
|
300
|
+
const middle = Math.floor((start + end) / double), middleParticle = bucket[middle];
|
|
301
|
+
if (!middleParticle) {
|
|
302
|
+
end = middle;
|
|
303
|
+
continue;
|
|
304
|
+
}
|
|
305
|
+
if (middleParticle.id < particleId) {
|
|
306
|
+
start = middle + one;
|
|
307
|
+
}
|
|
308
|
+
else {
|
|
309
|
+
end = middle;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
return start;
|
|
313
|
+
};
|
|
337
314
|
_initDensityFactor = densityOptions => {
|
|
338
315
|
const container = this._container;
|
|
339
|
-
if (!
|
|
316
|
+
if (!densityOptions.enable) {
|
|
340
317
|
return defaultDensityFactor;
|
|
341
318
|
}
|
|
342
|
-
const
|
|
343
|
-
|
|
319
|
+
const canvasSize = container.canvas.size, pxRatio = container.retina.pixelRatio;
|
|
320
|
+
if (!canvasSize.width || !canvasSize.height) {
|
|
321
|
+
return defaultDensityFactor;
|
|
322
|
+
}
|
|
323
|
+
return ((canvasSize.width * canvasSize.height) / (densityOptions.height * densityOptions.width * pxRatio ** squareExp));
|
|
324
|
+
};
|
|
325
|
+
_insertParticleIntoBucket = (particle) => {
|
|
326
|
+
const bucketIndex = this._getBucketIndex(particle.position.z), bucket = this._zBuckets[bucketIndex];
|
|
327
|
+
if (!bucket) {
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
bucket.splice(this._getParticleInsertIndex(bucket, particle.id), empty, particle);
|
|
331
|
+
this._particleBuckets.set(particle.id, bucketIndex);
|
|
344
332
|
};
|
|
345
333
|
_removeParticle = (index, group, override) => {
|
|
346
334
|
const particle = this._array[index];
|
|
@@ -350,9 +338,8 @@ export class ParticlesManager {
|
|
|
350
338
|
if (particle.group !== group) {
|
|
351
339
|
return false;
|
|
352
340
|
}
|
|
353
|
-
const zIdx = this._zArray.indexOf(particle);
|
|
354
341
|
this._array.splice(index, deleteCount);
|
|
355
|
-
this.
|
|
342
|
+
this._removeParticleFromBucket(particle);
|
|
356
343
|
particle.destroy(override);
|
|
357
344
|
this._container.dispatchEvent(EventType.particleRemoved, {
|
|
358
345
|
particle,
|
|
@@ -360,4 +347,96 @@ export class ParticlesManager {
|
|
|
360
347
|
this._addToPool(particle);
|
|
361
348
|
return true;
|
|
362
349
|
};
|
|
350
|
+
_removeParticleFromBucket = (particle) => {
|
|
351
|
+
const bucketIndex = this._particleBuckets.get(particle.id) ?? this._getBucketIndex(particle.position.z), bucket = this._zBuckets[bucketIndex];
|
|
352
|
+
if (!bucket) {
|
|
353
|
+
this._particleBuckets.delete(particle.id);
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
const particleIndex = this._getParticleInsertIndex(bucket, particle.id);
|
|
357
|
+
if (bucket[particleIndex]?.id !== particle.id) {
|
|
358
|
+
this._particleBuckets.delete(particle.id);
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
bucket.splice(particleIndex, deleteCount);
|
|
362
|
+
this._particleBuckets.delete(particle.id);
|
|
363
|
+
};
|
|
364
|
+
_resetBuckets = (zLayers) => {
|
|
365
|
+
const bucketCount = Math.max(Math.floor(zLayers), one);
|
|
366
|
+
if (this._zBuckets.length !== bucketCount) {
|
|
367
|
+
this._zBuckets = this._createBuckets(bucketCount);
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
for (const bucket of this._zBuckets) {
|
|
371
|
+
bucket.length = minIndex;
|
|
372
|
+
}
|
|
373
|
+
};
|
|
374
|
+
_updateParticleBucket = (particle) => {
|
|
375
|
+
const newBucketIndex = this._getBucketIndex(particle.position.z), currentBucketIndex = this._particleBuckets.get(particle.id);
|
|
376
|
+
if (currentBucketIndex === undefined) {
|
|
377
|
+
this._insertParticleIntoBucket(particle);
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
if (currentBucketIndex === newBucketIndex) {
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
const currentBucket = this._zBuckets[currentBucketIndex];
|
|
384
|
+
if (currentBucket) {
|
|
385
|
+
const particleIndex = this._getParticleInsertIndex(currentBucket, particle.id);
|
|
386
|
+
if (currentBucket[particleIndex]?.id === particle.id) {
|
|
387
|
+
currentBucket.splice(particleIndex, deleteCount);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
const newBucket = this._zBuckets[newBucketIndex];
|
|
391
|
+
if (!newBucket) {
|
|
392
|
+
this._particleBuckets.set(particle.id, newBucketIndex);
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
newBucket.splice(this._getParticleInsertIndex(newBucket, particle.id), empty, particle);
|
|
396
|
+
this._particleBuckets.set(particle.id, newBucketIndex);
|
|
397
|
+
};
|
|
398
|
+
_updateParticlesPhase1 = (delta) => {
|
|
399
|
+
const particlesToDelete = new Set(), resizeFactor = this._resizeFactor;
|
|
400
|
+
for (const particle of this._array) {
|
|
401
|
+
if (resizeFactor && !particle.ignoresResizeRatio) {
|
|
402
|
+
particle.position.x *= resizeFactor.width;
|
|
403
|
+
particle.position.y *= resizeFactor.height;
|
|
404
|
+
particle.initialPosition.x *= resizeFactor.width;
|
|
405
|
+
particle.initialPosition.y *= resizeFactor.height;
|
|
406
|
+
}
|
|
407
|
+
particle.ignoresResizeRatio = false;
|
|
408
|
+
for (const plugin of this._particleResetPlugins) {
|
|
409
|
+
plugin.particleReset?.(particle);
|
|
410
|
+
}
|
|
411
|
+
for (const plugin of this._particleUpdatePlugins) {
|
|
412
|
+
if (particle.destroyed) {
|
|
413
|
+
break;
|
|
414
|
+
}
|
|
415
|
+
plugin.particleUpdate?.(particle, delta);
|
|
416
|
+
}
|
|
417
|
+
if (particle.destroyed) {
|
|
418
|
+
particlesToDelete.add(particle);
|
|
419
|
+
continue;
|
|
420
|
+
}
|
|
421
|
+
this.grid.insert(particle);
|
|
422
|
+
}
|
|
423
|
+
return particlesToDelete;
|
|
424
|
+
};
|
|
425
|
+
_updateParticlesPhase2 = (delta, particlesToDelete) => {
|
|
426
|
+
for (const particle of this._array) {
|
|
427
|
+
if (particle.destroyed) {
|
|
428
|
+
particlesToDelete.add(particle);
|
|
429
|
+
continue;
|
|
430
|
+
}
|
|
431
|
+
for (const updater of this._container.particleUpdaters) {
|
|
432
|
+
updater.update(particle, delta);
|
|
433
|
+
}
|
|
434
|
+
if (!particle.spawning) {
|
|
435
|
+
for (const plugin of this._postParticleUpdatePlugins) {
|
|
436
|
+
plugin.postParticleUpdate?.(particle, delta);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
this._updateParticleBucket(particle);
|
|
440
|
+
}
|
|
441
|
+
};
|
|
363
442
|
}
|
package/cjs/Core/Retina.js
CHANGED
|
@@ -13,9 +13,8 @@ export class Retina {
|
|
|
13
13
|
const container = this.container, options = container.actualOptions;
|
|
14
14
|
this.pixelRatio = options.detectRetina ? devicePixelRatio : defaultRatio;
|
|
15
15
|
this.reduceFactor = defaultReduceFactor;
|
|
16
|
-
const ratio = this.pixelRatio, canvas = container.canvas;
|
|
17
|
-
if (
|
|
18
|
-
const element = canvas.element;
|
|
16
|
+
const ratio = this.pixelRatio, canvas = container.canvas, element = canvas.domElement;
|
|
17
|
+
if (element) {
|
|
19
18
|
canvas.size.width = element.offsetWidth * ratio;
|
|
20
19
|
canvas.size.height = element.offsetHeight * ratio;
|
|
21
20
|
}
|
|
@@ -66,7 +66,7 @@ export class EventListeners {
|
|
|
66
66
|
manageListener(globalThis, resizeEvent, handlers.resize, add);
|
|
67
67
|
return;
|
|
68
68
|
}
|
|
69
|
-
const canvasEl = container.canvas.
|
|
69
|
+
const canvasEl = container.canvas.domElement;
|
|
70
70
|
if (this._resizeObserver && !add) {
|
|
71
71
|
if (canvasEl) {
|
|
72
72
|
this._resizeObserver.unobserve(canvasEl);
|