@supermousejs/core 2.0.5 → 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 -37
- package/LICENSE.md +21 -21
- package/README.md +30 -31
- package/dist/index.d.ts +8 -55
- package/dist/index.mjs +84 -145
- package/dist/index.umd.js +2 -2
- package/package.json +1 -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 -155
- package/src/systems/index.ts +2 -2
- package/src/types.ts +168 -168
- package/src/utils/math.ts +11 -20
- package/tsconfig.json +7 -14
- package/tsconfig.tsbuildinfo +1 -0
- package/vite.config.ts +32 -32
package/src/systems/Stage.ts
CHANGED
|
@@ -1,155 +1,126 @@
|
|
|
1
|
-
let stageCount = 0;
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
this.
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
private updateCursorCSS() {
|
|
130
|
-
const rawSelectors = Array.from(this.selectors);
|
|
131
|
-
if (rawSelectors.length === 0) {
|
|
132
|
-
this.styleTag.innerText = '';
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// Scoped Selector Logic:
|
|
137
|
-
// We prepend the scope class to every selector to ensure we don't bleed into
|
|
138
|
-
// other parts of the page if the user is using a specific container.
|
|
139
|
-
// e.g. .supermouse-scope-0 a, .supermouse-scope-0 button { ... }
|
|
140
|
-
const scopedSelectors = rawSelectors.map(s => `.${this.scopeClass} ${s}`).join(', ');
|
|
141
|
-
|
|
142
|
-
this.styleTag.innerText = `
|
|
143
|
-
${scopedSelectors} {
|
|
144
|
-
cursor: none !important;
|
|
145
|
-
}
|
|
146
|
-
`;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
public destroy() {
|
|
150
|
-
this.element.remove();
|
|
151
|
-
this.styleTag.remove();
|
|
152
|
-
this.container.style.cursor = '';
|
|
153
|
-
this.container.classList.remove(this.scopeClass);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
1
|
+
let stageCount = 0;
|
|
2
|
+
|
|
3
|
+
export class Stage {
|
|
4
|
+
/** The container element appended to the document. */
|
|
5
|
+
public readonly element: HTMLDivElement;
|
|
6
|
+
private styleTag: HTMLStyleElement;
|
|
7
|
+
private id: string;
|
|
8
|
+
private scopeClass: string;
|
|
9
|
+
|
|
10
|
+
private currentCursorState: "none" | "auto" | "" | null = null;
|
|
11
|
+
|
|
12
|
+
private selectors: Set<string> = new Set([
|
|
13
|
+
"a",
|
|
14
|
+
"button",
|
|
15
|
+
"input",
|
|
16
|
+
"textarea",
|
|
17
|
+
"select",
|
|
18
|
+
'[role="button"]',
|
|
19
|
+
"[tabindex]"
|
|
20
|
+
]);
|
|
21
|
+
|
|
22
|
+
constructor(
|
|
23
|
+
private container: HTMLElement = document.body,
|
|
24
|
+
private hideNativeCursor: boolean
|
|
25
|
+
) {
|
|
26
|
+
if (!container || !(container instanceof HTMLElement)) {
|
|
27
|
+
throw new Error(`[Supermouse] Invalid container: ${container}. Must be an HTMLElement.`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const instanceId = stageCount++;
|
|
31
|
+
this.id = `supermouse-style-${instanceId}`;
|
|
32
|
+
this.scopeClass = `supermouse-scope-${instanceId}`;
|
|
33
|
+
|
|
34
|
+
const isBody = container === document.body;
|
|
35
|
+
|
|
36
|
+
this.element = document.createElement("div");
|
|
37
|
+
Object.assign(this.element.style, {
|
|
38
|
+
position: isBody ? "fixed" : "absolute",
|
|
39
|
+
top: "0",
|
|
40
|
+
left: "0",
|
|
41
|
+
width: "100%",
|
|
42
|
+
height: "100%",
|
|
43
|
+
pointerEvents: "none",
|
|
44
|
+
zIndex: "9999",
|
|
45
|
+
opacity: "1",
|
|
46
|
+
transition: "opacity 0.15s ease"
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
if (!isBody) {
|
|
50
|
+
const computed = window.getComputedStyle(container);
|
|
51
|
+
if (computed.position === "static") {
|
|
52
|
+
container.style.position = "relative";
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
container.appendChild(this.element);
|
|
57
|
+
|
|
58
|
+
this.styleTag = document.createElement("style");
|
|
59
|
+
this.styleTag.id = this.id;
|
|
60
|
+
document.head.appendChild(this.styleTag);
|
|
61
|
+
|
|
62
|
+
this.container.classList.add(this.scopeClass);
|
|
63
|
+
|
|
64
|
+
if (this.hideNativeCursor) {
|
|
65
|
+
this.setNativeCursor("none");
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Adds a new CSS selector to the Hide Native Cursor list.
|
|
71
|
+
* Called by `Supermouse` (and subsequently plugins) during install to ensure
|
|
72
|
+
* the native cursor is hidden on their specific interactive targets.
|
|
73
|
+
*/
|
|
74
|
+
public addSelector(selector: string) {
|
|
75
|
+
this.selectors.add(selector);
|
|
76
|
+
if (this.hideNativeCursor) {
|
|
77
|
+
this.updateCursorCSS();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
public setVisibility(visible: boolean) {
|
|
82
|
+
this.element.style.opacity = visible ? "1" : "0";
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Toggles the visibility of the native cursor via CSS injection.
|
|
87
|
+
* @param type 'none' to hide, 'auto' to show.
|
|
88
|
+
*/
|
|
89
|
+
public setNativeCursor(type: "none" | "auto" | "") {
|
|
90
|
+
if (!this.hideNativeCursor && type === "none") return;
|
|
91
|
+
|
|
92
|
+
if (type === this.currentCursorState) return;
|
|
93
|
+
this.currentCursorState = type;
|
|
94
|
+
|
|
95
|
+
if (type === "none") {
|
|
96
|
+
this.container.style.cursor = "none";
|
|
97
|
+
this.updateCursorCSS();
|
|
98
|
+
} else {
|
|
99
|
+
this.container.style.cursor = "";
|
|
100
|
+
this.styleTag.innerText = "";
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
private updateCursorCSS() {
|
|
105
|
+
const rawSelectors = Array.from(this.selectors);
|
|
106
|
+
if (rawSelectors.length === 0) {
|
|
107
|
+
this.styleTag.innerText = "";
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const scopedSelectors = rawSelectors.map((s) => `.${this.scopeClass} ${s}`).join(", ");
|
|
112
|
+
|
|
113
|
+
this.styleTag.innerText = `
|
|
114
|
+
${scopedSelectors} {
|
|
115
|
+
cursor: none !important;
|
|
116
|
+
}
|
|
117
|
+
`;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
public destroy() {
|
|
121
|
+
this.element.remove();
|
|
122
|
+
this.styleTag.remove();
|
|
123
|
+
this.container.style.cursor = "";
|
|
124
|
+
this.container.classList.remove(this.scopeClass);
|
|
125
|
+
}
|
|
126
|
+
}
|
package/src/systems/index.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export * from
|
|
2
|
-
export * from
|
|
1
|
+
export * from "./Stage";
|
|
2
|
+
export * from "./Input";
|
package/src/types.ts
CHANGED
|
@@ -1,168 +1,168 @@
|
|
|
1
|
-
import { Supermouse } from
|
|
2
|
-
|
|
3
|
-
export interface MousePosition {
|
|
4
|
-
x: number;
|
|
5
|
-
y: number;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export interface ShapeState {
|
|
9
|
-
width: number;
|
|
10
|
-
height: number;
|
|
11
|
-
borderRadius: number;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* The Interface for interaction state.
|
|
16
|
-
* Plugins should use Module Augmentation to add their specific properties to this interface.
|
|
17
|
-
*
|
|
18
|
-
* @example
|
|
19
|
-
* declare module '@supermousejs/core' {
|
|
20
|
-
* interface InteractionState {
|
|
21
|
-
* magnetic: boolean | number;
|
|
22
|
-
* text: string;
|
|
23
|
-
* }
|
|
24
|
-
* }
|
|
25
|
-
*/
|
|
26
|
-
export interface InteractionState {
|
|
27
|
-
/**
|
|
28
|
-
* Allow arbitrary keys for rapid prototyping.
|
|
29
|
-
* For type safety, use module augmentation to define expected keys.
|
|
30
|
-
*/
|
|
31
|
-
[key: string]: any;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export interface MouseState {
|
|
35
|
-
/** The raw position of the input pointer (mouse/touch). */
|
|
36
|
-
pointer: MousePosition;
|
|
37
|
-
/** The target position the cursor logic wants to reach. */
|
|
38
|
-
target: MousePosition;
|
|
39
|
-
/** The smoothed/interpolated position used for rendering. */
|
|
40
|
-
smooth: MousePosition;
|
|
41
|
-
/** The current velocity vector of the smooth position. */
|
|
42
|
-
velocity: MousePosition;
|
|
43
|
-
/** The angle of movement in degrees. Calculated from velocity. */
|
|
44
|
-
angle: number;
|
|
45
|
-
/** Whether the pointer is currently pressed down. */
|
|
46
|
-
isDown: boolean;
|
|
47
|
-
/** Whether the pointer is currently hovering over a registered interactive element. */
|
|
48
|
-
isHover: boolean;
|
|
49
|
-
/** Whether the native cursor is currently forced visible by internal logic (e.g. input elements). */
|
|
50
|
-
isNative: boolean;
|
|
51
|
-
/**
|
|
52
|
-
* If set, this overrides all auto-detection logic.
|
|
53
|
-
* 'auto' = Force Native Cursor (Show)
|
|
54
|
-
* 'none' = Force Custom Cursor (Hide Native)
|
|
55
|
-
* null = Let the Core decide based on isNative/isHover
|
|
56
|
-
*/
|
|
57
|
-
forcedCursor:
|
|
58
|
-
/** The DOM element currently being hovered, if any. */
|
|
59
|
-
hoverTarget: HTMLElement | null;
|
|
60
|
-
/** Whether the user has `prefers-reduced-motion` enabled. */
|
|
61
|
-
reducedMotion: boolean;
|
|
62
|
-
/** Whether the system has received valid input coordinates at least once. */
|
|
63
|
-
hasReceivedInput: boolean;
|
|
64
|
-
/** Defines a specific geometric shape the cursor should conform to. */
|
|
65
|
-
shape: ShapeState | null;
|
|
66
|
-
/** Centralized store for hover metadata from data attributes. */
|
|
67
|
-
interaction: InteractionState;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
export type NativeIgnoreStrategy =
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Configuration options passed to the Supermouse constructor.
|
|
74
|
-
*/
|
|
75
|
-
export interface SupermouseOptions {
|
|
76
|
-
/**
|
|
77
|
-
* The interpolation factor (0 to 1). Lower is smoother/slower.
|
|
78
|
-
* @default 0.15
|
|
79
|
-
*/
|
|
80
|
-
smoothness?: number;
|
|
81
|
-
/**
|
|
82
|
-
* List of CSS selectors that trigger the "Hover" state.
|
|
83
|
-
* Overrides the default set if provided.
|
|
84
|
-
*/
|
|
85
|
-
hoverSelectors?: string[];
|
|
86
|
-
/**
|
|
87
|
-
* Whether to enable custom cursor effects on touch devices.
|
|
88
|
-
* @default false
|
|
89
|
-
*/
|
|
90
|
-
enableTouch?: boolean;
|
|
91
|
-
/**
|
|
92
|
-
* Whether to automatically disable the custom cursor on devices with coarse pointers.
|
|
93
|
-
* @default true
|
|
94
|
-
*/
|
|
95
|
-
autoDisableOnMobile?: boolean;
|
|
96
|
-
/**
|
|
97
|
-
* Strategy for detecting when to fallback to the native cursor.
|
|
98
|
-
* - `true` / `'auto'`: Checks both HTML tags and CSS cursor styles (Accurate but slower).
|
|
99
|
-
* - `'tag'`: Checks only semantic tags like <input>, <textarea> (Fastest, prevents layout thrashing).
|
|
100
|
-
* - `'css'`: Checks only computed CSS cursor styles (Slow, triggers reflow).
|
|
101
|
-
* - `false`: Never fallback to native cursor.
|
|
102
|
-
* @default 'auto'
|
|
103
|
-
*/
|
|
104
|
-
ignoreOnNative?: boolean | NativeIgnoreStrategy;
|
|
105
|
-
/**
|
|
106
|
-
* Whether to hide the native cursor via global CSS injection.
|
|
107
|
-
* @default true
|
|
108
|
-
*/
|
|
109
|
-
hideCursor?: boolean;
|
|
110
|
-
/**
|
|
111
|
-
* Whether to hide the custom cursor when the mouse leaves the browser window.
|
|
112
|
-
* @default true
|
|
113
|
-
*/
|
|
114
|
-
hideOnLeave?: boolean;
|
|
115
|
-
/**
|
|
116
|
-
* List of plugins to initialize with the instance.
|
|
117
|
-
*/
|
|
118
|
-
plugins?: SupermousePlugin[];
|
|
119
|
-
/**
|
|
120
|
-
* The DOM element to append the cursor stage to.
|
|
121
|
-
* @default document.body
|
|
122
|
-
*/
|
|
123
|
-
container?: HTMLElement;
|
|
124
|
-
/**
|
|
125
|
-
* Whether to start the internal animation loop automatically.
|
|
126
|
-
* @default true
|
|
127
|
-
*/
|
|
128
|
-
autoStart?: boolean;
|
|
129
|
-
/**
|
|
130
|
-
* Semantic rules mapping CSS selectors to interaction state.
|
|
131
|
-
* @example { 'button': { icon: 'pointer' } }
|
|
132
|
-
*/
|
|
133
|
-
rules?: Record<string, InteractionState>;
|
|
134
|
-
/**
|
|
135
|
-
* Custom strategy to resolve interaction state from a hovered element.
|
|
136
|
-
* Overrides the default data-attribute scraping.
|
|
137
|
-
*/
|
|
138
|
-
resolveInteraction?: (target: HTMLElement) => InteractionState;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
*
|
|
143
|
-
*/
|
|
144
|
-
export type ValueOrGetter<T> = T | ((state: MouseState) => T);
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Interface for defining a Supermouse Plugin.
|
|
148
|
-
*/
|
|
149
|
-
export interface SupermousePlugin {
|
|
150
|
-
/** Unique name for the plugin. Used for toggling/retrieval. */
|
|
151
|
-
name: string;
|
|
152
|
-
/** Execution priority. Lower numbers run first. */
|
|
153
|
-
priority?: number;
|
|
154
|
-
/** If false, update() will not be called. */
|
|
155
|
-
isEnabled?: boolean;
|
|
156
|
-
|
|
157
|
-
/** Called when `app.use()` is executed. */
|
|
158
|
-
install?: (instance: Supermouse) => void;
|
|
159
|
-
/** Called on every animation frame. */
|
|
160
|
-
update?: (instance: Supermouse, deltaTime: number) => void;
|
|
161
|
-
/** Called when the plugin is removed or the app is destroyed. */
|
|
162
|
-
destroy?: (instance: Supermouse) => void;
|
|
163
|
-
|
|
164
|
-
/** Called when the plugin is enabled via .enablePlugin() */
|
|
165
|
-
onEnable?: (instance: Supermouse) => void;
|
|
166
|
-
/** Called when the plugin is disabled via .disablePlugin() */
|
|
167
|
-
onDisable?: (instance: Supermouse) => void;
|
|
168
|
-
}
|
|
1
|
+
import type { Supermouse } from "./Supermouse";
|
|
2
|
+
|
|
3
|
+
export interface MousePosition {
|
|
4
|
+
x: number;
|
|
5
|
+
y: number;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface ShapeState {
|
|
9
|
+
width: number;
|
|
10
|
+
height: number;
|
|
11
|
+
borderRadius: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The Interface for interaction state.
|
|
16
|
+
* Plugins should use Module Augmentation to add their specific properties to this interface.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* declare module '@supermousejs/core' {
|
|
20
|
+
* interface InteractionState {
|
|
21
|
+
* magnetic: boolean | number;
|
|
22
|
+
* text: string;
|
|
23
|
+
* }
|
|
24
|
+
* }
|
|
25
|
+
*/
|
|
26
|
+
export interface InteractionState {
|
|
27
|
+
/**
|
|
28
|
+
* Allow arbitrary keys for rapid prototyping.
|
|
29
|
+
* For type safety, use module augmentation to define expected keys.
|
|
30
|
+
*/
|
|
31
|
+
[key: string]: any;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface MouseState {
|
|
35
|
+
/** The raw position of the input pointer (mouse/touch). */
|
|
36
|
+
pointer: MousePosition;
|
|
37
|
+
/** The target position the cursor logic wants to reach. */
|
|
38
|
+
target: MousePosition;
|
|
39
|
+
/** The smoothed/interpolated position used for rendering. */
|
|
40
|
+
smooth: MousePosition;
|
|
41
|
+
/** The current velocity vector of the smooth position. */
|
|
42
|
+
velocity: MousePosition;
|
|
43
|
+
/** The angle of movement in degrees. Calculated from velocity. */
|
|
44
|
+
angle: number;
|
|
45
|
+
/** Whether the pointer is currently pressed down. */
|
|
46
|
+
isDown: boolean;
|
|
47
|
+
/** Whether the pointer is currently hovering over a registered interactive element. */
|
|
48
|
+
isHover: boolean;
|
|
49
|
+
/** Whether the native cursor is currently forced visible by internal logic (e.g. input elements). */
|
|
50
|
+
isNative: boolean;
|
|
51
|
+
/**
|
|
52
|
+
* If set, this overrides all auto-detection logic.
|
|
53
|
+
* 'auto' = Force Native Cursor (Show)
|
|
54
|
+
* 'none' = Force Custom Cursor (Hide Native)
|
|
55
|
+
* null = Let the Core decide based on isNative/isHover
|
|
56
|
+
*/
|
|
57
|
+
forcedCursor: "auto" | "none" | null;
|
|
58
|
+
/** The DOM element currently being hovered, if any. */
|
|
59
|
+
hoverTarget: HTMLElement | null;
|
|
60
|
+
/** Whether the user has `prefers-reduced-motion` enabled. */
|
|
61
|
+
reducedMotion: boolean;
|
|
62
|
+
/** Whether the system has received valid input coordinates at least once. */
|
|
63
|
+
hasReceivedInput: boolean;
|
|
64
|
+
/** Defines a specific geometric shape the cursor should conform to. */
|
|
65
|
+
shape: ShapeState | null;
|
|
66
|
+
/** Centralized store for hover metadata from data attributes. */
|
|
67
|
+
interaction: InteractionState;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export type NativeIgnoreStrategy = "auto" | "tag" | "css";
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Configuration options passed to the Supermouse constructor.
|
|
74
|
+
*/
|
|
75
|
+
export interface SupermouseOptions {
|
|
76
|
+
/**
|
|
77
|
+
* The interpolation factor (0 to 1). Lower is smoother/slower.
|
|
78
|
+
* @default 0.15
|
|
79
|
+
*/
|
|
80
|
+
smoothness?: number;
|
|
81
|
+
/**
|
|
82
|
+
* List of CSS selectors that trigger the "Hover" state.
|
|
83
|
+
* Overrides the default set if provided.
|
|
84
|
+
*/
|
|
85
|
+
hoverSelectors?: string[];
|
|
86
|
+
/**
|
|
87
|
+
* Whether to enable custom cursor effects on touch devices.
|
|
88
|
+
* @default false
|
|
89
|
+
*/
|
|
90
|
+
enableTouch?: boolean;
|
|
91
|
+
/**
|
|
92
|
+
* Whether to automatically disable the custom cursor on devices with coarse pointers.
|
|
93
|
+
* @default true
|
|
94
|
+
*/
|
|
95
|
+
autoDisableOnMobile?: boolean;
|
|
96
|
+
/**
|
|
97
|
+
* Strategy for detecting when to fallback to the native cursor.
|
|
98
|
+
* - `true` / `'auto'`: Checks both HTML tags and CSS cursor styles (Accurate but slower).
|
|
99
|
+
* - `'tag'`: Checks only semantic tags like <input>, <textarea> (Fastest, prevents layout thrashing).
|
|
100
|
+
* - `'css'`: Checks only computed CSS cursor styles (Slow, triggers reflow).
|
|
101
|
+
* - `false`: Never fallback to native cursor.
|
|
102
|
+
* @default 'auto'
|
|
103
|
+
*/
|
|
104
|
+
ignoreOnNative?: boolean | NativeIgnoreStrategy;
|
|
105
|
+
/**
|
|
106
|
+
* Whether to hide the native cursor via global CSS injection.
|
|
107
|
+
* @default true
|
|
108
|
+
*/
|
|
109
|
+
hideCursor?: boolean;
|
|
110
|
+
/**
|
|
111
|
+
* Whether to hide the custom cursor when the mouse leaves the browser window.
|
|
112
|
+
* @default true
|
|
113
|
+
*/
|
|
114
|
+
hideOnLeave?: boolean;
|
|
115
|
+
/**
|
|
116
|
+
* List of plugins to initialize with the instance.
|
|
117
|
+
*/
|
|
118
|
+
plugins?: SupermousePlugin[];
|
|
119
|
+
/**
|
|
120
|
+
* The DOM element to append the cursor stage to.
|
|
121
|
+
* @default document.body
|
|
122
|
+
*/
|
|
123
|
+
container?: HTMLElement;
|
|
124
|
+
/**
|
|
125
|
+
* Whether to start the internal animation loop automatically.
|
|
126
|
+
* @default true
|
|
127
|
+
*/
|
|
128
|
+
autoStart?: boolean;
|
|
129
|
+
/**
|
|
130
|
+
* Semantic rules mapping CSS selectors to interaction state.
|
|
131
|
+
* @example { 'button': { icon: 'pointer' } }
|
|
132
|
+
*/
|
|
133
|
+
rules?: Record<string, InteractionState>;
|
|
134
|
+
/**
|
|
135
|
+
* Custom strategy to resolve interaction state from a hovered element.
|
|
136
|
+
* Overrides the default data-attribute scraping.
|
|
137
|
+
*/
|
|
138
|
+
resolveInteraction?: (target: HTMLElement) => InteractionState;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Allows a property to be a static value or a function that returns the value based on state.
|
|
143
|
+
*/
|
|
144
|
+
export type ValueOrGetter<T> = T | ((state: MouseState) => T);
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Interface for defining a Supermouse Plugin.
|
|
148
|
+
*/
|
|
149
|
+
export interface SupermousePlugin {
|
|
150
|
+
/** Unique name for the plugin. Used for toggling/retrieval. */
|
|
151
|
+
name: string;
|
|
152
|
+
/** Execution priority. Lower numbers run first. */
|
|
153
|
+
priority?: number;
|
|
154
|
+
/** If false, update() will not be called. */
|
|
155
|
+
isEnabled?: boolean;
|
|
156
|
+
|
|
157
|
+
/** Called when `app.use()` is executed. */
|
|
158
|
+
install?: (instance: Supermouse) => void;
|
|
159
|
+
/** Called on every animation frame. */
|
|
160
|
+
update?: (instance: Supermouse, deltaTime: number) => void;
|
|
161
|
+
/** Called when the plugin is removed or the app is destroyed. */
|
|
162
|
+
destroy?: (instance: Supermouse) => void;
|
|
163
|
+
|
|
164
|
+
/** Called when the plugin is enabled via .enablePlugin() */
|
|
165
|
+
onEnable?: (instance: Supermouse) => void;
|
|
166
|
+
/** Called when the plugin is disabled via .disablePlugin() */
|
|
167
|
+
onDisable?: (instance: Supermouse) => void;
|
|
168
|
+
}
|
package/src/utils/math.ts
CHANGED
|
@@ -1,20 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
return lerp(a, b, 1 - Math.exp(-lambda * dt));
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Calculates the angle in degrees between two points.
|
|
17
|
-
*/
|
|
18
|
-
export function angle(x: number, y: number): number {
|
|
19
|
-
return Math.atan2(y, x) * (180 / Math.PI);
|
|
20
|
-
}
|
|
1
|
+
export function lerp(start: number, end: number, factor: number): number {
|
|
2
|
+
return start + (end - start) * factor;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export function damp(a: number, b: number, lambda: number, dt: number): number {
|
|
6
|
+
return lerp(a, b, 1 - Math.exp(-lambda * dt));
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function angle(x: number, y: number): number {
|
|
10
|
+
return Math.atan2(y, x) * (180 / Math.PI);
|
|
11
|
+
}
|