solid-drift 0.17.0 → 0.18.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 +30 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/pointer.d.ts +61 -0
- package/dist/pointer.js +118 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -345,6 +345,36 @@ const { rotateX, rotateY } = createTilt(() => card, { maxAngle: 12 })
|
|
|
345
345
|
|
|
346
346
|
Returns `{ rotateX, rotateY }`: spring-smoothed tilt in degrees. SSR-safe and reduced-motion safe: both return constant `0` accessors.
|
|
347
347
|
|
|
348
|
+
### `createTiltCard(ref, options?)`
|
|
349
|
+
|
|
350
|
+
Holographic trading-card tilt: 3D lean plus every signal a holo foil needs. Beyond `createTilt`'s rotation, this tracks `glareX`/`glareY` (pointer position 0..1, for a radial glare overlay), `holoAngle` (a rainbow angle that sweeps with the pointer, for a gradient foil overlay), `shine` (0..1 overlay intensity that fades in on hover and out on leave), `scale` (hover pop), `hovering`, and a ready-made `transform()` string (perspective, rotateX/rotateY, scale). SSR-safe and reduced-motion safe: static constants, no tilt, no shine. Touch drags tilt while touching, release settles back.
|
|
351
|
+
|
|
352
|
+
```tsx
|
|
353
|
+
import { createTiltCard } from "solid-drift"
|
|
354
|
+
|
|
355
|
+
let card!: HTMLDivElement
|
|
356
|
+
const c = createTiltCard(() => card, { maxAngle: 14 })
|
|
357
|
+
<div style={{ transform: c.transform() }} ref={card}>
|
|
358
|
+
{art}
|
|
359
|
+
<div style={{
|
|
360
|
+
background: `radial-gradient(circle at ${c.glareX() * 100}% ${c.glareY() * 100}%, rgba(255,255,255,0.6), transparent 60%)`,
|
|
361
|
+
opacity: c.shine(),
|
|
362
|
+
}} />
|
|
363
|
+
<div style={{
|
|
364
|
+
background: `linear-gradient(${c.holoAngle()}deg, #ff0080, #ff8000, #ffff00, #00ff80, #0080ff, #8000ff)`,
|
|
365
|
+
"mix-blend-mode": "color-dodge",
|
|
366
|
+
opacity: c.shine() * 0.55,
|
|
367
|
+
}} />
|
|
368
|
+
</div>
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
| Option | Default | Description |
|
|
372
|
+
| ------------- | ------- | ------------------------------------ |
|
|
373
|
+
| `maxAngle` | `12` | Maximum tilt in degrees at the edge |
|
|
374
|
+
| `scale` | `1.04` | Scale while hovering |
|
|
375
|
+
| `perspective` | `900` | Perspective distance in px |
|
|
376
|
+
| `spring` | default | Spring physics for tilt, shine, pop |
|
|
377
|
+
|
|
348
378
|
### `createDrag(ref, options?)`
|
|
349
379
|
|
|
350
380
|
Pointer drag with spring physics, constraints, and momentum. The gesture workhorse: draggable cards, sliders, bottom-sheet handles, sortable rows. While the pointer is down the element tracks it 1:1; on release it glides with inertia and springs into its constraints, stretching elastically past the edges while dragged. Set `touch-action: none` on the draggable element so touch drags do not fight the page scroll. For the physics-toy flavor (exponential friction plus bouncing off walls), see `createFling` instead.
|
package/dist/index.d.ts
CHANGED
|
@@ -18,7 +18,7 @@ export { createHorizontalScroll, type HorizontalScrollOptions, type HorizontalSc
|
|
|
18
18
|
export { createScrub, type ScrubKeyframe, type ScrubOptions } from "./scrub.js";
|
|
19
19
|
export { createScrollColor, type ScrollColorStop, type ScrollColorOptions, type ScrollColorFormat, createScrollTracking, type ScrollTrackingOptions, createScrollLine, type ScrollLineOptions, type ScrollLineStyle, type ScrollLineAxis, type ScrollLineOrigin, } from "./scrollfx.js";
|
|
20
20
|
export { createVelocity, type VelocityOptions } from "./velocity.js";
|
|
21
|
-
export { createMagnetic, type MagneticOptions, type MagneticResult, createTilt, type TiltOptions, type TiltResult, } from "./pointer.js";
|
|
21
|
+
export { createMagnetic, type MagneticOptions, type MagneticResult, createTilt, type TiltOptions, type TiltResult, createTiltCard, type TiltCardOptions, type TiltCardResult, } from "./pointer.js";
|
|
22
22
|
export { createTrail, type TrailOptions } from "./trail.js";
|
|
23
23
|
export { createTimeline, type TimelineStep, type TimelineStatus, type TimelineControls, } from "./timeline.js";
|
|
24
24
|
export { animateFlip, type FlipOptions, createSharedLayout, type SharedLayoutOptions, type SharedLayoutResult, } from "./flip.js";
|
package/dist/index.js
CHANGED
|
@@ -18,7 +18,7 @@ export { createHorizontalScroll, } from "./horizontal.js";
|
|
|
18
18
|
export { createScrub } from "./scrub.js";
|
|
19
19
|
export { createScrollColor, createScrollTracking, createScrollLine, } from "./scrollfx.js";
|
|
20
20
|
export { createVelocity } from "./velocity.js";
|
|
21
|
-
export { createMagnetic, createTilt, } from "./pointer.js";
|
|
21
|
+
export { createMagnetic, createTilt, createTiltCard, } from "./pointer.js";
|
|
22
22
|
export { createTrail } from "./trail.js";
|
|
23
23
|
export { createTimeline, } from "./timeline.js";
|
|
24
24
|
export { animateFlip, createSharedLayout, } from "./flip.js";
|
package/dist/pointer.d.ts
CHANGED
|
@@ -74,3 +74,64 @@ export declare function createMagnetic(ref: () => Element | null | undefined, op
|
|
|
74
74
|
* ```
|
|
75
75
|
*/
|
|
76
76
|
export declare function createTilt(ref: () => Element | null | undefined, options?: TiltOptions): TiltResult;
|
|
77
|
+
export interface TiltCardOptions {
|
|
78
|
+
/** Max tilt angle in degrees. Default 12. */
|
|
79
|
+
maxAngle?: number;
|
|
80
|
+
/** Scale while hovering. Default 1.04. */
|
|
81
|
+
scale?: number;
|
|
82
|
+
/** Perspective distance in px for the transform. Default 900. */
|
|
83
|
+
perspective?: number;
|
|
84
|
+
/** Spring options for tilt, shine, and scale. */
|
|
85
|
+
spring?: SpringOptions;
|
|
86
|
+
}
|
|
87
|
+
export interface TiltCardResult {
|
|
88
|
+
/** Spring-smoothed rotateX in degrees. */
|
|
89
|
+
rotateX: Accessor<number>;
|
|
90
|
+
/** Spring-smoothed rotateY in degrees. */
|
|
91
|
+
rotateY: Accessor<number>;
|
|
92
|
+
/** Pointer position 0..1 across the card, for glare placement. */
|
|
93
|
+
glareX: Accessor<number>;
|
|
94
|
+
/** Pointer position 0..1 down the card, for glare placement. */
|
|
95
|
+
glareY: Accessor<number>;
|
|
96
|
+
/** Rainbow angle in degrees, follows the pointer. Spring-smoothed. */
|
|
97
|
+
holoAngle: Accessor<number>;
|
|
98
|
+
/** 0..1 shine intensity, spring-smoothed, 0 when not hovering. */
|
|
99
|
+
shine: Accessor<number>;
|
|
100
|
+
/** Spring-smoothed scale. */
|
|
101
|
+
scale: Accessor<number>;
|
|
102
|
+
/** Whether the pointer is over the card. */
|
|
103
|
+
hovering: Accessor<boolean>;
|
|
104
|
+
/** Ready-made transform: perspective, rotateX/rotateY, scale. */
|
|
105
|
+
transform: Accessor<string>;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Holographic trading-card tilt: 3D lean plus the signals a holo
|
|
109
|
+
* foil needs.
|
|
110
|
+
*
|
|
111
|
+
* Beyond `createTilt`'s spring-smoothed rotation, this tracks the
|
|
112
|
+
* pointer as `glareX`/`glareY` (0..1, for a radial glare overlay),
|
|
113
|
+
* `holoAngle` (a rainbow angle that sweeps with the pointer, for a
|
|
114
|
+
* gradient foil overlay), `shine` (0..1 overlay intensity that fades
|
|
115
|
+
* in on hover and out on leave), and a hover `scale` pop. Bind them
|
|
116
|
+
* to two absolutely-positioned overlays over your card art:
|
|
117
|
+
*
|
|
118
|
+
* ```tsx
|
|
119
|
+
* const card = createTiltCard(() => el, { maxAngle: 14 });
|
|
120
|
+
* <div style={{ transform: card.transform() }}>
|
|
121
|
+
* {art}
|
|
122
|
+
* <div style={{
|
|
123
|
+
* background: `radial-gradient(circle at ${card.glareX() * 100}% ${card.glareY() * 100}%, rgba(255,255,255,0.6), transparent 60%)`,
|
|
124
|
+
* opacity: card.shine(),
|
|
125
|
+
* }} />
|
|
126
|
+
* <div style={{
|
|
127
|
+
* background: `linear-gradient(${card.holoAngle()}deg, #ff0080, #ff8000, #ffff00, #00ff80, #0080ff, #8000ff)`,
|
|
128
|
+
* "mix-blend-mode": "color-dodge",
|
|
129
|
+
* opacity: card.shine() * 0.55,
|
|
130
|
+
* }} />
|
|
131
|
+
* </div>
|
|
132
|
+
* ```
|
|
133
|
+
*
|
|
134
|
+
* SSR-safe and reduced-motion safe: static constants (no tilt, no
|
|
135
|
+
* shine). Touch drags tilt while touching, release settles back.
|
|
136
|
+
*/
|
|
137
|
+
export declare function createTiltCard(ref: () => Element | null | undefined, options?: TiltCardOptions): TiltCardResult;
|
package/dist/pointer.js
CHANGED
|
@@ -121,3 +121,121 @@ export function createTilt(ref, options = {}) {
|
|
|
121
121
|
onCleanup(() => window.removeEventListener("pointermove", onMove));
|
|
122
122
|
return { rotateX, rotateY };
|
|
123
123
|
}
|
|
124
|
+
/**
|
|
125
|
+
* Holographic trading-card tilt: 3D lean plus the signals a holo
|
|
126
|
+
* foil needs.
|
|
127
|
+
*
|
|
128
|
+
* Beyond `createTilt`'s spring-smoothed rotation, this tracks the
|
|
129
|
+
* pointer as `glareX`/`glareY` (0..1, for a radial glare overlay),
|
|
130
|
+
* `holoAngle` (a rainbow angle that sweeps with the pointer, for a
|
|
131
|
+
* gradient foil overlay), `shine` (0..1 overlay intensity that fades
|
|
132
|
+
* in on hover and out on leave), and a hover `scale` pop. Bind them
|
|
133
|
+
* to two absolutely-positioned overlays over your card art:
|
|
134
|
+
*
|
|
135
|
+
* ```tsx
|
|
136
|
+
* const card = createTiltCard(() => el, { maxAngle: 14 });
|
|
137
|
+
* <div style={{ transform: card.transform() }}>
|
|
138
|
+
* {art}
|
|
139
|
+
* <div style={{
|
|
140
|
+
* background: `radial-gradient(circle at ${card.glareX() * 100}% ${card.glareY() * 100}%, rgba(255,255,255,0.6), transparent 60%)`,
|
|
141
|
+
* opacity: card.shine(),
|
|
142
|
+
* }} />
|
|
143
|
+
* <div style={{
|
|
144
|
+
* background: `linear-gradient(${card.holoAngle()}deg, #ff0080, #ff8000, #ffff00, #00ff80, #0080ff, #8000ff)`,
|
|
145
|
+
* "mix-blend-mode": "color-dodge",
|
|
146
|
+
* opacity: card.shine() * 0.55,
|
|
147
|
+
* }} />
|
|
148
|
+
* </div>
|
|
149
|
+
* ```
|
|
150
|
+
*
|
|
151
|
+
* SSR-safe and reduced-motion safe: static constants (no tilt, no
|
|
152
|
+
* shine). Touch drags tilt while touching, release settles back.
|
|
153
|
+
*/
|
|
154
|
+
export function createTiltCard(ref, options = {}) {
|
|
155
|
+
const half = () => 0.5;
|
|
156
|
+
const one = () => 1;
|
|
157
|
+
const no = () => false;
|
|
158
|
+
if (typeof window === "undefined" || prefersReducedMotion()) {
|
|
159
|
+
const p = options.perspective ?? 900;
|
|
160
|
+
return {
|
|
161
|
+
rotateX: zero,
|
|
162
|
+
rotateY: zero,
|
|
163
|
+
glareX: half,
|
|
164
|
+
glareY: half,
|
|
165
|
+
holoAngle: zero,
|
|
166
|
+
shine: zero,
|
|
167
|
+
scale: one,
|
|
168
|
+
hovering: no,
|
|
169
|
+
transform: () => `perspective(${p}px) rotateX(0deg) rotateY(0deg) scale(1)`,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
const { maxAngle = 12, scale: hoverScale = 1.04, perspective = 900, spring, } = options;
|
|
173
|
+
const [targetRX, setTargetRX] = createSignal(0);
|
|
174
|
+
const [targetRY, setTargetRY] = createSignal(0);
|
|
175
|
+
const [targetHolo, setTargetHolo] = createSignal(0);
|
|
176
|
+
const [targetShine, setTargetShine] = createSignal(0);
|
|
177
|
+
const [targetScale, setTargetScale] = createSignal(1);
|
|
178
|
+
const [glareX, setGlareX] = createSignal(0.5);
|
|
179
|
+
const [glareY, setGlareY] = createSignal(0.5);
|
|
180
|
+
const [hovering, setHovering] = createSignal(false);
|
|
181
|
+
const rotateX = createSpring(targetRX, spring);
|
|
182
|
+
const rotateY = createSpring(targetRY, spring);
|
|
183
|
+
const holoAngle = createSpring(targetHolo, spring);
|
|
184
|
+
const shine = createSpring(targetShine, spring);
|
|
185
|
+
const scale = createSpring(targetScale, spring);
|
|
186
|
+
const onMove = (event) => {
|
|
187
|
+
const el = ref();
|
|
188
|
+
if (!el)
|
|
189
|
+
return;
|
|
190
|
+
const rect = el.getBoundingClientRect();
|
|
191
|
+
if (rect.width === 0 || rect.height === 0)
|
|
192
|
+
return;
|
|
193
|
+
if (event.clientX < rect.left ||
|
|
194
|
+
event.clientX > rect.right ||
|
|
195
|
+
event.clientY < rect.top ||
|
|
196
|
+
event.clientY > rect.bottom) {
|
|
197
|
+
return; // outside: pointerleave resets the card
|
|
198
|
+
}
|
|
199
|
+
const px = (event.clientX - rect.left) / rect.width;
|
|
200
|
+
const py = (event.clientY - rect.top) / rect.height;
|
|
201
|
+
const cx = px - 0.5;
|
|
202
|
+
const cy = py - 0.5;
|
|
203
|
+
setTargetRY(cx * 2 * maxAngle);
|
|
204
|
+
setTargetRX(-cy * 2 * maxAngle);
|
|
205
|
+
setGlareX(px);
|
|
206
|
+
setGlareY(py);
|
|
207
|
+
setTargetHolo((cx + cy) * 180);
|
|
208
|
+
setTargetShine(1);
|
|
209
|
+
setTargetScale(hoverScale);
|
|
210
|
+
setHovering(true);
|
|
211
|
+
};
|
|
212
|
+
const onLeave = () => {
|
|
213
|
+
setTargetRX(0);
|
|
214
|
+
setTargetRY(0);
|
|
215
|
+
setTargetHolo(0);
|
|
216
|
+
setTargetShine(0);
|
|
217
|
+
setTargetScale(1);
|
|
218
|
+
setHovering(false);
|
|
219
|
+
};
|
|
220
|
+
window.addEventListener("pointermove", onMove, { passive: true });
|
|
221
|
+
// Late-bound refs (Solid assigns `ref` after mount) still get the reset.
|
|
222
|
+
createEffect(() => {
|
|
223
|
+
const el = ref();
|
|
224
|
+
if (!el)
|
|
225
|
+
return;
|
|
226
|
+
el.addEventListener("pointerleave", onLeave);
|
|
227
|
+
onCleanup(() => el.removeEventListener("pointerleave", onLeave));
|
|
228
|
+
});
|
|
229
|
+
onCleanup(() => window.removeEventListener("pointermove", onMove));
|
|
230
|
+
return {
|
|
231
|
+
rotateX,
|
|
232
|
+
rotateY,
|
|
233
|
+
glareX,
|
|
234
|
+
glareY,
|
|
235
|
+
holoAngle,
|
|
236
|
+
shine,
|
|
237
|
+
scale,
|
|
238
|
+
hovering,
|
|
239
|
+
transform: () => `perspective(${perspective}px) rotateX(${rotateX()}deg) rotateY(${rotateY()}deg) scale(${scale()})`,
|
|
240
|
+
};
|
|
241
|
+
}
|
package/package.json
CHANGED