@supermousejs/core 2.0.4 → 2.1.0
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/CHANGELOG.md +51 -31
- package/LICENSE.md +21 -21
- package/README.md +30 -31
- package/dist/index.d.ts +8 -55
- package/dist/index.mjs +85 -146
- package/dist/index.umd.js +2 -2
- package/package.json +11 -1
- package/src/Supermouse.ts +339 -405
- package/src/index.ts +2 -2
- package/src/systems/Input.ts +231 -262
- package/src/systems/Stage.ts +126 -156
- package/src/systems/index.ts +2 -2
- package/src/types.ts +168 -169
- package/src/utils/math.ts +11 -20
- package/tsconfig.json +7 -14
- package/tsconfig.tsbuildinfo +1 -0
- package/vite.config.ts +32 -32
package/dist/index.mjs
CHANGED
|
@@ -3,8 +3,8 @@ class c {
|
|
|
3
3
|
constructor(t = document.body, e) {
|
|
4
4
|
if (this.container = t, this.hideNativeCursor = e, !t || !(t instanceof HTMLElement))
|
|
5
5
|
throw new Error(`[Supermouse] Invalid container: ${t}. Must be an HTMLElement.`);
|
|
6
|
-
const
|
|
7
|
-
this.id = `supermouse-style-${
|
|
6
|
+
const i = d++;
|
|
7
|
+
this.id = `supermouse-style-${i}`, this.scopeClass = `supermouse-scope-${i}`;
|
|
8
8
|
const n = t === document.body;
|
|
9
9
|
this.element = document.createElement("div"), Object.assign(this.element.style, {
|
|
10
10
|
position: n ? "fixed" : "absolute",
|
|
@@ -18,15 +18,14 @@ class c {
|
|
|
18
18
|
transition: "opacity 0.15s ease"
|
|
19
19
|
}), n || window.getComputedStyle(t).position === "static" && (t.style.position = "relative"), t.appendChild(this.element), this.styleTag = document.createElement("style"), this.styleTag.id = this.id, document.head.appendChild(this.styleTag), this.container.classList.add(this.scopeClass), this.hideNativeCursor && this.setNativeCursor("none");
|
|
20
20
|
}
|
|
21
|
-
|
|
21
|
+
container;
|
|
22
|
+
hideNativeCursor;
|
|
23
|
+
/** The container element appended to the document. */
|
|
22
24
|
element;
|
|
23
25
|
styleTag;
|
|
24
26
|
id;
|
|
25
27
|
scopeClass;
|
|
26
|
-
// Cache to prevent redundant DOM updates
|
|
27
28
|
currentCursorState = null;
|
|
28
|
-
// Defaults for CSS hiding. We must override user-agent styles on these elements
|
|
29
|
-
// to prevent the native cursor from popping through.
|
|
30
29
|
selectors = /* @__PURE__ */ new Set([
|
|
31
30
|
"a",
|
|
32
31
|
"button",
|
|
@@ -37,16 +36,13 @@ class c {
|
|
|
37
36
|
"[tabindex]"
|
|
38
37
|
]);
|
|
39
38
|
/**
|
|
40
|
-
* Adds a new CSS selector to the
|
|
41
|
-
* Called by `Supermouse` (and subsequently plugins) during install to ensure
|
|
39
|
+
* Adds a new CSS selector to the Hide Native Cursor list.
|
|
40
|
+
* Called by `Supermouse` (and subsequently plugins) during install to ensure
|
|
42
41
|
* the native cursor is hidden on their specific interactive targets.
|
|
43
42
|
*/
|
|
44
43
|
addSelector(t) {
|
|
45
44
|
this.selectors.add(t), this.hideNativeCursor && this.updateCursorCSS();
|
|
46
45
|
}
|
|
47
|
-
/**
|
|
48
|
-
* Controls the opacity of the entire stage (all custom cursor elements).
|
|
49
|
-
*/
|
|
50
46
|
setVisibility(t) {
|
|
51
47
|
this.element.style.opacity = t ? "1" : "0";
|
|
52
48
|
}
|
|
@@ -63,7 +59,7 @@ class c {
|
|
|
63
59
|
this.styleTag.innerText = "";
|
|
64
60
|
return;
|
|
65
61
|
}
|
|
66
|
-
const e = t.map((
|
|
62
|
+
const e = t.map((i) => `.${this.scopeClass} ${i}`).join(", ");
|
|
67
63
|
this.styleTag.innerText = `
|
|
68
64
|
${e} {
|
|
69
65
|
cursor: none !important;
|
|
@@ -75,24 +71,23 @@ class c {
|
|
|
75
71
|
}
|
|
76
72
|
}
|
|
77
73
|
class p {
|
|
78
|
-
constructor(t, e,
|
|
79
|
-
this.state = t, this.options = e, this.getHoverSelector =
|
|
74
|
+
constructor(t, e, i, n) {
|
|
75
|
+
this.state = t, this.options = e, this.getHoverSelector = i, this.onEnableChange = n, this.checkDeviceCapability(), this.checkMotionPreference(), this.bindEvents();
|
|
80
76
|
}
|
|
77
|
+
state;
|
|
78
|
+
options;
|
|
79
|
+
getHoverSelector;
|
|
80
|
+
onEnableChange;
|
|
81
81
|
mediaQueryList;
|
|
82
82
|
mediaQueryHandler;
|
|
83
83
|
motionQuery;
|
|
84
84
|
/**
|
|
85
|
-
* Master switch for input processing.
|
|
85
|
+
* Master switch for input processing.
|
|
86
86
|
* Toggled by `Supermouse.enable()`/`disable()` or automatically by device capability checks.
|
|
87
87
|
*/
|
|
88
88
|
isEnabled = !0;
|
|
89
89
|
/**
|
|
90
|
-
*
|
|
91
|
-
* We cache the resolved InteractionState for every element we encounter in a WeakMap.
|
|
92
|
-
*/
|
|
93
|
-
interactionCache = /* @__PURE__ */ new WeakMap();
|
|
94
|
-
/**
|
|
95
|
-
* Automatically disables the custom cursor on devices without fine pointer control (e.g. phones/tablets).
|
|
90
|
+
* Automatically disables the custom cursor on devices without fine pointer control.
|
|
96
91
|
* Relies on `matchMedia('(pointer: fine)')`.
|
|
97
92
|
*/
|
|
98
93
|
checkDeviceCapability() {
|
|
@@ -105,108 +100,95 @@ class p {
|
|
|
105
100
|
* If true, the core physics engine will switch to instant snapping (high damping) to avoid motion sickness.
|
|
106
101
|
*/
|
|
107
102
|
checkMotionPreference() {
|
|
108
|
-
this.motionQuery = window.matchMedia("(
|
|
103
|
+
this.motionQuery = window.matchMedia("(prefers-reduced-motion: reduce)"), this.state.reducedMotion = this.motionQuery.matches, this.motionQuery.addEventListener("change", (t) => {
|
|
109
104
|
this.state.reducedMotion = t.matches;
|
|
110
105
|
});
|
|
111
106
|
}
|
|
112
107
|
updateEnabledState(t) {
|
|
113
108
|
this.isEnabled = t, this.onEnableChange(t);
|
|
114
109
|
}
|
|
115
|
-
// --- Interaction Parsing ---
|
|
116
|
-
/**
|
|
117
|
-
* Scrapes the DOM element for metadata to populate `state.interaction`.
|
|
118
|
-
*
|
|
119
|
-
* **Strategy:**
|
|
120
|
-
* 1. Check WeakMap cache.
|
|
121
|
-
* 2. Apply config-based `rules`.
|
|
122
|
-
* 3. Scrape `dataset` (supermouse*).
|
|
123
|
-
* 4. Cache result.
|
|
124
|
-
*/
|
|
125
110
|
parseDOMInteraction(t) {
|
|
126
111
|
if (this.options.resolveInteraction) {
|
|
127
112
|
this.state.interaction = this.options.resolveInteraction(t);
|
|
128
113
|
return;
|
|
129
114
|
}
|
|
130
|
-
if (this.interactionCache.has(t)) {
|
|
131
|
-
this.state.interaction = this.interactionCache.get(t);
|
|
132
|
-
return;
|
|
133
|
-
}
|
|
134
115
|
const e = {};
|
|
135
116
|
if (this.options.rules)
|
|
136
|
-
for (const [n,
|
|
137
|
-
t.matches(n) && Object.assign(e,
|
|
138
|
-
const
|
|
139
|
-
for (const n in
|
|
117
|
+
for (const [n, s] of Object.entries(this.options.rules))
|
|
118
|
+
t.matches(n) && Object.assign(e, s);
|
|
119
|
+
const i = t.dataset;
|
|
120
|
+
for (const n in i)
|
|
140
121
|
if (n.startsWith("supermouse")) {
|
|
141
|
-
const
|
|
142
|
-
if (
|
|
143
|
-
const
|
|
144
|
-
e[
|
|
122
|
+
const s = n.slice(10);
|
|
123
|
+
if (s) {
|
|
124
|
+
const r = s.charAt(0).toLowerCase() + s.slice(1), a = i[n];
|
|
125
|
+
a !== void 0 && (e[r] = a === "" ? !0 : a);
|
|
145
126
|
}
|
|
146
127
|
}
|
|
147
|
-
this.
|
|
128
|
+
this.state.interaction = e;
|
|
148
129
|
}
|
|
149
|
-
|
|
150
|
-
// Unified Pointer Event Handler
|
|
151
|
-
handleMove = (t) => {
|
|
130
|
+
handleMove(t) {
|
|
152
131
|
if (!this.isEnabled || this.options.autoDisableOnMobile && t.pointerType === "touch") return;
|
|
153
|
-
let e = t.clientX,
|
|
132
|
+
let e = t.clientX, i = t.clientY;
|
|
154
133
|
if (this.options.container && this.options.container !== document.body) {
|
|
155
134
|
const n = this.options.container.getBoundingClientRect();
|
|
156
|
-
e -= n.left,
|
|
135
|
+
e -= n.left, i -= n.top;
|
|
157
136
|
}
|
|
158
|
-
this.state.pointer.x = e, this.state.pointer.y =
|
|
159
|
-
}
|
|
160
|
-
handleDown
|
|
137
|
+
this.state.pointer.x = e, this.state.pointer.y = i, this.state.hasReceivedInput || (this.state.hasReceivedInput = !0, this.state.target.x = this.state.smooth.x = e, this.state.target.y = this.state.smooth.y = i);
|
|
138
|
+
}
|
|
139
|
+
handleDown() {
|
|
161
140
|
this.isEnabled && (this.state.isDown = !0);
|
|
162
|
-
}
|
|
163
|
-
handleUp
|
|
141
|
+
}
|
|
142
|
+
handleUp() {
|
|
164
143
|
this.isEnabled && (this.state.isDown = !1);
|
|
165
|
-
}
|
|
166
|
-
handleMouseOver
|
|
144
|
+
}
|
|
145
|
+
handleMouseOver(t) {
|
|
167
146
|
if (!this.isEnabled) return;
|
|
168
147
|
const e = t.target;
|
|
169
148
|
if (e.closest("[data-supermouse-ignore]")) {
|
|
170
149
|
this.state.isNative = !0;
|
|
171
150
|
return;
|
|
172
151
|
}
|
|
173
|
-
const
|
|
152
|
+
const i = this.getHoverSelector(), n = e.closest(i);
|
|
174
153
|
n && (this.state.isHover = !0, this.state.hoverTarget = n, this.parseDOMInteraction(this.state.hoverTarget));
|
|
175
|
-
const
|
|
176
|
-
if (
|
|
177
|
-
const
|
|
154
|
+
const s = this.options.ignoreOnNative;
|
|
155
|
+
if (s) {
|
|
156
|
+
const r = s === !0 || s === "auto" || s === "tag", a = s === !0 || s === "auto" || s === "css";
|
|
178
157
|
let h = !1;
|
|
179
|
-
if (
|
|
180
|
-
const
|
|
181
|
-
(
|
|
158
|
+
if (r) {
|
|
159
|
+
const l = e.localName;
|
|
160
|
+
(l === "input" || l === "textarea" || l === "select" || e.isContentEditable) && (h = !0);
|
|
182
161
|
}
|
|
183
|
-
if (!h &&
|
|
184
|
-
const
|
|
185
|
-
["default", "auto", "pointer", "none", "inherit"].includes(
|
|
162
|
+
if (!h && a) {
|
|
163
|
+
const l = window.getComputedStyle(e).cursor;
|
|
164
|
+
["default", "auto", "pointer", "none", "inherit"].includes(l) || (h = !0);
|
|
186
165
|
}
|
|
187
166
|
h && (this.state.isNative = !0);
|
|
188
167
|
}
|
|
189
|
-
}
|
|
190
|
-
handleMouseOut
|
|
168
|
+
}
|
|
169
|
+
handleMouseOut(t) {
|
|
191
170
|
if (!this.isEnabled) return;
|
|
192
171
|
const e = t.target;
|
|
193
172
|
(e === this.state.hoverTarget || e.contains(this.state.hoverTarget)) && (!t.relatedTarget || !this.state.hoverTarget?.contains(t.relatedTarget)) && (this.state.isHover = !1, this.state.hoverTarget = null, this.state.interaction = {}), this.state.isNative && (this.state.isNative = !1);
|
|
194
|
-
}
|
|
195
|
-
handleWindowLeave
|
|
173
|
+
}
|
|
174
|
+
handleWindowLeave() {
|
|
196
175
|
this.options.hideOnLeave && (this.state.hasReceivedInput = !1);
|
|
197
|
-
}
|
|
176
|
+
}
|
|
177
|
+
clearHover() {
|
|
178
|
+
this.state.isHover = !1, this.state.hoverTarget = null, this.state.isNative = !1;
|
|
179
|
+
}
|
|
198
180
|
bindEvents() {
|
|
199
|
-
window.addEventListener("pointermove", this.handleMove, { passive: !0 }), window.addEventListener("pointerdown", this.handleDown, { passive: !0 }), window.addEventListener("pointerup", this.handleUp), document.addEventListener("mouseover", this.handleMouseOver), document.addEventListener("mouseout", this.handleMouseOut), document.addEventListener("mouseleave", this.handleWindowLeave);
|
|
181
|
+
window.addEventListener("pointermove", this.handleMove.bind(this), { passive: !0 }), window.addEventListener("pointerdown", this.handleDown.bind(this), { passive: !0 }), window.addEventListener("pointerup", this.handleUp.bind(this)), document.addEventListener("mouseover", this.handleMouseOver.bind(this)), document.addEventListener("mouseout", this.handleMouseOut.bind(this)), document.addEventListener("mouseleave", this.handleWindowLeave.bind(this));
|
|
200
182
|
}
|
|
201
183
|
destroy() {
|
|
202
|
-
this.mediaQueryList && this.mediaQueryHandler && this.mediaQueryList.removeEventListener("change", this.mediaQueryHandler), window.removeEventListener("pointermove", this.handleMove), window.removeEventListener("pointerdown", this.handleDown), window.removeEventListener("pointerup", this.handleUp), document.removeEventListener("mouseover", this.handleMouseOver), document.removeEventListener("mouseout", this.handleMouseOut), document.removeEventListener("mouseleave", this.handleWindowLeave);
|
|
184
|
+
this.mediaQueryList && this.mediaQueryHandler && this.mediaQueryList.removeEventListener("change", this.mediaQueryHandler), this.motionQuery && (this.motionQuery.onchange = null), window.removeEventListener("pointermove", this.handleMove), window.removeEventListener("pointerdown", this.handleDown), window.removeEventListener("pointerup", this.handleUp), document.removeEventListener("mouseover", this.handleMouseOver), document.removeEventListener("mouseout", this.handleMouseOut), document.removeEventListener("mouseleave", this.handleWindowLeave);
|
|
203
185
|
}
|
|
204
186
|
}
|
|
205
187
|
function v(o, t, e) {
|
|
206
188
|
return o + (t - o) * e;
|
|
207
189
|
}
|
|
208
|
-
function u(o, t, e,
|
|
209
|
-
return v(o, t, 1 - Math.exp(-e *
|
|
190
|
+
function u(o, t, e, i) {
|
|
191
|
+
return v(o, t, 1 - Math.exp(-e * i));
|
|
210
192
|
}
|
|
211
193
|
function f(o, t) {
|
|
212
194
|
return Math.atan2(t, o) * (180 / Math.PI);
|
|
@@ -220,34 +202,15 @@ const g = [
|
|
|
220
202
|
"[data-cursor]"
|
|
221
203
|
];
|
|
222
204
|
class y {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
version = "2.0.4";
|
|
226
|
-
/**
|
|
227
|
-
* The Single Source of Truth.
|
|
228
|
-
*
|
|
229
|
-
* This object is shared by reference. `Input` writes to it; `Supermouse` physics reads/writes to it;
|
|
230
|
-
* Plugins read/write to it.
|
|
231
|
-
*/
|
|
205
|
+
static version = "2.1.0";
|
|
206
|
+
version = "2.1.0";
|
|
232
207
|
state;
|
|
233
208
|
/**
|
|
234
209
|
* Configuration options.
|
|
235
210
|
*/
|
|
236
211
|
options;
|
|
237
|
-
/**
|
|
238
|
-
* Registry of active plugins.
|
|
239
|
-
* @internal Use `use()`, `enablePlugin()`, or `disablePlugin()` to interact with this.
|
|
240
|
-
*/
|
|
241
212
|
plugins = [];
|
|
242
|
-
/**
|
|
243
|
-
* The Stage System responsible for the DOM container and CSS injection.
|
|
244
|
-
* @internal
|
|
245
|
-
*/
|
|
246
213
|
stage;
|
|
247
|
-
/**
|
|
248
|
-
* The Input System responsible for event listeners.
|
|
249
|
-
* @internal
|
|
250
|
-
*/
|
|
251
214
|
input;
|
|
252
215
|
rafId = 0;
|
|
253
216
|
lastTime = 0;
|
|
@@ -255,7 +218,7 @@ class y {
|
|
|
255
218
|
hoverSelectors;
|
|
256
219
|
/**
|
|
257
220
|
* Creates a new Supermouse instance.
|
|
258
|
-
*
|
|
221
|
+
*
|
|
259
222
|
* @param options - Global configuration options.
|
|
260
223
|
* @throws Will throw if running in a non-browser environment (window/document undefined).
|
|
261
224
|
*/
|
|
@@ -329,20 +292,10 @@ class y {
|
|
|
329
292
|
const e = this.getPlugin(t);
|
|
330
293
|
e && (e.isEnabled === !1 ? this.enablePlugin(t) : this.disablePlugin(t));
|
|
331
294
|
}
|
|
332
|
-
/**
|
|
333
|
-
* Registers a CSS selector as an "Interactive Target".
|
|
334
|
-
*
|
|
335
|
-
* When the mouse hovers over an element matching this selector:
|
|
336
|
-
* 1. `state.isHover` becomes `true`.
|
|
337
|
-
* 2. `state.hoverTarget` is set to the element.
|
|
338
|
-
* 3. The `Stage` system injects CSS to hide the native cursor for this element (if `hideCursor: true`).
|
|
339
|
-
*
|
|
340
|
-
* @param selector - A valid CSS selector string (e.g., `.my-button`, `[data-trigger]`).
|
|
341
|
-
*/
|
|
342
295
|
registerHoverTarget(t) {
|
|
343
296
|
this.hoverSelectors.has(t) || (this.hoverSelectors.add(t), this.stage.addSelector(t));
|
|
344
297
|
}
|
|
345
|
-
/**
|
|
298
|
+
/**
|
|
346
299
|
* The fixed container element where plugins should append their DOM nodes.
|
|
347
300
|
*/
|
|
348
301
|
get container() {
|
|
@@ -350,8 +303,7 @@ class y {
|
|
|
350
303
|
}
|
|
351
304
|
/**
|
|
352
305
|
* Manually override the native cursor visibility.
|
|
353
|
-
*
|
|
354
|
-
*
|
|
306
|
+
*
|
|
355
307
|
* @param type 'auto' (Show Native), 'none' (Hide Native), or null (Resume Auto-detection)
|
|
356
308
|
*/
|
|
357
309
|
setCursor(t) {
|
|
@@ -360,32 +312,19 @@ class y {
|
|
|
360
312
|
init() {
|
|
361
313
|
this.options.autoStart && this.startLoop();
|
|
362
314
|
}
|
|
363
|
-
/**
|
|
364
|
-
* Starts the update loop and enables input listeners.
|
|
365
|
-
* Hides the native cursor if configured.
|
|
366
|
-
*/
|
|
367
315
|
enable() {
|
|
368
316
|
this.input.isEnabled = !0, this.stage.setNativeCursor("none");
|
|
369
317
|
}
|
|
370
|
-
/**
|
|
371
|
-
* Stops the update loop, disables listeners, and restores the native cursor.
|
|
372
|
-
* Resets internal state positions to off-screen.
|
|
373
|
-
*/
|
|
374
318
|
disable() {
|
|
375
319
|
this.input.isEnabled = !1, this.stage.setNativeCursor("auto"), this.resetPosition();
|
|
376
320
|
}
|
|
377
321
|
/**
|
|
378
322
|
* Registers a new plugin.
|
|
379
|
-
*
|
|
380
|
-
* @remarks
|
|
381
|
-
* Plugins are sorted by `priority` immediately after registration.
|
|
382
|
-
* - **Negative Priority (< 0)**: Logic plugins (run before physics).
|
|
383
|
-
* - **Positive Priority (>= 0)**: Visual plugins (run after physics).
|
|
384
|
-
*
|
|
323
|
+
*
|
|
385
324
|
* @param plugin - The plugin object to install.
|
|
386
325
|
*/
|
|
387
326
|
use(t) {
|
|
388
|
-
return this.plugins.find((e) => e.name === t.name) ? (console.warn(`[Supermouse] Plugin "${t.name}" already installed.`), this) : (t.isEnabled === void 0 && (t.isEnabled = !0), this.plugins.push(t), this.plugins.sort((e,
|
|
327
|
+
return this.plugins.find((e) => e.name === t.name) ? (console.warn(`[Supermouse] Plugin "${t.name}" already installed.`), this) : (t.isEnabled === void 0 && (t.isEnabled = !0), this.plugins.push(t), this.plugins.sort((e, i) => (e.priority || 0) - (i.priority || 0)), t.install?.(this), this);
|
|
389
328
|
}
|
|
390
329
|
resetPosition() {
|
|
391
330
|
const t = { x: -100, y: -100 };
|
|
@@ -396,9 +335,7 @@ class y {
|
|
|
396
335
|
}
|
|
397
336
|
/**
|
|
398
337
|
* Manually steps the animation loop.
|
|
399
|
-
*
|
|
400
|
-
* you want to disable the internal RAF and drive `Supermouse` from your own ticker.
|
|
401
|
-
*
|
|
338
|
+
*
|
|
402
339
|
* @param time Current timestamp in milliseconds.
|
|
403
340
|
*/
|
|
404
341
|
step(t) {
|
|
@@ -408,40 +345,42 @@ class y {
|
|
|
408
345
|
if (t.isEnabled !== !1)
|
|
409
346
|
try {
|
|
410
347
|
t.update?.(this, e);
|
|
411
|
-
} catch (
|
|
412
|
-
console.error(`[Supermouse] Plugin '${t.name}' crashed and has been disabled.`,
|
|
348
|
+
} catch (i) {
|
|
349
|
+
console.error(`[Supermouse] Plugin '${t.name}' crashed and has been disabled.`, i), t.isEnabled = !1;
|
|
350
|
+
try {
|
|
351
|
+
t.onDisable?.(this);
|
|
352
|
+
} catch {
|
|
353
|
+
}
|
|
413
354
|
}
|
|
414
355
|
}
|
|
415
356
|
/**
|
|
416
|
-
* The Heartbeat.
|
|
417
357
|
* Runs on every animation frame.
|
|
418
358
|
*/
|
|
419
359
|
tick = (t) => {
|
|
420
|
-
const e = t - this.lastTime,
|
|
421
|
-
this.lastTime = t;
|
|
360
|
+
const e = t - this.lastTime, i = Math.min(e / 1e3, 0.1);
|
|
361
|
+
this.lastTime = t, this.state.hoverTarget && !this.state.hoverTarget.isConnected && this.input.clearHover();
|
|
422
362
|
const n = this.input.isEnabled && !this.state.isNative && this.state.hasReceivedInput;
|
|
423
363
|
if (this.stage.setVisibility(n), this.input.isEnabled && this.options.hideCursor) {
|
|
424
|
-
let
|
|
425
|
-
this.state.forcedCursor !== null ?
|
|
364
|
+
let s = "auto";
|
|
365
|
+
this.state.forcedCursor !== null ? s = this.state.forcedCursor : s = this.state.isNative || !this.state.hasReceivedInput ? "auto" : "none", this.stage.setNativeCursor(s);
|
|
426
366
|
}
|
|
427
367
|
if (this.input.isEnabled) {
|
|
428
368
|
this.state.target.x = this.state.pointer.x, this.state.target.y = this.state.pointer.y;
|
|
429
|
-
for (let
|
|
430
|
-
this.runPluginSafe(this.plugins[
|
|
431
|
-
const
|
|
432
|
-
this.state.smooth.x = u(this.state.smooth.x, this.state.target.x,
|
|
433
|
-
const r = this.state.target.x - this.state.smooth.x,
|
|
434
|
-
this.state.velocity.x = r, this.state.velocity.y =
|
|
369
|
+
for (let h = 0; h < this.plugins.length; h++)
|
|
370
|
+
this.runPluginSafe(this.plugins[h], e);
|
|
371
|
+
const s = this.state.reducedMotion ? 1e3 : 1 / this.options.smoothness * 2;
|
|
372
|
+
this.state.smooth.x = u(this.state.smooth.x, this.state.target.x, s, i), this.state.smooth.y = u(this.state.smooth.y, this.state.target.y, s, i);
|
|
373
|
+
const r = this.state.target.x - this.state.smooth.x, a = this.state.target.y - this.state.smooth.y;
|
|
374
|
+
this.state.velocity.x = r, this.state.velocity.y = a, (Math.abs(r) > 0.1 || Math.abs(a) > 0.1) && (this.state.angle = f(r, a));
|
|
435
375
|
} else {
|
|
436
376
|
this.state.smooth.x = -100, this.state.smooth.y = -100, this.state.pointer.x = -100, this.state.pointer.y = -100, this.state.velocity.x = 0, this.state.velocity.y = 0;
|
|
437
|
-
for (let
|
|
438
|
-
this.runPluginSafe(this.plugins[
|
|
377
|
+
for (let s = 0; s < this.plugins.length; s++)
|
|
378
|
+
this.runPluginSafe(this.plugins[s], e);
|
|
439
379
|
}
|
|
440
380
|
this.options.autoStart && this.isRunning && (this.rafId = requestAnimationFrame(this.tick));
|
|
441
381
|
};
|
|
442
|
-
/**
|
|
382
|
+
/**
|
|
443
383
|
* Destroys the instance.
|
|
444
|
-
* Stops the loop, removes all DOM elements, removes all event listeners, and calls destroy on all plugins.
|
|
445
384
|
*/
|
|
446
385
|
destroy() {
|
|
447
386
|
this.isRunning = !1, cancelAnimationFrame(this.rafId), this.input.destroy(), this.stage.destroy(), this.plugins.forEach((t) => t.destroy?.(this)), this.plugins = [];
|
package/dist/index.umd.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
(function(u
|
|
1
|
+
(function(l,u){typeof exports=="object"&&typeof module<"u"?u(exports):typeof define=="function"&&define.amd?define(["exports"],u):(l=typeof globalThis<"u"?globalThis:l||self,u(l.SupermouseCore={}))})(this,(function(l){"use strict";let u=0;class f{constructor(t=document.body,e){if(this.container=t,this.hideNativeCursor=e,!t||!(t instanceof HTMLElement))throw new Error(`[Supermouse] Invalid container: ${t}. Must be an HTMLElement.`);const i=u++;this.id=`supermouse-style-${i}`,this.scopeClass=`supermouse-scope-${i}`;const n=t===document.body;this.element=document.createElement("div"),Object.assign(this.element.style,{position:n?"fixed":"absolute",top:"0",left:"0",width:"100%",height:"100%",pointerEvents:"none",zIndex:"9999",opacity:"1",transition:"opacity 0.15s ease"}),n||window.getComputedStyle(t).position==="static"&&(t.style.position="relative"),t.appendChild(this.element),this.styleTag=document.createElement("style"),this.styleTag.id=this.id,document.head.appendChild(this.styleTag),this.container.classList.add(this.scopeClass),this.hideNativeCursor&&this.setNativeCursor("none")}container;hideNativeCursor;element;styleTag;id;scopeClass;currentCursorState=null;selectors=new Set(["a","button","input","textarea","select",'[role="button"]',"[tabindex]"]);addSelector(t){this.selectors.add(t),this.hideNativeCursor&&this.updateCursorCSS()}setVisibility(t){this.element.style.opacity=t?"1":"0"}setNativeCursor(t){!this.hideNativeCursor&&t==="none"||t!==this.currentCursorState&&(this.currentCursorState=t,t==="none"?(this.container.style.cursor="none",this.updateCursorCSS()):(this.container.style.cursor="",this.styleTag.innerText=""))}updateCursorCSS(){const t=Array.from(this.selectors);if(t.length===0){this.styleTag.innerText="";return}const e=t.map(i=>`.${this.scopeClass} ${i}`).join(", ");this.styleTag.innerText=`
|
|
2
2
|
${e} {
|
|
3
3
|
cursor: none !important;
|
|
4
4
|
}
|
|
5
|
-
`}destroy(){this.element.remove(),this.styleTag.remove(),this.container.style.cursor="",this.container.classList.remove(this.scopeClass)}}class v{constructor(t,e,
|
|
5
|
+
`}destroy(){this.element.remove(),this.styleTag.remove(),this.container.style.cursor="",this.container.classList.remove(this.scopeClass)}}class v{constructor(t,e,i,n){this.state=t,this.options=e,this.getHoverSelector=i,this.onEnableChange=n,this.checkDeviceCapability(),this.checkMotionPreference(),this.bindEvents()}state;options;getHoverSelector;onEnableChange;mediaQueryList;mediaQueryHandler;motionQuery;isEnabled=!0;checkDeviceCapability(){this.options.autoDisableOnMobile&&(this.mediaQueryList=window.matchMedia("(pointer: fine)"),this.updateEnabledState(this.mediaQueryList.matches),this.mediaQueryHandler=t=>{this.updateEnabledState(t.matches)},this.mediaQueryList.addEventListener("change",this.mediaQueryHandler))}checkMotionPreference(){this.motionQuery=window.matchMedia("(prefers-reduced-motion: reduce)"),this.state.reducedMotion=this.motionQuery.matches,this.motionQuery.addEventListener("change",t=>{this.state.reducedMotion=t.matches})}updateEnabledState(t){this.isEnabled=t,this.onEnableChange(t)}parseDOMInteraction(t){if(this.options.resolveInteraction){this.state.interaction=this.options.resolveInteraction(t);return}const e={};if(this.options.rules)for(const[n,s]of Object.entries(this.options.rules))t.matches(n)&&Object.assign(e,s);const i=t.dataset;for(const n in i)if(n.startsWith("supermouse")){const s=n.slice(10);if(s){const r=s.charAt(0).toLowerCase()+s.slice(1),a=i[n];a!==void 0&&(e[r]=a===""?!0:a)}}this.state.interaction=e}handleMove(t){if(!this.isEnabled||this.options.autoDisableOnMobile&&t.pointerType==="touch")return;let e=t.clientX,i=t.clientY;if(this.options.container&&this.options.container!==document.body){const n=this.options.container.getBoundingClientRect();e-=n.left,i-=n.top}this.state.pointer.x=e,this.state.pointer.y=i,this.state.hasReceivedInput||(this.state.hasReceivedInput=!0,this.state.target.x=this.state.smooth.x=e,this.state.target.y=this.state.smooth.y=i)}handleDown(){this.isEnabled&&(this.state.isDown=!0)}handleUp(){this.isEnabled&&(this.state.isDown=!1)}handleMouseOver(t){if(!this.isEnabled)return;const e=t.target;if(e.closest("[data-supermouse-ignore]")){this.state.isNative=!0;return}const i=this.getHoverSelector(),n=e.closest(i);n&&(this.state.isHover=!0,this.state.hoverTarget=n,this.parseDOMInteraction(this.state.hoverTarget));const s=this.options.ignoreOnNative;if(s){const r=s===!0||s==="auto"||s==="tag",a=s===!0||s==="auto"||s==="css";let h=!1;if(r){const d=e.localName;(d==="input"||d==="textarea"||d==="select"||e.isContentEditable)&&(h=!0)}if(!h&&a){const d=window.getComputedStyle(e).cursor;["default","auto","pointer","none","inherit"].includes(d)||(h=!0)}h&&(this.state.isNative=!0)}}handleMouseOut(t){if(!this.isEnabled)return;const e=t.target;(e===this.state.hoverTarget||e.contains(this.state.hoverTarget))&&(!t.relatedTarget||!this.state.hoverTarget?.contains(t.relatedTarget))&&(this.state.isHover=!1,this.state.hoverTarget=null,this.state.interaction={}),this.state.isNative&&(this.state.isNative=!1)}handleWindowLeave(){this.options.hideOnLeave&&(this.state.hasReceivedInput=!1)}clearHover(){this.state.isHover=!1,this.state.hoverTarget=null,this.state.isNative=!1}bindEvents(){window.addEventListener("pointermove",this.handleMove.bind(this),{passive:!0}),window.addEventListener("pointerdown",this.handleDown.bind(this),{passive:!0}),window.addEventListener("pointerup",this.handleUp.bind(this)),document.addEventListener("mouseover",this.handleMouseOver.bind(this)),document.addEventListener("mouseout",this.handleMouseOut.bind(this)),document.addEventListener("mouseleave",this.handleWindowLeave.bind(this))}destroy(){this.mediaQueryList&&this.mediaQueryHandler&&this.mediaQueryList.removeEventListener("change",this.mediaQueryHandler),this.motionQuery&&(this.motionQuery.onchange=null),window.removeEventListener("pointermove",this.handleMove),window.removeEventListener("pointerdown",this.handleDown),window.removeEventListener("pointerup",this.handleUp),document.removeEventListener("mouseover",this.handleMouseOver),document.removeEventListener("mouseout",this.handleMouseOut),document.removeEventListener("mouseleave",this.handleWindowLeave)}}function m(o,t,e){return o+(t-o)*e}function c(o,t,e,i){return m(o,t,1-Math.exp(-e*i))}function g(o,t){return Math.atan2(t,o)*(180/Math.PI)}const p=["a","button","input","textarea","[data-hover]","[data-cursor]"];class y{static version="2.1.0";version="2.1.0";state;options;plugins=[];stage;input;rafId=0;lastTime=0;isRunning=!1;hoverSelectors;constructor(t={}){this.options={smoothness:.15,enableTouch:!1,autoDisableOnMobile:!0,ignoreOnNative:"auto",hideCursor:!0,hideOnLeave:!0,autoStart:!0,container:document.body,...t},this.options.container||(this.options.container=document.body),this.state={pointer:{x:-100,y:-100},target:{x:-100,y:-100},smooth:{x:-100,y:-100},velocity:{x:0,y:0},angle:0,isDown:!1,isHover:!1,isNative:!1,forcedCursor:null,hoverTarget:null,reducedMotion:!1,hasReceivedInput:!1,shape:null,interaction:{}},this.options.hoverSelectors?this.hoverSelectors=new Set(this.options.hoverSelectors):this.hoverSelectors=new Set(p),this.stage=new f(this.options.container,!!this.options.hideCursor),this.hoverSelectors.forEach(e=>this.stage.addSelector(e)),this.input=new v(this.state,this.options,()=>Array.from(this.hoverSelectors).join(", "),e=>{e||this.resetPosition()}),this.options.plugins&&this.options.plugins.forEach(e=>this.use(e)),this.init()}getPlugin(t){return this.plugins.find(e=>e.name===t)}get isEnabled(){return this.input.isEnabled}enablePlugin(t){const e=this.getPlugin(t);e&&e.isEnabled===!1&&(e.isEnabled=!0,e.onEnable?.(this))}disablePlugin(t){const e=this.getPlugin(t);e&&e.isEnabled!==!1&&(e.isEnabled=!1,e.onDisable?.(this))}togglePlugin(t){const e=this.getPlugin(t);e&&(e.isEnabled===!1?this.enablePlugin(t):this.disablePlugin(t))}registerHoverTarget(t){this.hoverSelectors.has(t)||(this.hoverSelectors.add(t),this.stage.addSelector(t))}get container(){return this.stage.element}setCursor(t){this.state.forcedCursor=t}init(){this.options.autoStart&&this.startLoop()}enable(){this.input.isEnabled=!0,this.stage.setNativeCursor("none")}disable(){this.input.isEnabled=!1,this.stage.setNativeCursor("auto"),this.resetPosition()}use(t){return this.plugins.find(e=>e.name===t.name)?(console.warn(`[Supermouse] Plugin "${t.name}" already installed.`),this):(t.isEnabled===void 0&&(t.isEnabled=!0),this.plugins.push(t),this.plugins.sort((e,i)=>(e.priority||0)-(i.priority||0)),t.install?.(this),this)}resetPosition(){const t={x:-100,y:-100};this.state.pointer={...t},this.state.target={...t},this.state.smooth={...t},this.state.velocity={x:0,y:0},this.state.angle=0,this.state.hasReceivedInput=!1,this.state.shape=null,this.state.interaction={}}startLoop(){this.isRunning||(this.isRunning=!0,this.lastTime=performance.now(),this.tick(this.lastTime))}step(t){this.tick(t)}runPluginSafe(t,e){if(t.isEnabled!==!1)try{t.update?.(this,e)}catch(i){console.error(`[Supermouse] Plugin '${t.name}' crashed and has been disabled.`,i),t.isEnabled=!1;try{t.onDisable?.(this)}catch{}}}tick=t=>{const e=t-this.lastTime,i=Math.min(e/1e3,.1);this.lastTime=t,this.state.hoverTarget&&!this.state.hoverTarget.isConnected&&this.input.clearHover();const n=this.input.isEnabled&&!this.state.isNative&&this.state.hasReceivedInput;if(this.stage.setVisibility(n),this.input.isEnabled&&this.options.hideCursor){let s="auto";this.state.forcedCursor!==null?s=this.state.forcedCursor:s=this.state.isNative||!this.state.hasReceivedInput?"auto":"none",this.stage.setNativeCursor(s)}if(this.input.isEnabled){this.state.target.x=this.state.pointer.x,this.state.target.y=this.state.pointer.y;for(let h=0;h<this.plugins.length;h++)this.runPluginSafe(this.plugins[h],e);const s=this.state.reducedMotion?1e3:1/this.options.smoothness*2;this.state.smooth.x=c(this.state.smooth.x,this.state.target.x,s,i),this.state.smooth.y=c(this.state.smooth.y,this.state.target.y,s,i);const r=this.state.target.x-this.state.smooth.x,a=this.state.target.y-this.state.smooth.y;this.state.velocity.x=r,this.state.velocity.y=a,(Math.abs(r)>.1||Math.abs(a)>.1)&&(this.state.angle=g(r,a))}else{this.state.smooth.x=-100,this.state.smooth.y=-100,this.state.pointer.x=-100,this.state.pointer.y=-100,this.state.velocity.x=0,this.state.velocity.y=0;for(let s=0;s<this.plugins.length;s++)this.runPluginSafe(this.plugins[s],e)}this.options.autoStart&&this.isRunning&&(this.rafId=requestAnimationFrame(this.tick))};destroy(){this.isRunning=!1,cancelAnimationFrame(this.rafId),this.input.destroy(),this.stage.destroy(),this.plugins.forEach(t=>t.destroy?.(this)),this.plugins=[]}}l.DEFAULT_HOVER_SELECTORS=p,l.Supermouse=y,Object.defineProperty(l,Symbol.toStringTag,{value:"Module"})}));
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@supermousejs/core",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
|
+
"description": "The core physics engine and state manager for Supermouse.",
|
|
4
5
|
"main": "dist/index.umd.js",
|
|
5
6
|
"module": "dist/index.mjs",
|
|
6
7
|
"types": "dist/index.d.ts",
|
|
@@ -20,6 +21,15 @@
|
|
|
20
21
|
"javascript",
|
|
21
22
|
"web"
|
|
22
23
|
],
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "git+https://github.com/Whitestar14/supermouse-js.git",
|
|
27
|
+
"directory": "packages/core"
|
|
28
|
+
},
|
|
29
|
+
"bugs": {
|
|
30
|
+
"url": "https://github.com/Whitestar14/supermouse-js/issues"
|
|
31
|
+
},
|
|
32
|
+
"homepage": "https://github.com/Whitestar14/supermouse-js/tree/main/packages/core#readme",
|
|
23
33
|
"dependencies": {},
|
|
24
34
|
"devDependencies": {},
|
|
25
35
|
"exports": {
|