UNPKG

13.1 kBJavaScriptView Raw
1import { ClickMode, InteractivityDetect } from "../Enums";
2import { Constants } from "./Constants";
3function manageListener(element, event, handler, add, options) {
4 if (add) {
5 let addOptions = { passive: true };
6 if (typeof options === "boolean") {
7 addOptions.capture = options;
8 }
9 else if (options !== undefined) {
10 addOptions = options;
11 }
12 element.addEventListener(event, handler, addOptions);
13 }
14 else {
15 const removeOptions = options;
16 element.removeEventListener(event, handler, removeOptions);
17 }
18}
19export class EventListeners {
20 constructor(container) {
21 this.container = container;
22 this.canPush = true;
23 this.mouseMoveHandler = (e) => this.mouseTouchMove(e);
24 this.touchStartHandler = (e) => this.mouseTouchMove(e);
25 this.touchMoveHandler = (e) => this.mouseTouchMove(e);
26 this.touchEndHandler = () => this.mouseTouchFinish();
27 this.mouseLeaveHandler = () => this.mouseTouchFinish();
28 this.touchCancelHandler = () => this.mouseTouchFinish();
29 this.touchEndClickHandler = (e) => this.mouseTouchClick(e);
30 this.mouseUpHandler = (e) => this.mouseTouchClick(e);
31 this.mouseDownHandler = () => this.mouseDown();
32 this.visibilityChangeHandler = () => this.handleVisibilityChange();
33 this.resizeHandler = () => this.handleWindowResize();
34 }
35 addListeners() {
36 this.manageListeners(true);
37 }
38 removeListeners() {
39 this.manageListeners(false);
40 }
41 manageListeners(add) {
42 var _a;
43 const container = this.container;
44 const options = container.actualOptions;
45 const detectType = options.interactivity.detectsOn;
46 let mouseLeaveEvent = Constants.mouseLeaveEvent;
47 if (detectType === InteractivityDetect.window) {
48 container.interactivity.element = window;
49 mouseLeaveEvent = Constants.mouseOutEvent;
50 }
51 else if (detectType === InteractivityDetect.parent && container.canvas.element) {
52 const canvasEl = container.canvas.element;
53 container.interactivity.element = (_a = canvasEl.parentElement) !== null && _a !== void 0 ? _a : canvasEl.parentNode;
54 }
55 else {
56 container.interactivity.element = container.canvas.element;
57 }
58 const interactivityEl = container.interactivity.element;
59 if (!interactivityEl) {
60 return;
61 }
62 const html = interactivityEl;
63 if (options.interactivity.events.onHover.enable || options.interactivity.events.onClick.enable) {
64 manageListener(interactivityEl, Constants.mouseMoveEvent, this.mouseMoveHandler, add);
65 manageListener(interactivityEl, Constants.touchStartEvent, this.touchStartHandler, add);
66 manageListener(interactivityEl, Constants.touchMoveEvent, this.touchMoveHandler, add);
67 if (!options.interactivity.events.onClick.enable) {
68 manageListener(interactivityEl, Constants.touchEndEvent, this.touchEndHandler, add);
69 }
70 else {
71 manageListener(interactivityEl, Constants.touchEndEvent, this.touchEndClickHandler, add);
72 manageListener(interactivityEl, Constants.mouseUpEvent, this.mouseUpHandler, add);
73 manageListener(interactivityEl, Constants.mouseDownEvent, this.mouseDownHandler, add);
74 }
75 manageListener(interactivityEl, mouseLeaveEvent, this.mouseLeaveHandler, add);
76 manageListener(interactivityEl, Constants.touchCancelEvent, this.touchCancelHandler, add);
77 }
78 if (container.canvas.element) {
79 container.canvas.element.style.pointerEvents = html === container.canvas.element ? "initial" : "none";
80 }
81 if (options.interactivity.events.resize) {
82 manageListener(window, Constants.resizeEvent, this.resizeHandler, add);
83 }
84 if (document) {
85 manageListener(document, Constants.visibilityChangeEvent, this.visibilityChangeHandler, add, false);
86 }
87 }
88 handleWindowResize() {
89 var _a;
90 (_a = this.container.canvas) === null || _a === void 0 ? void 0 : _a.windowResize();
91 }
92 handleVisibilityChange() {
93 const container = this.container;
94 const options = container.actualOptions;
95 this.mouseTouchFinish();
96 if (!options.pauseOnBlur) {
97 return;
98 }
99 if (document === null || document === void 0 ? void 0 : document.hidden) {
100 container.pageHidden = true;
101 container.pause();
102 }
103 else {
104 container.pageHidden = false;
105 if (container.getAnimationStatus()) {
106 container.play(true);
107 }
108 else {
109 container.draw();
110 }
111 }
112 }
113 mouseDown() {
114 const interactivity = this.container.interactivity;
115 if (interactivity) {
116 const mouse = interactivity.mouse;
117 mouse.clicking = true;
118 mouse.downPosition = mouse.position;
119 }
120 }
121 mouseTouchMove(e) {
122 var _a, _b, _c, _d, _e, _f, _g;
123 const container = this.container;
124 const options = container.actualOptions;
125 if (((_a = container.interactivity) === null || _a === void 0 ? void 0 : _a.element) === undefined) {
126 return;
127 }
128 container.interactivity.mouse.inside = true;
129 let pos;
130 const canvas = container.canvas.element;
131 if (e.type.startsWith("mouse")) {
132 this.canPush = true;
133 const mouseEvent = e;
134 if (container.interactivity.element === window) {
135 if (canvas) {
136 const clientRect = canvas.getBoundingClientRect();
137 pos = {
138 x: mouseEvent.clientX - clientRect.left,
139 y: mouseEvent.clientY - clientRect.top,
140 };
141 }
142 }
143 else if (options.interactivity.detectsOn === InteractivityDetect.parent) {
144 const source = mouseEvent.target;
145 const target = mouseEvent.currentTarget;
146 const canvasEl = container.canvas.element;
147 if (source && target && canvasEl) {
148 const sourceRect = source.getBoundingClientRect();
149 const targetRect = target.getBoundingClientRect();
150 const canvasRect = canvasEl.getBoundingClientRect();
151 pos = {
152 x: mouseEvent.offsetX + 2 * sourceRect.left - (targetRect.left + canvasRect.left),
153 y: mouseEvent.offsetY + 2 * sourceRect.top - (targetRect.top + canvasRect.top),
154 };
155 }
156 else {
157 pos = {
158 x: (_b = mouseEvent.offsetX) !== null && _b !== void 0 ? _b : mouseEvent.clientX,
159 y: (_c = mouseEvent.offsetY) !== null && _c !== void 0 ? _c : mouseEvent.clientY,
160 };
161 }
162 }
163 else {
164 if (mouseEvent.target === container.canvas.element) {
165 pos = {
166 x: (_d = mouseEvent.offsetX) !== null && _d !== void 0 ? _d : mouseEvent.clientX,
167 y: (_e = mouseEvent.offsetY) !== null && _e !== void 0 ? _e : mouseEvent.clientY,
168 };
169 }
170 }
171 }
172 else {
173 this.canPush = e.type !== "touchmove";
174 const touchEvent = e;
175 const lastTouch = touchEvent.touches[touchEvent.touches.length - 1];
176 const canvasRect = canvas === null || canvas === void 0 ? void 0 : canvas.getBoundingClientRect();
177 pos = {
178 x: lastTouch.clientX - ((_f = canvasRect === null || canvasRect === void 0 ? void 0 : canvasRect.left) !== null && _f !== void 0 ? _f : 0),
179 y: lastTouch.clientY - ((_g = canvasRect === null || canvasRect === void 0 ? void 0 : canvasRect.top) !== null && _g !== void 0 ? _g : 0),
180 };
181 }
182 const pxRatio = container.retina.pixelRatio;
183 if (pos) {
184 pos.x *= pxRatio;
185 pos.y *= pxRatio;
186 }
187 container.interactivity.mouse.position = pos;
188 container.interactivity.status = Constants.mouseMoveEvent;
189 }
190 mouseTouchFinish() {
191 const interactivity = this.container.interactivity;
192 if (interactivity === undefined) {
193 return;
194 }
195 const mouse = interactivity.mouse;
196 delete mouse.position;
197 delete mouse.clickPosition;
198 delete mouse.downPosition;
199 interactivity.status = Constants.mouseLeaveEvent;
200 mouse.inside = false;
201 mouse.clicking = false;
202 }
203 mouseTouchClick(e) {
204 const container = this.container;
205 const options = container.actualOptions;
206 const mouse = container.interactivity.mouse;
207 mouse.inside = true;
208 let handled = false;
209 const mousePosition = mouse.position;
210 if (mousePosition === undefined || !options.interactivity.events.onClick.enable) {
211 return;
212 }
213 for (const [, plugin] of container.plugins) {
214 if (plugin.clickPositionValid !== undefined) {
215 handled = plugin.clickPositionValid(mousePosition);
216 if (handled) {
217 break;
218 }
219 }
220 }
221 if (!handled) {
222 this.doMouseTouchClick(e);
223 }
224 mouse.clicking = false;
225 }
226 doMouseTouchClick(e) {
227 const container = this.container;
228 const options = container.actualOptions;
229 if (this.canPush) {
230 const mousePos = container.interactivity.mouse.position;
231 if (mousePos) {
232 container.interactivity.mouse.clickPosition = {
233 x: mousePos.x,
234 y: mousePos.y,
235 };
236 }
237 else {
238 return;
239 }
240 container.interactivity.mouse.clickTime = new Date().getTime();
241 const onClick = options.interactivity.events.onClick;
242 if (onClick.mode instanceof Array) {
243 for (const mode of onClick.mode) {
244 this.handleClickMode(mode);
245 }
246 }
247 else {
248 this.handleClickMode(onClick.mode);
249 }
250 }
251 if (e.type === "touchend") {
252 setTimeout(() => this.mouseTouchFinish(), 500);
253 }
254 }
255 handleClickMode(mode) {
256 const container = this.container;
257 const options = container.actualOptions;
258 const pushNb = options.interactivity.modes.push.quantity;
259 const removeNb = options.interactivity.modes.remove.quantity;
260 switch (mode) {
261 case ClickMode.push: {
262 if (pushNb > 0) {
263 container.particles.push(pushNb, container.interactivity.mouse);
264 }
265 break;
266 }
267 case ClickMode.remove:
268 container.particles.removeQuantity(removeNb);
269 break;
270 case ClickMode.bubble:
271 container.bubble.clicking = true;
272 break;
273 case ClickMode.repulse:
274 container.repulse.clicking = true;
275 container.repulse.count = 0;
276 for (const particle of container.repulse.particles) {
277 particle.velocity.setTo(particle.initialVelocity);
278 }
279 container.repulse.particles = [];
280 container.repulse.finish = false;
281 setTimeout(() => {
282 if (!container.destroyed) {
283 container.repulse.clicking = false;
284 }
285 }, options.interactivity.modes.repulse.duration * 1000);
286 break;
287 case ClickMode.attract:
288 container.attract.clicking = true;
289 container.attract.count = 0;
290 for (const particle of container.attract.particles) {
291 particle.velocity.setTo(particle.initialVelocity);
292 }
293 container.attract.particles = [];
294 container.attract.finish = false;
295 setTimeout(() => {
296 if (!container.destroyed) {
297 container.attract.clicking = false;
298 }
299 }, options.interactivity.modes.attract.duration * 1000);
300 break;
301 case ClickMode.pause:
302 if (container.getAnimationStatus()) {
303 container.pause();
304 }
305 else {
306 container.play();
307 }
308 break;
309 }
310 for (const [, plugin] of container.plugins) {
311 if (plugin.handleClickMode) {
312 plugin.handleClickMode(mode);
313 }
314 }
315 }
316}