UNPKG

12 kBJavaScriptView Raw
1import { colorToHsl, colorToRgb, Constants, deepExtend, drawConnectLine, drawGrabLine, drawParticle, drawParticlePlugin, drawPlugin, getStyleFromHsl, getStyleFromRgb, gradient, paintBase, } from "../Utils";
2import { clear } from "../Utils";
3/**
4 * Canvas manager
5 * @category Core
6 */
7export class Canvas {
8 /**
9 * Constructor of canvas manager
10 * @param container the parent container
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 /* ---------- tsParticles functions - canvas ------------ */
22 /**
23 * Initializes the canvas element
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 * Paints the canvas background
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 * Clears the canvas content
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 /* density particles enabled */
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 * Calculates the size of the canvas
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}