@supermousejs/labs 2.3.0 → 2.4.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/README.md +86 -15
- package/dist/index.d.ts +11 -4
- package/dist/index.mjs +269 -162
- package/dist/index.umd.js +9 -9
- package/package.json +9 -5
- package/CHANGELOG.md +0 -103
- package/meta.json +0 -148
- package/src/SmartIcon.ts +0 -200
- package/src/SmartRing.ts +0 -105
- package/src/Sparkles.ts +0 -168
- package/src/TextRing.ts +0 -159
- package/src/index.ts +0 -4
- package/tsconfig.json +0 -4
- package/vite.config.ts +0 -24
package/package.json
CHANGED
|
@@ -1,21 +1,25 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@supermousejs/labs",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.0",
|
|
4
4
|
"main": "dist/index.umd.js",
|
|
5
5
|
"module": "dist/index.mjs",
|
|
6
|
+
"description": "Experimentental state machines and effects for supermouse",
|
|
6
7
|
"types": "dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist",
|
|
10
|
+
"README.md"
|
|
11
|
+
],
|
|
7
12
|
"author": "O.S David",
|
|
8
13
|
"url": "https://github.com/Whitestar14/supermouse-js",
|
|
9
14
|
"license": "MIT",
|
|
10
15
|
"dependencies": {
|
|
11
|
-
"@supermousejs/utils": "2.
|
|
12
|
-
"@supermousejs/zoetrope": "2.3.0"
|
|
16
|
+
"@supermousejs/utils": "2.4.0"
|
|
13
17
|
},
|
|
14
18
|
"peerDependencies": {
|
|
15
|
-
"@supermousejs/core": "2.
|
|
19
|
+
"@supermousejs/core": "2.4.0"
|
|
16
20
|
},
|
|
17
21
|
"devDependencies": {
|
|
18
|
-
"@supermousejs/core": "2.
|
|
22
|
+
"@supermousejs/core": "2.4.0"
|
|
19
23
|
},
|
|
20
24
|
"repository": {
|
|
21
25
|
"type": "git",
|
package/CHANGELOG.md
DELETED
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
# @supermousejs/labs
|
|
2
|
-
|
|
3
|
-
## 2.3.0
|
|
4
|
-
|
|
5
|
-
### Patch Changes
|
|
6
|
-
|
|
7
|
-
- Updated dependencies [f6f44b2]
|
|
8
|
-
- @supermousejs/core@2.2.0
|
|
9
|
-
- @supermousejs/utils@2.3.0
|
|
10
|
-
- @supermousejs/zoetrope@2.3.0
|
|
11
|
-
|
|
12
|
-
## 2.2.0
|
|
13
|
-
|
|
14
|
-
### Minor Changes
|
|
15
|
-
|
|
16
|
-
- 9fa6ece: Add `injectStyles` to safely handle injecting global CSS styles into the document head
|
|
17
|
-
|
|
18
|
-
### Patch Changes
|
|
19
|
-
|
|
20
|
-
- 6d70c18: remove legacy package and update supermouse domain in readme
|
|
21
|
-
- 14fb5b6: Updated tsconfig to be reference-compliant with core, utils and zoetrope when required
|
|
22
|
-
- Updated dependencies [6d70c18]
|
|
23
|
-
- Updated dependencies [2590af3]
|
|
24
|
-
- Updated dependencies [14fb5b6]
|
|
25
|
-
- Updated dependencies [9fe1a7b]
|
|
26
|
-
- Updated dependencies [9fa6ece]
|
|
27
|
-
- @supermousejs/core@2.1.0
|
|
28
|
-
- @supermousejs/utils@2.2.0
|
|
29
|
-
- @supermousejs/zoetrope@2.2.0
|
|
30
|
-
|
|
31
|
-
## 2.1.1
|
|
32
|
-
|
|
33
|
-
### Patch Changes
|
|
34
|
-
|
|
35
|
-
- 67f771b: Add relevant npm metadata to package.json file
|
|
36
|
-
- Updated dependencies [67f771b]
|
|
37
|
-
- @supermousejs/zoetrope@2.1.1
|
|
38
|
-
- @supermousejs/utils@2.1.1
|
|
39
|
-
- @supermousejs/core@2.0.5
|
|
40
|
-
|
|
41
|
-
## 2.1.0
|
|
42
|
-
|
|
43
|
-
### Minor Changes
|
|
44
|
-
|
|
45
|
-
- 0a1652d: fixed build architecture and updated plugin metadata
|
|
46
|
-
|
|
47
|
-
### Patch Changes
|
|
48
|
-
|
|
49
|
-
- Updated dependencies [0a1652d]
|
|
50
|
-
- @supermousejs/zoetrope@2.1.0
|
|
51
|
-
- @supermousejs/utils@2.1.0
|
|
52
|
-
|
|
53
|
-
## 2.0.4
|
|
54
|
-
|
|
55
|
-
### Patch Changes
|
|
56
|
-
|
|
57
|
-
- 993dc67: Updated supemousejs packages with proper author, license and url descriptors to repo
|
|
58
|
-
- Updated dependencies [993dc67]
|
|
59
|
-
- @supermousejs/zoetrope@2.0.4
|
|
60
|
-
- @supermousejs/utils@2.0.4
|
|
61
|
-
- @supermousejs/core@2.0.4
|
|
62
|
-
|
|
63
|
-
## 2.0.3
|
|
64
|
-
|
|
65
|
-
### Patch Changes
|
|
66
|
-
|
|
67
|
-
- Updated dependencies
|
|
68
|
-
- @supermousejs/core@2.0.3
|
|
69
|
-
- @supermousejs/utils@2.0.3
|
|
70
|
-
- @supermousejs/zoetrope@2.0.3
|
|
71
|
-
|
|
72
|
-
## 2.0.2
|
|
73
|
-
|
|
74
|
-
### Patch Changes
|
|
75
|
-
|
|
76
|
-
- ae219a0: Update READMEs with correct link to documentation
|
|
77
|
-
- Updated dependencies [ae219a0]
|
|
78
|
-
- @supermousejs/zoetrope@2.0.2
|
|
79
|
-
- @supermousejs/utils@2.0.2
|
|
80
|
-
- @supermousejs/core@2.0.2
|
|
81
|
-
|
|
82
|
-
## 2.0.1
|
|
83
|
-
|
|
84
|
-
### Patch Changes
|
|
85
|
-
|
|
86
|
-
- Add minimal README.md files to packages
|
|
87
|
-
- Updated dependencies
|
|
88
|
-
- @supermousejs/zoetrope@2.0.1
|
|
89
|
-
- @supermousejs/utils@2.0.1
|
|
90
|
-
- @supermousejs/core@2.0.1
|
|
91
|
-
|
|
92
|
-
## 2.0.0
|
|
93
|
-
|
|
94
|
-
### Major Changes
|
|
95
|
-
|
|
96
|
-
- Initial v2.0.0 release
|
|
97
|
-
|
|
98
|
-
### Patch Changes
|
|
99
|
-
|
|
100
|
-
- Updated dependencies
|
|
101
|
-
- @supermousejs/zoetrope@2.0.0
|
|
102
|
-
- @supermousejs/utils@2.0.0
|
|
103
|
-
- @supermousejs/core@2.0.0
|
package/meta.json
DELETED
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
[
|
|
2
|
-
{
|
|
3
|
-
"id": "smart-icon",
|
|
4
|
-
"name": "SmartIcon",
|
|
5
|
-
"package": "@supermousejs/labs",
|
|
6
|
-
"description": "Advanced state machine that morphs SVGs based on semantic context.",
|
|
7
|
-
"code": "import { SmartIcon } from '@supermousejs/labs';\n\napp.use(SmartIcon({ icons: { ... } }));",
|
|
8
|
-
"recipeId": "context-icon",
|
|
9
|
-
"icon": "labs",
|
|
10
|
-
"options": [
|
|
11
|
-
{
|
|
12
|
-
"name": "icons",
|
|
13
|
-
"type": "Record<string, string>",
|
|
14
|
-
"default": "{}",
|
|
15
|
-
"description": "Map of state names to SVG strings.",
|
|
16
|
-
"reactive": false
|
|
17
|
-
},
|
|
18
|
-
{
|
|
19
|
-
"name": "size",
|
|
20
|
-
"type": "number",
|
|
21
|
-
"default": "24",
|
|
22
|
-
"description": "Size of the icon container.",
|
|
23
|
-
"reactive": true
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
"name": "color",
|
|
27
|
-
"type": "string",
|
|
28
|
-
"default": "'black'",
|
|
29
|
-
"description": "Icon fill color (currentColor).",
|
|
30
|
-
"reactive": true
|
|
31
|
-
},
|
|
32
|
-
{
|
|
33
|
-
"name": "anchor",
|
|
34
|
-
"type": "string",
|
|
35
|
-
"default": "'center'",
|
|
36
|
-
"description": "Alignment (center, top-left, etc).",
|
|
37
|
-
"reactive": true
|
|
38
|
-
},
|
|
39
|
-
{
|
|
40
|
-
"name": "offset",
|
|
41
|
-
"type": "[number, number]",
|
|
42
|
-
"default": "[0, 0]",
|
|
43
|
-
"description": "Fixed offset.",
|
|
44
|
-
"reactive": false
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
"name": "transitionDuration",
|
|
48
|
-
"type": "number",
|
|
49
|
-
"default": "200",
|
|
50
|
-
"description": "Morph transition time in ms.",
|
|
51
|
-
"reactive": false
|
|
52
|
-
}
|
|
53
|
-
]
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
"id": "smart-ring",
|
|
57
|
-
"name": "SmartRing",
|
|
58
|
-
"package": "@supermousejs/labs",
|
|
59
|
-
"description": "A reactive ring that distorts with velocity and adapts to stuck elements.",
|
|
60
|
-
"code": "import { SmartRing } from '@supermousejs/labs';\n\napp.use(SmartRing({ size: 24, color: '#f59e0b', enableSkew: true }));",
|
|
61
|
-
"icon": "ring",
|
|
62
|
-
"options": [
|
|
63
|
-
{
|
|
64
|
-
"name": "size",
|
|
65
|
-
"type": "number",
|
|
66
|
-
"default": "20",
|
|
67
|
-
"description": "Base diameter.",
|
|
68
|
-
"reactive": true
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
"name": "hoverSize",
|
|
72
|
-
"type": "number",
|
|
73
|
-
"default": "40",
|
|
74
|
-
"description": "Diameter on hover.",
|
|
75
|
-
"reactive": true
|
|
76
|
-
},
|
|
77
|
-
{
|
|
78
|
-
"name": "enableSkew",
|
|
79
|
-
"type": "boolean",
|
|
80
|
-
"default": "false",
|
|
81
|
-
"description": "Distort shape based on velocity.",
|
|
82
|
-
"reactive": false
|
|
83
|
-
}
|
|
84
|
-
]
|
|
85
|
-
},
|
|
86
|
-
{
|
|
87
|
-
"id": "sparkles",
|
|
88
|
-
"name": "Sparkles",
|
|
89
|
-
"package": "@supermousejs/labs",
|
|
90
|
-
"description": "Emits particle trails based on movement velocity.",
|
|
91
|
-
"code": "import { Sparkles } from '@supermousejs/labs';\n\napp.use(Sparkles({ color: '#f59e0b', frequency: 10 }));",
|
|
92
|
-
"icon": "trail",
|
|
93
|
-
"options": [
|
|
94
|
-
{
|
|
95
|
-
"name": "color",
|
|
96
|
-
"type": "string",
|
|
97
|
-
"default": "'#ff00ff'",
|
|
98
|
-
"description": "Particle color.",
|
|
99
|
-
"reactive": true
|
|
100
|
-
},
|
|
101
|
-
{
|
|
102
|
-
"name": "frequency",
|
|
103
|
-
"type": "number",
|
|
104
|
-
"default": "10",
|
|
105
|
-
"description": "Pixels per spawn.",
|
|
106
|
-
"reactive": false
|
|
107
|
-
},
|
|
108
|
-
{
|
|
109
|
-
"name": "decay",
|
|
110
|
-
"type": "number",
|
|
111
|
-
"default": "0.05",
|
|
112
|
-
"description": "Fade rate.",
|
|
113
|
-
"reactive": false
|
|
114
|
-
}
|
|
115
|
-
]
|
|
116
|
-
},
|
|
117
|
-
{
|
|
118
|
-
"id": "text-ring",
|
|
119
|
-
"name": "TextRing",
|
|
120
|
-
"package": "@supermousejs/labs",
|
|
121
|
-
"description": "Rotates text around the cursor position.",
|
|
122
|
-
"code": "import { TextRing } from '@supermousejs/labs';\n\napp.use(TextRing({ text: 'LOADING • ', radius: 40 }));",
|
|
123
|
-
"icon": "text",
|
|
124
|
-
"options": [
|
|
125
|
-
{
|
|
126
|
-
"name": "text",
|
|
127
|
-
"type": "string",
|
|
128
|
-
"default": "'SUPERMOUSE • '",
|
|
129
|
-
"description": "Text content.",
|
|
130
|
-
"reactive": true
|
|
131
|
-
},
|
|
132
|
-
{
|
|
133
|
-
"name": "radius",
|
|
134
|
-
"type": "number",
|
|
135
|
-
"default": "60",
|
|
136
|
-
"description": "Ring radius.",
|
|
137
|
-
"reactive": true
|
|
138
|
-
},
|
|
139
|
-
{
|
|
140
|
-
"name": "speed",
|
|
141
|
-
"type": "number",
|
|
142
|
-
"default": "0.5",
|
|
143
|
-
"description": "Rotation speed deg/frame.",
|
|
144
|
-
"reactive": true
|
|
145
|
-
}
|
|
146
|
-
]
|
|
147
|
-
}
|
|
148
|
-
]
|
package/src/SmartIcon.ts
DELETED
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
import type { ValueOrGetter, Supermouse } from "@supermousejs/core";
|
|
2
|
-
import { definePlugin, normalize, dom, math, Layers } from "@supermousejs/utils";
|
|
3
|
-
|
|
4
|
-
export interface SmartIconMap {
|
|
5
|
-
[key: string]: string;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export type SmartIconAnchor = "center" | "top-left" | "top-right" | "bottom-left" | "bottom-right";
|
|
9
|
-
|
|
10
|
-
export interface SmartIconOptions {
|
|
11
|
-
name?: string;
|
|
12
|
-
isEnabled?: boolean;
|
|
13
|
-
icons: SmartIconMap;
|
|
14
|
-
defaultState?: string;
|
|
15
|
-
useSemanticTags?: boolean;
|
|
16
|
-
transitionDuration?: number;
|
|
17
|
-
size?: ValueOrGetter<number>;
|
|
18
|
-
color?: ValueOrGetter<string>;
|
|
19
|
-
offset?: [number, number];
|
|
20
|
-
anchor?: ValueOrGetter<SmartIconAnchor>;
|
|
21
|
-
followStrategy?: ValueOrGetter<"smooth" | "raw">;
|
|
22
|
-
rotateWithVelocity?: ValueOrGetter<boolean>;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function resolveSemanticState(target: HTMLElement, icons: SmartIconMap): string | null {
|
|
26
|
-
const tag = target.tagName.toLowerCase();
|
|
27
|
-
|
|
28
|
-
if (tag === "input" || tag === "textarea" || target.isContentEditable) {
|
|
29
|
-
const type = (target as HTMLInputElement).type;
|
|
30
|
-
if (!["button", "submit", "checkbox", "radio", "range", "color"].includes(type)) {
|
|
31
|
-
if (icons["text"]) return "text";
|
|
32
|
-
} else if (icons["pointer"]) {
|
|
33
|
-
return "pointer";
|
|
34
|
-
}
|
|
35
|
-
} else if (tag === "a" || tag === "button" || target.closest("a") || target.closest("button")) {
|
|
36
|
-
if (icons["pointer"]) return "pointer";
|
|
37
|
-
}
|
|
38
|
-
return null;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export const SmartIcon = (options: SmartIconOptions) => {
|
|
42
|
-
let contentWrapper: HTMLDivElement;
|
|
43
|
-
|
|
44
|
-
let currentState = options.defaultState || "default";
|
|
45
|
-
let targetState = options.defaultState || "default";
|
|
46
|
-
|
|
47
|
-
let lastTarget: HTMLElement | null = null;
|
|
48
|
-
let cachedSemanticState: string | null = null;
|
|
49
|
-
|
|
50
|
-
let isTransitioning = false;
|
|
51
|
-
let transitionTimer: ReturnType<typeof setTimeout>;
|
|
52
|
-
|
|
53
|
-
let currentRotation = 0;
|
|
54
|
-
let lastTargetRotation = 0;
|
|
55
|
-
|
|
56
|
-
const getSize = normalize(options.size, 24);
|
|
57
|
-
const getStrategy = normalize(options.followStrategy, "smooth");
|
|
58
|
-
const getAnchor = normalize(options.anchor, "center");
|
|
59
|
-
const getShouldRotate = normalize(options.rotateWithVelocity, false);
|
|
60
|
-
|
|
61
|
-
const useSemanticTags = options.useSemanticTags ?? true;
|
|
62
|
-
const duration = options.transitionDuration ?? 200;
|
|
63
|
-
const userOffX = options.offset ? options.offset[0] : 0;
|
|
64
|
-
const userOffY = options.offset ? options.offset[1] : 0;
|
|
65
|
-
|
|
66
|
-
return definePlugin<HTMLDivElement, SmartIconOptions>(
|
|
67
|
-
{
|
|
68
|
-
name: options.name || "smart-icon",
|
|
69
|
-
selector: "[data-supermouse-icon]",
|
|
70
|
-
|
|
71
|
-
create: () => {
|
|
72
|
-
const el = dom.createActor("div") as HTMLDivElement;
|
|
73
|
-
el.style.zIndex = Layers.CURSOR;
|
|
74
|
-
|
|
75
|
-
contentWrapper = dom.createActor("div") as HTMLDivElement;
|
|
76
|
-
dom.applyStyles(contentWrapper, {
|
|
77
|
-
width: "100%",
|
|
78
|
-
height: "100%",
|
|
79
|
-
display: "flex",
|
|
80
|
-
alignItems: "center",
|
|
81
|
-
justifyContent: "center",
|
|
82
|
-
transformOrigin: "center center",
|
|
83
|
-
transform: "scale(1)",
|
|
84
|
-
transition: `transform ${duration / 2}ms cubic-bezier(0.16, 1, 0.3, 1)`
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
contentWrapper.innerHTML = options.icons[currentState] || "";
|
|
88
|
-
|
|
89
|
-
dom.injectStyles(
|
|
90
|
-
"supermouse-smart-icon-styles",
|
|
91
|
-
`
|
|
92
|
-
@keyframes sm {
|
|
93
|
-
0% { transform: rotate(0deg); }
|
|
94
|
-
100% { transform: rotate(360deg); }
|
|
95
|
-
}
|
|
96
|
-
.supermouse-spin {
|
|
97
|
-
animation: sm 1s linear infinite;
|
|
98
|
-
}
|
|
99
|
-
`
|
|
100
|
-
);
|
|
101
|
-
|
|
102
|
-
el.appendChild(contentWrapper);
|
|
103
|
-
return el;
|
|
104
|
-
},
|
|
105
|
-
|
|
106
|
-
styles: {
|
|
107
|
-
color: "color"
|
|
108
|
-
},
|
|
109
|
-
|
|
110
|
-
update: (app: Supermouse, el: HTMLDivElement) => {
|
|
111
|
-
const icons = options.icons;
|
|
112
|
-
const target = app.state.hoverTarget;
|
|
113
|
-
|
|
114
|
-
let nextState = options.defaultState || "default";
|
|
115
|
-
|
|
116
|
-
if (target) {
|
|
117
|
-
if (target !== lastTarget) {
|
|
118
|
-
lastTarget = target;
|
|
119
|
-
cachedSemanticState = useSemanticTags ? resolveSemanticState(target, icons) : null;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const attrSmartIcon = app.state.interaction?.icon;
|
|
123
|
-
|
|
124
|
-
if (attrSmartIcon && icons[attrSmartIcon]) {
|
|
125
|
-
nextState = attrSmartIcon;
|
|
126
|
-
} else if (cachedSemanticState) {
|
|
127
|
-
nextState = cachedSemanticState;
|
|
128
|
-
}
|
|
129
|
-
} else {
|
|
130
|
-
lastTarget = null;
|
|
131
|
-
cachedSemanticState = null;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
if (nextState !== currentState && !isTransitioning) {
|
|
135
|
-
if (icons[nextState] || nextState === (options.defaultState || "default")) {
|
|
136
|
-
targetState = nextState;
|
|
137
|
-
isTransitioning = true;
|
|
138
|
-
|
|
139
|
-
clearTimeout(transitionTimer);
|
|
140
|
-
|
|
141
|
-
contentWrapper.style.transform = "scale(0)";
|
|
142
|
-
|
|
143
|
-
transitionTimer = setTimeout(() => {
|
|
144
|
-
currentState = targetState;
|
|
145
|
-
contentWrapper.innerHTML = icons[currentState] || "";
|
|
146
|
-
contentWrapper.style.transform = "scale(1)";
|
|
147
|
-
|
|
148
|
-
transitionTimer = setTimeout(() => {
|
|
149
|
-
isTransitioning = false;
|
|
150
|
-
}, duration / 2);
|
|
151
|
-
}, duration / 2);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
const size = getSize(app.state);
|
|
156
|
-
dom.setStyle(el, "width", `${size}px`);
|
|
157
|
-
dom.setStyle(el, "height", `${size}px`);
|
|
158
|
-
|
|
159
|
-
let anchorX = 0;
|
|
160
|
-
let anchorY = 0;
|
|
161
|
-
const half = size / 2;
|
|
162
|
-
const anchor = getAnchor(app.state);
|
|
163
|
-
|
|
164
|
-
if (anchor !== "center") {
|
|
165
|
-
if (anchor.includes("left")) anchorX = half;
|
|
166
|
-
if (anchor.includes("right")) anchorX = -half;
|
|
167
|
-
if (anchor.includes("top")) anchorY = half;
|
|
168
|
-
if (anchor.includes("bottom")) anchorY = -half;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
const isSemanticState = currentState === "pointer" || currentState === "text";
|
|
172
|
-
|
|
173
|
-
if (getShouldRotate(app.state) && !isSemanticState && !app.state.reducedMotion) {
|
|
174
|
-
const { x: vx, y: vy } = app.state.velocity;
|
|
175
|
-
const speed = math.dist(vx, vy);
|
|
176
|
-
|
|
177
|
-
if (speed > 1) {
|
|
178
|
-
lastTargetRotation = math.angle(vx, vy);
|
|
179
|
-
}
|
|
180
|
-
currentRotation = math.lerpAngle(currentRotation, lastTargetRotation, 0.15);
|
|
181
|
-
} else {
|
|
182
|
-
currentRotation = math.lerpAngle(currentRotation, 0, 0.15);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
const pos = getStrategy(app.state) === "raw" ? app.state.pointer : app.state.smooth;
|
|
186
|
-
dom.setTransform(
|
|
187
|
-
el,
|
|
188
|
-
pos.x + userOffX + anchorX,
|
|
189
|
-
pos.y + userOffY + anchorY,
|
|
190
|
-
currentRotation
|
|
191
|
-
);
|
|
192
|
-
},
|
|
193
|
-
|
|
194
|
-
cleanup() {
|
|
195
|
-
clearTimeout(transitionTimer);
|
|
196
|
-
}
|
|
197
|
-
},
|
|
198
|
-
options
|
|
199
|
-
);
|
|
200
|
-
};
|
package/src/SmartRing.ts
DELETED
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
import type { ValueOrGetter, Supermouse } from "@supermousejs/core";
|
|
2
|
-
import { definePlugin, normalize, dom, math, effects, Layers } from "@supermousejs/utils";
|
|
3
|
-
|
|
4
|
-
export interface SmartRingOptions {
|
|
5
|
-
name?: string;
|
|
6
|
-
isEnabled?: boolean;
|
|
7
|
-
size?: ValueOrGetter<number>;
|
|
8
|
-
hoverSize?: ValueOrGetter<number>;
|
|
9
|
-
color?: ValueOrGetter<string>;
|
|
10
|
-
fill?: ValueOrGetter<string>;
|
|
11
|
-
borderWidth?: ValueOrGetter<number>;
|
|
12
|
-
mixBlendMode?: string;
|
|
13
|
-
enableSkew?: boolean;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export const SmartRing = (options: SmartRingOptions = {}) => {
|
|
17
|
-
const getSize = normalize(options.size, 20);
|
|
18
|
-
const getHoverSize = normalize(options.hoverSize, 40);
|
|
19
|
-
const getColor = normalize(options.color, "#ffffff");
|
|
20
|
-
const getBorder = normalize(options.borderWidth, 2);
|
|
21
|
-
const getFill = normalize(options.fill, "transparent");
|
|
22
|
-
|
|
23
|
-
let currentW = 20;
|
|
24
|
-
let currentH = 20;
|
|
25
|
-
let currentRot = 0;
|
|
26
|
-
let currentScaleX = 1;
|
|
27
|
-
let currentScaleY = 1;
|
|
28
|
-
|
|
29
|
-
return definePlugin<HTMLDivElement, SmartRingOptions>(
|
|
30
|
-
{
|
|
31
|
-
name: options.name || "smart-ring",
|
|
32
|
-
selector: "[data-supermouse-color]",
|
|
33
|
-
|
|
34
|
-
create: (app: Supermouse) => {
|
|
35
|
-
const el = dom.createCircle(getSize(app.state), getFill(app.state));
|
|
36
|
-
dom.applyStyles(el, {
|
|
37
|
-
zIndex: Layers.FOLLOWER,
|
|
38
|
-
mixBlendMode: options.mixBlendMode || "difference",
|
|
39
|
-
transition: "opacity 0.2s ease, border-radius 0.2s ease",
|
|
40
|
-
borderStyle: "solid"
|
|
41
|
-
});
|
|
42
|
-
return el;
|
|
43
|
-
},
|
|
44
|
-
|
|
45
|
-
update: (app: Supermouse, el: HTMLDivElement) => {
|
|
46
|
-
const baseSize = getSize(app.state);
|
|
47
|
-
const shape = app.state.shape;
|
|
48
|
-
|
|
49
|
-
let targetW = baseSize;
|
|
50
|
-
let targetH = baseSize;
|
|
51
|
-
let targetRadius = "50%";
|
|
52
|
-
let color = getColor(app.state);
|
|
53
|
-
|
|
54
|
-
if (shape) {
|
|
55
|
-
targetW = shape.width;
|
|
56
|
-
targetH = shape.height;
|
|
57
|
-
targetRadius = `${shape.borderRadius}px`;
|
|
58
|
-
} else {
|
|
59
|
-
if (app.state.isHover) {
|
|
60
|
-
targetW = getHoverSize(app.state);
|
|
61
|
-
targetH = getHoverSize(app.state);
|
|
62
|
-
}
|
|
63
|
-
if (app.state.isDown) {
|
|
64
|
-
targetW *= 0.9;
|
|
65
|
-
targetH *= 0.9;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
if (app.state.interaction.color) color = app.state.interaction.color;
|
|
70
|
-
|
|
71
|
-
currentW = math.lerp(currentW, targetW, 0.2);
|
|
72
|
-
currentH = math.lerp(currentH, targetH, 0.2);
|
|
73
|
-
|
|
74
|
-
dom.setStyle(el, "width", `${currentW}px`);
|
|
75
|
-
dom.setStyle(el, "height", `${currentH}px`);
|
|
76
|
-
dom.setStyle(el, "borderRadius", targetRadius);
|
|
77
|
-
dom.setStyle(el, "borderColor", color);
|
|
78
|
-
dom.setStyle(el, "backgroundColor", getFill(app.state));
|
|
79
|
-
dom.setStyle(el, "borderWidth", `${getBorder(app.state)}px`);
|
|
80
|
-
|
|
81
|
-
let targetRot = 0;
|
|
82
|
-
let targetScaleX = 1;
|
|
83
|
-
let targetScaleY = 1;
|
|
84
|
-
|
|
85
|
-
if (!shape && options.enableSkew && !app.state.reducedMotion) {
|
|
86
|
-
const { velocity } = app.state;
|
|
87
|
-
const dist = effects.getVelocityDistortion(velocity.x, velocity.y);
|
|
88
|
-
targetRot = dist.rotation;
|
|
89
|
-
targetScaleX = dist.scaleX;
|
|
90
|
-
targetScaleY = dist.scaleY;
|
|
91
|
-
currentRot = math.lerpAngle(currentRot, targetRot, 0.15);
|
|
92
|
-
} else {
|
|
93
|
-
currentRot = 0;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
currentScaleX = math.lerp(currentScaleX, targetScaleX, 0.15);
|
|
97
|
-
currentScaleY = math.lerp(currentScaleY, targetScaleY, 0.15);
|
|
98
|
-
|
|
99
|
-
const { x, y } = app.state.smooth;
|
|
100
|
-
dom.setTransform(el, x, y, currentRot, currentScaleX, currentScaleY);
|
|
101
|
-
}
|
|
102
|
-
},
|
|
103
|
-
options
|
|
104
|
-
);
|
|
105
|
-
};
|