1 | import { colorToHsl, colorToRgb, Constants, deepExtend, drawConnectLine, drawGrabLine, drawParticle, drawParticlePlugin, drawPlugin, getStyleFromHsl, getStyleFromRgb, gradient, paintBase, } from "../Utils";
|
2 | import { clear } from "../Utils";
|
3 |
|
4 |
|
5 |
|
6 |
|
7 | export class Canvas {
|
8 | |
9 |
|
10 |
|
11 |
|
12 | constructor(container) {
|
13 | this.container = container;
|
14 | this.size = {
|
15 | height: 0,
|
16 | width: 0,
|
17 | };
|
18 | this.context = null;
|
19 | this.generatedCanvas = false;
|
20 | }
|
21 |
|
22 | |
23 |
|
24 |
|
25 | init() {
|
26 | this.resize();
|
27 | this.initStyle();
|
28 | this.initCover();
|
29 | this.initTrail();
|
30 | this.initBackground();
|
31 | this.paint();
|
32 | }
|
33 | loadCanvas(canvas, generatedCanvas) {
|
34 | var _a;
|
35 | if (!canvas.className) {
|
36 | canvas.className = Constants.canvasClass;
|
37 | }
|
38 | if (this.generatedCanvas) {
|
39 | (_a = this.element) === null || _a === void 0 ? void 0 : _a.remove();
|
40 | }
|
41 | this.generatedCanvas = generatedCanvas !== null && generatedCanvas !== void 0 ? generatedCanvas : this.generatedCanvas;
|
42 | this.element = canvas;
|
43 | this.originalStyle = deepExtend({}, this.element.style);
|
44 | this.size.height = canvas.offsetHeight;
|
45 | this.size.width = canvas.offsetWidth;
|
46 | this.context = this.element.getContext("2d");
|
47 | this.container.retina.init();
|
48 | this.initBackground();
|
49 | }
|
50 | destroy() {
|
51 | var _a;
|
52 | if (this.generatedCanvas) {
|
53 | (_a = this.element) === null || _a === void 0 ? void 0 : _a.remove();
|
54 | }
|
55 | this.draw((ctx) => {
|
56 | clear(ctx, this.size);
|
57 | });
|
58 | }
|
59 | |
60 |
|
61 |
|
62 | paint() {
|
63 | const options = this.container.actualOptions;
|
64 | this.draw((ctx) => {
|
65 | if (options.backgroundMask.enable && options.backgroundMask.cover && this.coverColor) {
|
66 | clear(ctx, this.size);
|
67 | this.paintBase(getStyleFromRgb(this.coverColor, this.coverColor.a));
|
68 | }
|
69 | else {
|
70 | this.paintBase();
|
71 | }
|
72 | });
|
73 | }
|
74 | |
75 |
|
76 |
|
77 | clear() {
|
78 | const options = this.container.actualOptions;
|
79 | const trail = options.particles.move.trail;
|
80 | if (options.backgroundMask.enable) {
|
81 | this.paint();
|
82 | }
|
83 | else if (trail.enable && trail.length > 0 && this.trailFillColor) {
|
84 | this.paintBase(getStyleFromRgb(this.trailFillColor, 1 / trail.length));
|
85 | }
|
86 | else {
|
87 | this.draw((ctx) => {
|
88 | clear(ctx, this.size);
|
89 | });
|
90 | }
|
91 | }
|
92 | windowResize() {
|
93 | if (!this.element) {
|
94 | return;
|
95 | }
|
96 | const container = this.container;
|
97 | this.resize();
|
98 | const needsRefresh = container.updateActualOptions();
|
99 |
|
100 | container.particles.setDensity();
|
101 | for (const [, plugin] of container.plugins) {
|
102 | if (plugin.resize !== undefined) {
|
103 | plugin.resize();
|
104 | }
|
105 | }
|
106 | if (needsRefresh) {
|
107 | container.refresh();
|
108 | }
|
109 | }
|
110 | |
111 |
|
112 |
|
113 | resize() {
|
114 | if (!this.element) {
|
115 | return;
|
116 | }
|
117 | const container = this.container;
|
118 | const pxRatio = container.retina.pixelRatio;
|
119 | const size = container.canvas.size;
|
120 | const oldSize = {
|
121 | width: size.width,
|
122 | height: size.height,
|
123 | };
|
124 | size.width = this.element.offsetWidth * pxRatio;
|
125 | size.height = this.element.offsetHeight * pxRatio;
|
126 | this.element.width = size.width;
|
127 | this.element.height = size.height;
|
128 | if (this.container.started) {
|
129 | this.resizeFactor = {
|
130 | width: size.width / oldSize.width,
|
131 | height: size.height / oldSize.height,
|
132 | };
|
133 | }
|
134 | }
|
135 | drawConnectLine(p1, p2) {
|
136 | this.draw((ctx) => {
|
137 | var _a;
|
138 | const lineStyle = this.lineStyle(p1, p2);
|
139 | if (!lineStyle) {
|
140 | return;
|
141 | }
|
142 | const pos1 = p1.getPosition();
|
143 | const pos2 = p2.getPosition();
|
144 | drawConnectLine(ctx, (_a = p1.retina.linksWidth) !== null && _a !== void 0 ? _a : this.container.retina.linksWidth, lineStyle, pos1, pos2);
|
145 | });
|
146 | }
|
147 | drawGrabLine(particle, lineColor, opacity, mousePos) {
|
148 | const container = this.container;
|
149 | this.draw((ctx) => {
|
150 | var _a;
|
151 | const beginPos = particle.getPosition();
|
152 | drawGrabLine(ctx, (_a = particle.retina.linksWidth) !== null && _a !== void 0 ? _a : container.retina.linksWidth, beginPos, mousePos, lineColor, opacity);
|
153 | });
|
154 | }
|
155 | drawParticle(particle, delta) {
|
156 | var _a, _b, _c, _d, _e, _f;
|
157 | if (particle.spawning || particle.destroyed) {
|
158 | return;
|
159 | }
|
160 | const pfColor = particle.getFillColor();
|
161 | const psColor = (_a = particle.getStrokeColor()) !== null && _a !== void 0 ? _a : pfColor;
|
162 | if (!pfColor && !psColor) {
|
163 | return;
|
164 | }
|
165 | let [fColor, sColor] = this.getPluginParticleColors(particle);
|
166 | const pOptions = particle.options;
|
167 | const twinkle = pOptions.twinkle.particles;
|
168 | const twinkling = twinkle.enable && Math.random() < twinkle.frequency;
|
169 | if (!fColor || !sColor) {
|
170 | const twinkleRgb = colorToHsl(twinkle.color);
|
171 | if (!fColor) {
|
172 | fColor = twinkling && twinkleRgb !== undefined ? twinkleRgb : pfColor ? pfColor : undefined;
|
173 | }
|
174 | if (!sColor) {
|
175 | sColor = twinkling && twinkleRgb !== undefined ? twinkleRgb : psColor ? psColor : undefined;
|
176 | }
|
177 | }
|
178 | const options = this.container.actualOptions;
|
179 | const zIndexOptions = particle.options.zIndex;
|
180 | const zOpacityFactor = (1 - particle.zIndexFactor) ** zIndexOptions.opacityRate;
|
181 | const radius = particle.getRadius();
|
182 | const opacity = twinkling ? twinkle.opacity : (_d = (_b = particle.bubble.opacity) !== null && _b !== void 0 ? _b : (_c = particle.opacity) === null || _c === void 0 ? void 0 : _c.value) !== null && _d !== void 0 ? _d : 1;
|
183 | const strokeOpacity = (_f = (_e = particle.stroke) === null || _e === void 0 ? void 0 : _e.opacity) !== null && _f !== void 0 ? _f : opacity;
|
184 | const zOpacity = opacity * zOpacityFactor;
|
185 | const fillColorValue = fColor ? getStyleFromHsl(fColor, zOpacity) : undefined;
|
186 | if (!fillColorValue && !sColor) {
|
187 | return;
|
188 | }
|
189 | this.draw((ctx) => {
|
190 | const zSizeFactor = (1 - particle.zIndexFactor) ** zIndexOptions.sizeRate;
|
191 | const zStrokeOpacity = strokeOpacity * zOpacityFactor;
|
192 | const strokeColorValue = sColor ? getStyleFromHsl(sColor, zStrokeOpacity) : fillColorValue;
|
193 | if (radius <= 0) {
|
194 | return;
|
195 | }
|
196 | const container = this.container;
|
197 | for (const updater of container.particles.updaters) {
|
198 | if (updater.beforeDraw) {
|
199 | updater.beforeDraw(particle);
|
200 | }
|
201 | }
|
202 | drawParticle(this.container, ctx, particle, delta, fillColorValue, strokeColorValue, options.backgroundMask.enable, options.backgroundMask.composite, radius * zSizeFactor, zOpacity, particle.options.shadow, particle.gradient);
|
203 | for (const updater of container.particles.updaters) {
|
204 | if (updater.afterDraw) {
|
205 | updater.afterDraw(particle);
|
206 | }
|
207 | }
|
208 | });
|
209 | }
|
210 | drawPlugin(plugin, delta) {
|
211 | this.draw((ctx) => {
|
212 | drawPlugin(ctx, plugin, delta);
|
213 | });
|
214 | }
|
215 | drawParticlePlugin(plugin, particle, delta) {
|
216 | this.draw((ctx) => {
|
217 | drawParticlePlugin(ctx, plugin, particle, delta);
|
218 | });
|
219 | }
|
220 | initBackground() {
|
221 | const options = this.container.actualOptions;
|
222 | const background = options.background;
|
223 | const element = this.element;
|
224 | const elementStyle = element === null || element === void 0 ? void 0 : element.style;
|
225 | if (!elementStyle) {
|
226 | return;
|
227 | }
|
228 | if (background.color) {
|
229 | const color = colorToRgb(background.color);
|
230 | elementStyle.backgroundColor = color ? getStyleFromRgb(color, background.opacity) : "";
|
231 | }
|
232 | else {
|
233 | elementStyle.backgroundColor = "";
|
234 | }
|
235 | elementStyle.backgroundImage = background.image || "";
|
236 | elementStyle.backgroundPosition = background.position || "";
|
237 | elementStyle.backgroundRepeat = background.repeat || "";
|
238 | elementStyle.backgroundSize = background.size || "";
|
239 | }
|
240 | draw(cb) {
|
241 | if (!this.context) {
|
242 | return;
|
243 | }
|
244 | return cb(this.context);
|
245 | }
|
246 | initCover() {
|
247 | const options = this.container.actualOptions;
|
248 | const cover = options.backgroundMask.cover;
|
249 | const color = cover.color;
|
250 | const coverRgb = colorToRgb(color);
|
251 | if (coverRgb) {
|
252 | this.coverColor = {
|
253 | r: coverRgb.r,
|
254 | g: coverRgb.g,
|
255 | b: coverRgb.b,
|
256 | a: cover.opacity,
|
257 | };
|
258 | }
|
259 | }
|
260 | initTrail() {
|
261 | const options = this.container.actualOptions;
|
262 | const trail = options.particles.move.trail;
|
263 | const fillColor = colorToRgb(trail.fillColor);
|
264 | if (fillColor) {
|
265 | const trail = options.particles.move.trail;
|
266 | this.trailFillColor = {
|
267 | r: fillColor.r,
|
268 | g: fillColor.g,
|
269 | b: fillColor.b,
|
270 | a: 1 / trail.length,
|
271 | };
|
272 | }
|
273 | }
|
274 | getPluginParticleColors(particle) {
|
275 | let fColor;
|
276 | let sColor;
|
277 | for (const [, plugin] of this.container.plugins) {
|
278 | if (!fColor && plugin.particleFillColor) {
|
279 | fColor = colorToHsl(plugin.particleFillColor(particle));
|
280 | }
|
281 | if (!sColor && plugin.particleStrokeColor) {
|
282 | sColor = colorToHsl(plugin.particleStrokeColor(particle));
|
283 | }
|
284 | if (fColor && sColor) {
|
285 | break;
|
286 | }
|
287 | }
|
288 | return [fColor, sColor];
|
289 | }
|
290 | initStyle() {
|
291 | const element = this.element, options = this.container.actualOptions;
|
292 | if (!element) {
|
293 | return;
|
294 | }
|
295 | const originalStyle = this.originalStyle;
|
296 | if (options.fullScreen.enable) {
|
297 | this.originalStyle = deepExtend({}, element.style);
|
298 | element.style.position = "fixed";
|
299 | element.style.zIndex = options.fullScreen.zIndex.toString(10);
|
300 | element.style.top = "0";
|
301 | element.style.left = "0";
|
302 | element.style.width = "100%";
|
303 | element.style.height = "100%";
|
304 | }
|
305 | else if (originalStyle) {
|
306 | element.style.position = originalStyle.position;
|
307 | element.style.zIndex = originalStyle.zIndex;
|
308 | element.style.top = originalStyle.top;
|
309 | element.style.left = originalStyle.left;
|
310 | element.style.width = originalStyle.width;
|
311 | element.style.height = originalStyle.height;
|
312 | }
|
313 | }
|
314 | paintBase(baseColor) {
|
315 | this.draw((ctx) => {
|
316 | paintBase(ctx, this.size, baseColor);
|
317 | });
|
318 | }
|
319 | lineStyle(p1, p2) {
|
320 | return this.draw((ctx) => {
|
321 | const options = this.container.actualOptions;
|
322 | const connectOptions = options.interactivity.modes.connect;
|
323 | return gradient(ctx, p1, p2, connectOptions.links.opacity);
|
324 | });
|
325 | }
|
326 | }
|