@bug-on/m3-expressive 1.3.5 → 1.3.7
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 +12 -0
- package/dist/buttons.d.mts +3 -3
- package/dist/buttons.d.ts +3 -3
- package/dist/buttons.js +110 -91
- package/dist/buttons.js.map +1 -1
- package/dist/buttons.mjs +110 -91
- package/dist/buttons.mjs.map +1 -1
- package/dist/{core-Cckt8qEm.d.ts → core-DamhB5Yd.d.ts} +1 -1
- package/dist/{core-DDfG4pym.d.mts → core-Wsfqn9_N.d.mts} +1 -1
- package/dist/core.d.mts +2 -2
- package/dist/core.d.ts +2 -2
- package/dist/core.js +1 -1
- package/dist/core.js.map +1 -1
- package/dist/core.mjs +1 -1
- package/dist/core.mjs.map +1 -1
- package/dist/{fading-blur-mask--uZbBKLj.d.ts → fading-blur-mask-CX-bwjNU.d.mts} +6 -1
- package/dist/{fading-blur-mask--uZbBKLj.d.mts → fading-blur-mask-D57UMguz.d.ts} +6 -1
- package/dist/feedback.d.mts +17 -5
- package/dist/feedback.d.ts +17 -5
- package/dist/feedback.js +1 -1
- package/dist/feedback.js.map +1 -1
- package/dist/feedback.mjs +1 -1
- package/dist/feedback.mjs.map +1 -1
- package/dist/forms.d.mts +3 -3
- package/dist/forms.d.ts +3 -3
- package/dist/forms.js +16 -6
- package/dist/forms.js.map +1 -1
- package/dist/forms.mjs +16 -6
- package/dist/forms.mjs.map +1 -1
- package/dist/{icon-button-CxyJv7UV.d.mts → icon-button-BpX9_fwZ.d.mts} +1 -1
- package/dist/{icon-button-Di8VX6ou.d.ts → icon-button-DFXp9hze.d.ts} +1 -1
- package/dist/index.d.mts +11 -10
- package/dist/index.d.ts +11 -10
- package/dist/index.js +1790 -1133
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1640 -988
- package/dist/index.mjs.map +1 -1
- package/dist/layout.d.mts +92 -23
- package/dist/layout.d.ts +92 -23
- package/dist/layout.js +118 -13
- package/dist/layout.js.map +1 -1
- package/dist/layout.mjs +118 -15
- package/dist/layout.mjs.map +1 -1
- package/dist/{md3-expressive-shapes-CPcfl_Hf.d.mts → md3-expressive-shapes-wk_98LJh.d.mts} +20 -1
- package/dist/{md3-expressive-shapes-CPcfl_Hf.d.ts → md3-expressive-shapes-wk_98LJh.d.ts} +20 -1
- package/dist/md3-xBl4wgND.d.mts +44 -0
- package/dist/md3-xBl4wgND.d.ts +44 -0
- package/dist/navigation.d.mts +5 -5
- package/dist/navigation.d.ts +5 -5
- package/dist/navigation.js +74 -21
- package/dist/navigation.js.map +1 -1
- package/dist/navigation.mjs +74 -21
- package/dist/navigation.mjs.map +1 -1
- package/dist/overlays.d.mts +4 -2
- package/dist/overlays.d.ts +4 -2
- package/dist/overlays.js +85 -44
- package/dist/overlays.js.map +1 -1
- package/dist/overlays.mjs +85 -44
- package/dist/overlays.mjs.map +1 -1
- package/dist/pickers.js +89 -56
- package/dist/pickers.js.map +1 -1
- package/dist/pickers.mjs +89 -56
- package/dist/pickers.mjs.map +1 -1
- package/dist/shapes.d.mts +150 -17
- package/dist/shapes.d.ts +150 -17
- package/dist/shapes.js +618 -126
- package/dist/shapes.js.map +1 -1
- package/dist/shapes.mjs +614 -127
- package/dist/shapes.mjs.map +1 -1
- package/dist/{side-sheet-modal-Bd5Qqvp9.d.ts → side-sheet-modal-BfdgYjd3.d.ts} +70 -0
- package/dist/{side-sheet-modal-64FGhDxL.d.mts → side-sheet-modal-kLfHINFZ.d.mts} +70 -0
- package/dist/{split-button-trailing-uncheckable-CGzk7KcG.d.ts → split-button-trailing-uncheckable-BpyIlOdn.d.ts} +7 -3
- package/dist/{split-button-trailing-uncheckable-gAz6OAIi.d.mts → split-button-trailing-uncheckable-CpT8iPpr.d.mts} +7 -3
- package/dist/{text-field-DARNdj14.d.ts → text-field-Ct-LT_2T.d.ts} +23 -4
- package/dist/{text-field-4OlT9o8s.d.mts → text-field-D5xiGn-q.d.mts} +23 -4
- package/llms-full.txt +25 -3
- package/llms.txt +4 -1
- package/package.json +1 -1
- package/dist/md3-BQhRygSi.d.mts +0 -31
- package/dist/md3-BQhRygSi.d.ts +0 -31
package/dist/shapes.mjs
CHANGED
|
@@ -36,6 +36,13 @@ function transformFeature(feature, fn) {
|
|
|
36
36
|
}
|
|
37
37
|
return edgeFeature(cubics);
|
|
38
38
|
}
|
|
39
|
+
function reverseFeature(feature) {
|
|
40
|
+
const cubics = feature.cubics.slice().reverse().map((c) => c.reverse());
|
|
41
|
+
if (feature.type === "corner") {
|
|
42
|
+
return cornerFeature(cubics, feature.convex);
|
|
43
|
+
}
|
|
44
|
+
return edgeFeature(cubics);
|
|
45
|
+
}
|
|
39
46
|
|
|
40
47
|
// src/shapes/math/corner-rounding.ts
|
|
41
48
|
function cornerRounding(radius = 0, smoothing = 0) {
|
|
@@ -80,6 +87,10 @@ function convex(prevX, prevY, currX, currY, nextX, nextY) {
|
|
|
80
87
|
const v2y = nextY - currY;
|
|
81
88
|
return v1x * v2y - v1y * v2x > 0;
|
|
82
89
|
}
|
|
90
|
+
function formatCoordinate(v) {
|
|
91
|
+
const rounded = Math.round(v * 1e4) / 1e4;
|
|
92
|
+
return (rounded === 0 ? 0 : rounded).toString();
|
|
93
|
+
}
|
|
83
94
|
|
|
84
95
|
// src/shapes/math/point.ts
|
|
85
96
|
function point(x, y) {
|
|
@@ -179,9 +190,16 @@ var Cubic = class _Cubic {
|
|
|
179
190
|
*/
|
|
180
191
|
pointOnCurve(t) {
|
|
181
192
|
const u = 1 - t;
|
|
193
|
+
const uu = u * u;
|
|
194
|
+
const tt = t * t;
|
|
195
|
+
const uuu = uu * u;
|
|
196
|
+
const ttt = tt * t;
|
|
197
|
+
const c0 = 3 * t * uu;
|
|
198
|
+
const c1 = 3 * tt * u;
|
|
199
|
+
const p = this.points;
|
|
182
200
|
return point(
|
|
183
|
-
|
|
184
|
-
|
|
201
|
+
p[0] * uuu + p[2] * c0 + p[4] * c1 + p[6] * ttt,
|
|
202
|
+
p[1] * uuu + p[3] * c0 + p[5] * c1 + p[7] * ttt
|
|
185
203
|
);
|
|
186
204
|
}
|
|
187
205
|
/**
|
|
@@ -404,13 +422,17 @@ var MutableCubic = class extends Cubic {
|
|
|
404
422
|
* @param progress - Interpolation factor [0, 1]
|
|
405
423
|
*/
|
|
406
424
|
interpolate(c1, c2, progress) {
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
425
|
+
const p1 = c1.points;
|
|
426
|
+
const p2 = c2.points;
|
|
427
|
+
const p = this.points;
|
|
428
|
+
p[0] = p1[0] + (p2[0] - p1[0]) * progress;
|
|
429
|
+
p[1] = p1[1] + (p2[1] - p1[1]) * progress;
|
|
430
|
+
p[2] = p1[2] + (p2[2] - p1[2]) * progress;
|
|
431
|
+
p[3] = p1[3] + (p2[3] - p1[3]) * progress;
|
|
432
|
+
p[4] = p1[4] + (p2[4] - p1[4]) * progress;
|
|
433
|
+
p[5] = p1[5] + (p2[5] - p1[5]) * progress;
|
|
434
|
+
p[6] = p1[6] + (p2[6] - p1[6]) * progress;
|
|
435
|
+
p[7] = p1[7] + (p2[7] - p1[7]) * progress;
|
|
414
436
|
}
|
|
415
437
|
/**
|
|
416
438
|
* Mutably applies a PointTransformer to all points.
|
|
@@ -822,6 +844,36 @@ var RoundedPolygon = class _RoundedPolygon {
|
|
|
822
844
|
this.centerY + d
|
|
823
845
|
];
|
|
824
846
|
}
|
|
847
|
+
/**
|
|
848
|
+
* Calculates the approximate signed area of the polygon using anchor points.
|
|
849
|
+
* Returns > 0 for Clockwise (in screen coords, y down), < 0 for Counter-Clockwise.
|
|
850
|
+
*/
|
|
851
|
+
calculateSignedArea() {
|
|
852
|
+
let area = 0;
|
|
853
|
+
const n = this.cubics.length;
|
|
854
|
+
for (let i = 0; i < n; i++) {
|
|
855
|
+
const c = this.cubics[i];
|
|
856
|
+
area += c.anchor0X * c.anchor1Y - c.anchor1X * c.anchor0Y;
|
|
857
|
+
}
|
|
858
|
+
return area / 2;
|
|
859
|
+
}
|
|
860
|
+
/**
|
|
861
|
+
* Returns true if the polygon outline runs in Clockwise order (screen coords).
|
|
862
|
+
*/
|
|
863
|
+
isClockwise() {
|
|
864
|
+
return this.calculateSignedArea() > 0;
|
|
865
|
+
}
|
|
866
|
+
/**
|
|
867
|
+
* Returns a reversed copy of the polygon with reversed traversal order.
|
|
868
|
+
* Flips the winding direction (CW ↔ CCW).
|
|
869
|
+
*/
|
|
870
|
+
reversed() {
|
|
871
|
+
const reversedFeatures = this.features.slice().reverse().map((f) => reverseFeature(f));
|
|
872
|
+
return new _RoundedPolygon(
|
|
873
|
+
reversedFeatures,
|
|
874
|
+
point(this.centerX, this.centerY)
|
|
875
|
+
);
|
|
876
|
+
}
|
|
825
877
|
};
|
|
826
878
|
function buildCubicsList(features) {
|
|
827
879
|
const result = [];
|
|
@@ -890,10 +942,21 @@ function validateContiguity(cubics) {
|
|
|
890
942
|
|
|
891
943
|
// src/shapes/catalog/md3-expressive-shapes.ts
|
|
892
944
|
function createShape(pts) {
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
945
|
+
let rawArea = 0;
|
|
946
|
+
for (const p of pts) {
|
|
947
|
+
rawArea += p[0] * p[7] - p[6] * p[1];
|
|
948
|
+
}
|
|
949
|
+
const normalizedPts = rawArea < 0 ? pts.slice().reverse().map((p) => [p[6], p[7], p[4], p[5], p[2], p[3], p[0], p[1]]) : pts;
|
|
950
|
+
const cubics = normalizedPts.map((p) => new Cubic(p));
|
|
951
|
+
const n = cubics.length;
|
|
952
|
+
const features = cubics.map((c, i) => {
|
|
953
|
+
const prev = cubics[(i - 1 + n) % n].pointOnCurve(0.5);
|
|
954
|
+
const curr = c.pointOnCurve(0.5);
|
|
955
|
+
const next = cubics[(i + 1) % n].pointOnCurve(0.5);
|
|
956
|
+
const isConvex = convex(prev.x, prev.y, curr.x, curr.y, next.x, next.y);
|
|
957
|
+
return cornerFeature([c], isConvex);
|
|
958
|
+
});
|
|
959
|
+
return RoundedPolygon.fromFeatures(features);
|
|
897
960
|
}
|
|
898
961
|
var archShape = createShape([
|
|
899
962
|
[1, 0.83461, 1, 0.8547, 1, 0.86477, 0.99898, 0.87322],
|
|
@@ -1991,6 +2054,94 @@ function resolveShape(ref) {
|
|
|
1991
2054
|
return shape;
|
|
1992
2055
|
}
|
|
1993
2056
|
|
|
2057
|
+
// src/shapes/catalog/shape-pairings.ts
|
|
2058
|
+
var MD3_SHAPE_PAIRINGS = {
|
|
2059
|
+
// ─── Organic & Floral ──────────────────────────────────────────
|
|
2060
|
+
circle: "flower",
|
|
2061
|
+
flower: "sunny",
|
|
2062
|
+
sunny: "verySunny",
|
|
2063
|
+
verySunny: "flower",
|
|
2064
|
+
bun: "clamshell",
|
|
2065
|
+
clamshell: "bun",
|
|
2066
|
+
puffy: "circle",
|
|
2067
|
+
// ─── Starburst & Explosive ─────────────────────────────────────
|
|
2068
|
+
burst: "softBurst",
|
|
2069
|
+
softBurst: "boom",
|
|
2070
|
+
boom: "softBoom",
|
|
2071
|
+
softBoom: "burst",
|
|
2072
|
+
cookie12Sided: "softBurst",
|
|
2073
|
+
// ─── Clover, Gem & Diamond ─────────────────────────────────────
|
|
2074
|
+
diamond: "puffyDiamond",
|
|
2075
|
+
puffyDiamond: "diamond",
|
|
2076
|
+
clover4Leaf: "clover8Leaf",
|
|
2077
|
+
clover8Leaf: "clover4Leaf",
|
|
2078
|
+
gem: "diamond",
|
|
2079
|
+
heart: "clover4Leaf",
|
|
2080
|
+
// ─── Cookies & Geometric Multi-sided ───────────────────────────
|
|
2081
|
+
cookie4Sided: "cookie6Sided",
|
|
2082
|
+
cookie6Sided: "cookie9Sided",
|
|
2083
|
+
cookie7Sided: "cookie12Sided",
|
|
2084
|
+
cookie9Sided: "cookie7Sided",
|
|
2085
|
+
square: "cookie4Sided",
|
|
2086
|
+
// ─── Rounded & Architectural ───────────────────────────────────
|
|
2087
|
+
pill: "oval",
|
|
2088
|
+
oval: "arch",
|
|
2089
|
+
arch: "semiCircle",
|
|
2090
|
+
semiCircle: "pill",
|
|
2091
|
+
slanted: "arch",
|
|
2092
|
+
// ─── Polygonal, Directional & Pixel ────────────────────────────
|
|
2093
|
+
triangle: "arrow",
|
|
2094
|
+
arrow: "pentagon",
|
|
2095
|
+
pentagon: "triangle",
|
|
2096
|
+
fan: "ghostish",
|
|
2097
|
+
ghostish: "fan",
|
|
2098
|
+
pixelTriangle: "pixelCircle",
|
|
2099
|
+
pixelCircle: "circle"
|
|
2100
|
+
};
|
|
2101
|
+
var MD3_SHAPE_FAMILIES = {
|
|
2102
|
+
organic: [
|
|
2103
|
+
"circle",
|
|
2104
|
+
"flower",
|
|
2105
|
+
"sunny",
|
|
2106
|
+
"verySunny",
|
|
2107
|
+
"bun",
|
|
2108
|
+
"clamshell",
|
|
2109
|
+
"puffy"
|
|
2110
|
+
],
|
|
2111
|
+
starburst: ["burst", "softBurst", "boom", "softBoom", "cookie12Sided"],
|
|
2112
|
+
cloverAndGems: [
|
|
2113
|
+
"diamond",
|
|
2114
|
+
"puffyDiamond",
|
|
2115
|
+
"clover4Leaf",
|
|
2116
|
+
"clover8Leaf",
|
|
2117
|
+
"gem",
|
|
2118
|
+
"heart"
|
|
2119
|
+
],
|
|
2120
|
+
cookiesAndPolygons: [
|
|
2121
|
+
"cookie4Sided",
|
|
2122
|
+
"cookie6Sided",
|
|
2123
|
+
"cookie7Sided",
|
|
2124
|
+
"cookie9Sided",
|
|
2125
|
+
"square"
|
|
2126
|
+
],
|
|
2127
|
+
roundedContainers: ["pill", "oval", "arch", "semiCircle", "slanted"],
|
|
2128
|
+
directionalAndPixel: [
|
|
2129
|
+
"triangle",
|
|
2130
|
+
"arrow",
|
|
2131
|
+
"pentagon",
|
|
2132
|
+
"fan",
|
|
2133
|
+
"ghostish",
|
|
2134
|
+
"pixelTriangle",
|
|
2135
|
+
"pixelCircle"
|
|
2136
|
+
]
|
|
2137
|
+
};
|
|
2138
|
+
function getRecommendedMorphShape(shape) {
|
|
2139
|
+
if (typeof shape === "string" && shape in MD3_SHAPE_PAIRINGS) {
|
|
2140
|
+
return MD3_SHAPE_PAIRINGS[shape];
|
|
2141
|
+
}
|
|
2142
|
+
return "circle";
|
|
2143
|
+
}
|
|
2144
|
+
|
|
1994
2145
|
// src/shapes/catalog/shape-tokens.ts
|
|
1995
2146
|
var MD3CornerRadius = {
|
|
1996
2147
|
/** 0dp — Sharp corners */
|
|
@@ -2156,6 +2307,56 @@ function pillStarVertices(width, height, numVerticesPerRadius, innerRadiusRatio,
|
|
|
2156
2307
|
return result;
|
|
2157
2308
|
}
|
|
2158
2309
|
|
|
2310
|
+
// src/shapes/morph/feature-mapping.ts
|
|
2311
|
+
function angleDiff(a1, a2) {
|
|
2312
|
+
let diff = Math.abs(a1 - a2);
|
|
2313
|
+
if (diff > Math.PI) diff = 2 * Math.PI - diff;
|
|
2314
|
+
return diff;
|
|
2315
|
+
}
|
|
2316
|
+
var TWO_PI_SQ = 4 * Math.PI * Math.PI;
|
|
2317
|
+
function featureMapper(features1, features2) {
|
|
2318
|
+
const convex1 = features1.filter((f) => f.convex);
|
|
2319
|
+
const convex2 = features2.filter((f) => f.convex);
|
|
2320
|
+
const set1 = convex1.length > 0 ? convex1 : features1;
|
|
2321
|
+
const set2 = convex2.length > 0 ? convex2 : features2;
|
|
2322
|
+
if (set1.length === 0 || set2.length === 0) {
|
|
2323
|
+
return { map: (v) => v, mapBack: (v) => v };
|
|
2324
|
+
}
|
|
2325
|
+
let bestShift = 0;
|
|
2326
|
+
let minCost = Number.MAX_VALUE;
|
|
2327
|
+
for (const f1 of set1) {
|
|
2328
|
+
for (const f2 of set2) {
|
|
2329
|
+
const shift = positiveModulo(f2.progress - f1.progress, 1);
|
|
2330
|
+
let cost = 0;
|
|
2331
|
+
for (const c1 of set1) {
|
|
2332
|
+
const expected = positiveModulo(c1.progress + shift, 1);
|
|
2333
|
+
let dMin = Number.MAX_VALUE;
|
|
2334
|
+
let matchedC2 = set2[0];
|
|
2335
|
+
for (const c2 of set2) {
|
|
2336
|
+
const diff = Math.abs(expected - c2.progress);
|
|
2337
|
+
const d = diff > 0.5 ? 1 - diff : diff;
|
|
2338
|
+
if (d < dMin) {
|
|
2339
|
+
dMin = d;
|
|
2340
|
+
matchedC2 = c2;
|
|
2341
|
+
}
|
|
2342
|
+
}
|
|
2343
|
+
const progErrorSq = dMin * dMin * TWO_PI_SQ;
|
|
2344
|
+
const angDiff = c1.angle !== void 0 && matchedC2.angle !== void 0 ? angleDiff(c1.angle, matchedC2.angle) : 0;
|
|
2345
|
+
cost += progErrorSq + angDiff * angDiff;
|
|
2346
|
+
if (cost >= minCost) break;
|
|
2347
|
+
}
|
|
2348
|
+
if (cost < minCost) {
|
|
2349
|
+
minCost = cost;
|
|
2350
|
+
bestShift = shift;
|
|
2351
|
+
}
|
|
2352
|
+
}
|
|
2353
|
+
}
|
|
2354
|
+
return {
|
|
2355
|
+
map: (v) => positiveModulo(v + bestShift, 1),
|
|
2356
|
+
mapBack: (v) => positiveModulo(v - bestShift, 1)
|
|
2357
|
+
};
|
|
2358
|
+
}
|
|
2359
|
+
|
|
2159
2360
|
// src/shapes/morph/float-mapping.ts
|
|
2160
2361
|
function mapSegments(value, segments) {
|
|
2161
2362
|
for (const seg of segments) {
|
|
@@ -2200,32 +2401,6 @@ function createDoubleMapper(fromValues, toValues) {
|
|
|
2200
2401
|
};
|
|
2201
2402
|
}
|
|
2202
2403
|
|
|
2203
|
-
// src/shapes/morph/feature-mapping.ts
|
|
2204
|
-
function featureMapper(features1, features2) {
|
|
2205
|
-
const convex1 = features1.filter((f) => f.convex);
|
|
2206
|
-
const convex2 = features2.filter((f) => f.convex);
|
|
2207
|
-
if (convex1.length === 0 || convex2.length === 0) {
|
|
2208
|
-
return createDoubleMapper([], []);
|
|
2209
|
-
}
|
|
2210
|
-
const fromValues = [];
|
|
2211
|
-
const toValues = [];
|
|
2212
|
-
for (const f1 of convex1) {
|
|
2213
|
-
let bestDist = Number.MAX_VALUE;
|
|
2214
|
-
let bestProgress = 0;
|
|
2215
|
-
for (const f2 of convex2) {
|
|
2216
|
-
const diff = Math.abs(f1.progress - f2.progress);
|
|
2217
|
-
const dist = Math.min(diff, 1 - diff);
|
|
2218
|
-
if (dist < bestDist) {
|
|
2219
|
-
bestDist = dist;
|
|
2220
|
-
bestProgress = f2.progress;
|
|
2221
|
-
}
|
|
2222
|
-
}
|
|
2223
|
-
fromValues.push(f1.progress);
|
|
2224
|
-
toValues.push(bestProgress);
|
|
2225
|
-
}
|
|
2226
|
-
return createDoubleMapper(fromValues, toValues);
|
|
2227
|
-
}
|
|
2228
|
-
|
|
2229
2404
|
// src/shapes/morph/polygon-measure.ts
|
|
2230
2405
|
var MeasuredPolygon = class _MeasuredPolygon {
|
|
2231
2406
|
constructor(measuredCubics, features) {
|
|
@@ -2259,9 +2434,8 @@ var MeasuredPolygon = class _MeasuredPolygon {
|
|
|
2259
2434
|
const t = segLen > 0 ? (cutPoint - cutCubic.startOutlineProgress) / segLen : 0;
|
|
2260
2435
|
const [left, right] = cutCubic.cubic.split(t);
|
|
2261
2436
|
const shift = (p) => {
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
return s;
|
|
2437
|
+
const s = p - cutPoint;
|
|
2438
|
+
return s < 0 ? s + 1 : s;
|
|
2265
2439
|
};
|
|
2266
2440
|
if (!right.zeroLength()) {
|
|
2267
2441
|
result.push({
|
|
@@ -2270,9 +2444,19 @@ var MeasuredPolygon = class _MeasuredPolygon {
|
|
|
2270
2444
|
endOutlineProgress: shift(cutCubic.endOutlineProgress)
|
|
2271
2445
|
});
|
|
2272
2446
|
}
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
const mc = this.measuredCubics[
|
|
2447
|
+
const totalCubics = this.measuredCubics.length;
|
|
2448
|
+
for (let i = cutCubicIdx + 1; i < totalCubics; i++) {
|
|
2449
|
+
const mc = this.measuredCubics[i];
|
|
2450
|
+
if (!mc.cubic.zeroLength()) {
|
|
2451
|
+
result.push({
|
|
2452
|
+
cubic: mc.cubic,
|
|
2453
|
+
startOutlineProgress: shift(mc.startOutlineProgress),
|
|
2454
|
+
endOutlineProgress: shift(mc.endOutlineProgress)
|
|
2455
|
+
});
|
|
2456
|
+
}
|
|
2457
|
+
}
|
|
2458
|
+
for (let i = 0; i < cutCubicIdx; i++) {
|
|
2459
|
+
const mc = this.measuredCubics[i];
|
|
2276
2460
|
if (!mc.cubic.zeroLength()) {
|
|
2277
2461
|
result.push({
|
|
2278
2462
|
cubic: mc.cubic,
|
|
@@ -2354,7 +2538,16 @@ var MeasuredPolygon = class _MeasuredPolygon {
|
|
|
2354
2538
|
const mc = measured[Math.min(cubicIdx + middleCubicIdx, measured.length - 1)];
|
|
2355
2539
|
if (mc) {
|
|
2356
2540
|
const midProgress = (mc.startOutlineProgress + mc.endOutlineProgress) / 2;
|
|
2357
|
-
|
|
2541
|
+
const pt = mc.cubic.pointOnCurve(0.5);
|
|
2542
|
+
const angle = Math.atan2(
|
|
2543
|
+
pt.y - polygon.centerY,
|
|
2544
|
+
pt.x - polygon.centerX
|
|
2545
|
+
);
|
|
2546
|
+
featureList.push({
|
|
2547
|
+
progress: midProgress,
|
|
2548
|
+
convex: feature.convex,
|
|
2549
|
+
angle
|
|
2550
|
+
});
|
|
2358
2551
|
}
|
|
2359
2552
|
}
|
|
2360
2553
|
cubicIdx += feature.cubics.length;
|
|
@@ -2364,18 +2557,30 @@ var MeasuredPolygon = class _MeasuredPolygon {
|
|
|
2364
2557
|
};
|
|
2365
2558
|
function approximateLength(cubic) {
|
|
2366
2559
|
let len = 0;
|
|
2367
|
-
|
|
2560
|
+
const p = cubic.points;
|
|
2561
|
+
let prevX = p[0];
|
|
2562
|
+
let prevY = p[1];
|
|
2368
2563
|
const steps = 4;
|
|
2369
2564
|
for (let i = 1; i <= steps; i++) {
|
|
2370
|
-
const
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2565
|
+
const t = i / steps;
|
|
2566
|
+
const u = 1 - t;
|
|
2567
|
+
const uu = u * u;
|
|
2568
|
+
const tt = t * t;
|
|
2569
|
+
const uuu = uu * u;
|
|
2570
|
+
const ttt = tt * t;
|
|
2571
|
+
const c0 = 3 * t * uu;
|
|
2572
|
+
const c1 = 3 * tt * u;
|
|
2573
|
+
const px = p[0] * uuu + p[2] * c0 + p[4] * c1 + p[6] * ttt;
|
|
2574
|
+
const py = p[1] * uuu + p[3] * c0 + p[5] * c1 + p[7] * ttt;
|
|
2575
|
+
len += distance(px - prevX, py - prevY);
|
|
2576
|
+
prevX = px;
|
|
2577
|
+
prevY = py;
|
|
2374
2578
|
}
|
|
2375
2579
|
return len;
|
|
2376
2580
|
}
|
|
2377
2581
|
|
|
2378
2582
|
// src/shapes/morph/morph.ts
|
|
2583
|
+
var morphMatchCache = /* @__PURE__ */ new WeakMap();
|
|
2379
2584
|
var Morph = class _Morph {
|
|
2380
2585
|
/**
|
|
2381
2586
|
* Creates a Morph between two shapes.
|
|
@@ -2391,23 +2596,79 @@ var Morph = class _Morph {
|
|
|
2391
2596
|
* Returns the interpolated shape at the given progress as a list of Cubics.
|
|
2392
2597
|
*
|
|
2393
2598
|
* Note: This allocates a new list. For performance-critical animation loops,
|
|
2394
|
-
* use {@link forEachCubic} instead.
|
|
2599
|
+
* use {@link toSvgPath} or {@link forEachCubic} instead.
|
|
2395
2600
|
*
|
|
2396
2601
|
* @param progress - Value in [0, 1]. 0 = start shape, 1 = end shape.
|
|
2397
2602
|
* Values outside [0, 1] produce exaggerated shapes (useful for bounce/overshoot).
|
|
2398
2603
|
* @returns List of interpolated Cubic curves forming the morphed shape
|
|
2399
2604
|
*/
|
|
2400
2605
|
asCubics(progress) {
|
|
2401
|
-
const
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2606
|
+
const match = this.morphMatch;
|
|
2607
|
+
const len = match.length;
|
|
2608
|
+
const result = new Array(len);
|
|
2609
|
+
for (let i = 0; i < len; i++) {
|
|
2610
|
+
const [c1, c2] = match[i];
|
|
2611
|
+
const p1 = c1.points;
|
|
2612
|
+
const p2 = c2.points;
|
|
2613
|
+
result[i] = new Cubic([
|
|
2614
|
+
p1[0] + (p2[0] - p1[0]) * progress,
|
|
2615
|
+
p1[1] + (p2[1] - p1[1]) * progress,
|
|
2616
|
+
p1[2] + (p2[2] - p1[2]) * progress,
|
|
2617
|
+
p1[3] + (p2[3] - p1[3]) * progress,
|
|
2618
|
+
p1[4] + (p2[4] - p1[4]) * progress,
|
|
2619
|
+
p1[5] + (p2[5] - p1[5]) * progress,
|
|
2620
|
+
p1[6] + (p2[6] - p1[6]) * progress,
|
|
2621
|
+
p1[7] + (p2[7] - p1[7]) * progress
|
|
2622
|
+
]);
|
|
2408
2623
|
}
|
|
2409
2624
|
return result;
|
|
2410
2625
|
}
|
|
2626
|
+
/**
|
|
2627
|
+
* Returns an SVG path data string for the morphed shape at the given progress.
|
|
2628
|
+
* Directly evaluates coordinates without allocating intermediate Cubic objects.
|
|
2629
|
+
*
|
|
2630
|
+
* @param progress - Value in [0, 1]
|
|
2631
|
+
* @param width - Target pixel width (default: 1)
|
|
2632
|
+
* @param height - Target pixel height (default: 1)
|
|
2633
|
+
*/
|
|
2634
|
+
toSvgPath(progress, width = 1, height = 1) {
|
|
2635
|
+
const match = this.morphMatch;
|
|
2636
|
+
const len = match.length;
|
|
2637
|
+
if (len === 0) return "";
|
|
2638
|
+
const paddingX = width * 0.015;
|
|
2639
|
+
const paddingY = height * 0.015;
|
|
2640
|
+
const sx = width - 2 * paddingX;
|
|
2641
|
+
const sy = height - 2 * paddingY;
|
|
2642
|
+
const [firstC1, firstC2] = match[0];
|
|
2643
|
+
const f1 = firstC1.points;
|
|
2644
|
+
const f2 = firstC2.points;
|
|
2645
|
+
const a0x = f1[0] + (f2[0] - f1[0]) * progress;
|
|
2646
|
+
const a0y = f1[1] + (f2[1] - f1[1]) * progress;
|
|
2647
|
+
let d = `M ${formatCoordinate(paddingX + a0x * sx)} ${formatCoordinate(paddingY + sy * (1 - a0y))}`;
|
|
2648
|
+
for (let i = 0; i < len; i++) {
|
|
2649
|
+
const [c1, c2] = match[i];
|
|
2650
|
+
const p1 = c1.points;
|
|
2651
|
+
const p2 = c2.points;
|
|
2652
|
+
const c0x = p1[2] + (p2[2] - p1[2]) * progress;
|
|
2653
|
+
const c0y = p1[3] + (p2[3] - p1[3]) * progress;
|
|
2654
|
+
const c1x = p1[4] + (p2[4] - p1[4]) * progress;
|
|
2655
|
+
const c1y = p1[5] + (p2[5] - p1[5]) * progress;
|
|
2656
|
+
const a1x = p1[6] + (p2[6] - p1[6]) * progress;
|
|
2657
|
+
const a1y = p1[7] + (p2[7] - p1[7]) * progress;
|
|
2658
|
+
d += ` C ${formatCoordinate(paddingX + c0x * sx)} ${formatCoordinate(paddingY + sy * (1 - c0y))},${formatCoordinate(paddingX + c1x * sx)} ${formatCoordinate(paddingY + sy * (1 - c1y))},${formatCoordinate(paddingX + a1x * sx)} ${formatCoordinate(paddingY + sy * (1 - a1y))}`;
|
|
2659
|
+
}
|
|
2660
|
+
return `${d} Z`;
|
|
2661
|
+
}
|
|
2662
|
+
/**
|
|
2663
|
+
* Returns a CSS clip-path value for the morphed shape at the given progress.
|
|
2664
|
+
*
|
|
2665
|
+
* @param progress - Value in [0, 1]
|
|
2666
|
+
* @param width - Element width in pixels
|
|
2667
|
+
* @param height - Element height in pixels
|
|
2668
|
+
*/
|
|
2669
|
+
toClipPath(progress, width, height) {
|
|
2670
|
+
return `path('${this.toSvgPath(progress, width, height)}')`;
|
|
2671
|
+
}
|
|
2411
2672
|
/**
|
|
2412
2673
|
* Iterates over the morphed cubics without allocating new Cubic instances.
|
|
2413
2674
|
* Reuses a single MutableCubic for each callback invocation.
|
|
@@ -2481,8 +2742,15 @@ var Morph = class _Morph {
|
|
|
2481
2742
|
* @internal
|
|
2482
2743
|
*/
|
|
2483
2744
|
static match(p1, p2) {
|
|
2484
|
-
|
|
2485
|
-
|
|
2745
|
+
let cachedMap = morphMatchCache.get(p1);
|
|
2746
|
+
if (cachedMap) {
|
|
2747
|
+
const cached = cachedMap.get(p2);
|
|
2748
|
+
if (cached) return cached;
|
|
2749
|
+
}
|
|
2750
|
+
const poly1 = p1.isClockwise() ? p1 : p1.reversed();
|
|
2751
|
+
const poly2 = p2.isClockwise() ? p2 : p2.reversed();
|
|
2752
|
+
const measured1 = MeasuredPolygon.measurePolygon(poly1);
|
|
2753
|
+
const measured2 = MeasuredPolygon.measurePolygon(poly2);
|
|
2486
2754
|
const doubleMapper = featureMapper(measured1.features, measured2.features);
|
|
2487
2755
|
const polygon2CutPoint = doubleMapper.map(0);
|
|
2488
2756
|
const bs1 = measured1;
|
|
@@ -2493,11 +2761,12 @@ var Morph = class _Morph {
|
|
|
2493
2761
|
let b2 = bs2.getOrNull(i2++);
|
|
2494
2762
|
while (b1 !== null && b2 !== null) {
|
|
2495
2763
|
const b1a = i1 >= bs1.size ? 1 : b1.endOutlineProgress;
|
|
2496
|
-
const
|
|
2497
|
-
const b2a = doubleMapper.mapBack(b2aRaw);
|
|
2764
|
+
const b2a = i2 >= bs2.size ? 1 : b2.endOutlineProgress;
|
|
2498
2765
|
const minb = Math.min(b1a, b2a);
|
|
2499
|
-
let seg1 = b1
|
|
2500
|
-
let
|
|
2766
|
+
let seg1 = b1;
|
|
2767
|
+
let newb1 = bs1.getOrNull(i1);
|
|
2768
|
+
let seg2 = b2;
|
|
2769
|
+
let newb2 = bs2.getOrNull(i2);
|
|
2501
2770
|
if (b1a > minb + ANGLE_EPSILON) {
|
|
2502
2771
|
const [cut, rest] = bs1.cutAtProgress(b1, minb);
|
|
2503
2772
|
seg1 = cut;
|
|
@@ -2506,11 +2775,7 @@ var Morph = class _Morph {
|
|
|
2506
2775
|
i1++;
|
|
2507
2776
|
}
|
|
2508
2777
|
if (b2a > minb + ANGLE_EPSILON) {
|
|
2509
|
-
const
|
|
2510
|
-
doubleMapper.map(minb) - polygon2CutPoint,
|
|
2511
|
-
1
|
|
2512
|
-
);
|
|
2513
|
-
const [cut, rest] = bs2.cutAtProgress(b2, targetProgress);
|
|
2778
|
+
const [cut, rest] = bs2.cutAtProgress(b2, minb);
|
|
2514
2779
|
seg2 = cut;
|
|
2515
2780
|
newb2 = rest;
|
|
2516
2781
|
} else {
|
|
@@ -2520,44 +2785,83 @@ var Morph = class _Morph {
|
|
|
2520
2785
|
b1 = newb1;
|
|
2521
2786
|
b2 = newb2;
|
|
2522
2787
|
}
|
|
2788
|
+
if (!cachedMap) {
|
|
2789
|
+
cachedMap = /* @__PURE__ */ new WeakMap();
|
|
2790
|
+
morphMatchCache.set(p1, cachedMap);
|
|
2791
|
+
}
|
|
2792
|
+
cachedMap.set(p2, result);
|
|
2523
2793
|
return result;
|
|
2524
2794
|
}
|
|
2525
2795
|
};
|
|
2526
2796
|
|
|
2527
2797
|
// src/shapes/render/shape-rendering.ts
|
|
2528
2798
|
function toSvgPath(cubics, width = 1, height = 1) {
|
|
2529
|
-
|
|
2799
|
+
const len = cubics.length;
|
|
2800
|
+
if (len === 0) return "";
|
|
2530
2801
|
const paddingX = width * 0.015;
|
|
2531
2802
|
const paddingY = height * 0.015;
|
|
2532
2803
|
const sx = width - 2 * paddingX;
|
|
2533
2804
|
const sy = height - 2 * paddingY;
|
|
2534
|
-
const
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
parts.push(
|
|
2540
|
-
`C ${fmt(paddingX + c.control0X * sx)} ${fmt(flipY(c.control0Y))},${fmt(paddingX + c.control1X * sx)} ${fmt(flipY(c.control1Y))},${fmt(paddingX + c.anchor1X * sx)} ${fmt(flipY(c.anchor1Y))}`
|
|
2541
|
-
);
|
|
2805
|
+
const c0 = cubics[0];
|
|
2806
|
+
let d = `M ${formatCoordinate(paddingX + c0.anchor0X * sx)} ${formatCoordinate(paddingY + sy * (1 - c0.anchor0Y))}`;
|
|
2807
|
+
for (let i = 0; i < len; i++) {
|
|
2808
|
+
const c = cubics[i];
|
|
2809
|
+
d += ` C ${formatCoordinate(paddingX + c.control0X * sx)} ${formatCoordinate(paddingY + sy * (1 - c.control0Y))},${formatCoordinate(paddingX + c.control1X * sx)} ${formatCoordinate(paddingY + sy * (1 - c.control1Y))},${formatCoordinate(paddingX + c.anchor1X * sx)} ${formatCoordinate(paddingY + sy * (1 - c.anchor1Y))}`;
|
|
2542
2810
|
}
|
|
2543
|
-
|
|
2544
|
-
return parts.join(" ");
|
|
2811
|
+
return `${d} Z`;
|
|
2545
2812
|
}
|
|
2546
2813
|
function toClipPath(polygon, width, height) {
|
|
2547
2814
|
const pathData = toSvgPath(polygon.cubics, width, height);
|
|
2548
2815
|
return `path('${pathData}')`;
|
|
2549
2816
|
}
|
|
2550
2817
|
function interpolatePath(morph, progress, width, height) {
|
|
2551
|
-
|
|
2552
|
-
return toClipPath({ cubics }, width, height);
|
|
2553
|
-
}
|
|
2554
|
-
function fmt(v) {
|
|
2555
|
-
return Number(v.toFixed(4)).toString();
|
|
2818
|
+
return morph.toClipPath(progress, width, height);
|
|
2556
2819
|
}
|
|
2820
|
+
|
|
2821
|
+
// src/ui/shape-media/animation-utils.ts
|
|
2557
2822
|
function prefersReducedMotion() {
|
|
2558
|
-
if (typeof window === "undefined")
|
|
2823
|
+
if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
|
|
2824
|
+
return false;
|
|
2825
|
+
}
|
|
2559
2826
|
return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
2560
2827
|
}
|
|
2828
|
+
function easeCubicInOut(t) {
|
|
2829
|
+
return t < 0.5 ? 4 * t * t * t : 1 - (-2 * t + 2) ** 3 / 2;
|
|
2830
|
+
}
|
|
2831
|
+
function cubicBezier(x1, y1, x2, y2) {
|
|
2832
|
+
return (t) => {
|
|
2833
|
+
if (t === 0 || t === 1) return t;
|
|
2834
|
+
let tParam = t;
|
|
2835
|
+
for (let i = 0; i < 8; i++) {
|
|
2836
|
+
const x = 3 * (1 - tParam) ** 2 * tParam * x1 + 3 * (1 - tParam) * tParam ** 2 * x2 + tParam ** 3;
|
|
2837
|
+
const dx = 3 * (1 - tParam) ** 2 * x1 + 6 * (1 - tParam) * tParam * (x2 - x1) + 3 * tParam ** 2 * (1 - x2);
|
|
2838
|
+
if (Math.abs(dx) < 1e-6) break;
|
|
2839
|
+
tParam -= (x - t) / dx;
|
|
2840
|
+
}
|
|
2841
|
+
return 3 * (1 - tParam) ** 2 * tParam * y1 + 3 * (1 - tParam) * tParam ** 2 * y2 + tParam ** 3;
|
|
2842
|
+
};
|
|
2843
|
+
}
|
|
2844
|
+
function getEasingFunction(easing) {
|
|
2845
|
+
if (Array.isArray(easing)) {
|
|
2846
|
+
if (easing.length === 4) {
|
|
2847
|
+
const [x1, y1, x2, y2] = easing;
|
|
2848
|
+
return cubicBezier(x1, y1, x2, y2);
|
|
2849
|
+
}
|
|
2850
|
+
return easeCubicInOut;
|
|
2851
|
+
}
|
|
2852
|
+
switch (easing) {
|
|
2853
|
+
case "linear":
|
|
2854
|
+
return (t) => t;
|
|
2855
|
+
case "ease-in":
|
|
2856
|
+
return (t) => t * t * t;
|
|
2857
|
+
case "ease-out":
|
|
2858
|
+
return (t) => 1 - (1 - t) ** 3;
|
|
2859
|
+
default:
|
|
2860
|
+
return easeCubicInOut;
|
|
2861
|
+
}
|
|
2862
|
+
}
|
|
2863
|
+
|
|
2864
|
+
// src/ui/shape-media/useShapeMorph.ts
|
|
2561
2865
|
function useShapeMorph({
|
|
2562
2866
|
shape,
|
|
2563
2867
|
morphTo,
|
|
@@ -2572,10 +2876,16 @@ function useShapeMorph({
|
|
|
2572
2876
|
const duration = (_a = morphOptions == null ? void 0 : morphOptions.duration) != null ? _a : 0.3;
|
|
2573
2877
|
const easing = (_b = morphOptions == null ? void 0 : morphOptions.easing) != null ? _b : "ease-in-out";
|
|
2574
2878
|
const easingFunction = useMemo(() => getEasingFunction(easing), [easing]);
|
|
2879
|
+
const resolvedTarget = useMemo(() => {
|
|
2880
|
+
if (morphTo === "auto") {
|
|
2881
|
+
return getRecommendedMorphShape(shape);
|
|
2882
|
+
}
|
|
2883
|
+
return morphTo;
|
|
2884
|
+
}, [morphTo, shape]);
|
|
2575
2885
|
const startShape = useMemo(() => resolveShape(shape), [shape]);
|
|
2576
2886
|
const endShape = useMemo(
|
|
2577
|
-
() =>
|
|
2578
|
-
[
|
|
2887
|
+
() => resolvedTarget ? resolveShape(resolvedTarget) : null,
|
|
2888
|
+
[resolvedTarget]
|
|
2579
2889
|
);
|
|
2580
2890
|
const morph = useMemo(
|
|
2581
2891
|
() => endShape ? new Morph(startShape, endShape) : null,
|
|
@@ -2590,7 +2900,45 @@ function useShapeMorph({
|
|
|
2590
2900
|
const startProgressRef = useRef(0);
|
|
2591
2901
|
const targetProgressRef = useRef(0);
|
|
2592
2902
|
const currentProgressRef = useRef(0);
|
|
2903
|
+
const prevShapeRef = useRef(shape);
|
|
2904
|
+
useEffect(() => {
|
|
2905
|
+
if (prevShapeRef.current === shape) return;
|
|
2906
|
+
const oldShape = prevShapeRef.current;
|
|
2907
|
+
prevShapeRef.current = shape;
|
|
2908
|
+
if (disabled || prefersReducedMotion()) {
|
|
2909
|
+
setClipPath(toClipPath(resolveShape(shape), width, height));
|
|
2910
|
+
return;
|
|
2911
|
+
}
|
|
2912
|
+
try {
|
|
2913
|
+
const transitionMorph = new Morph(
|
|
2914
|
+
resolveShape(oldShape),
|
|
2915
|
+
resolveShape(shape)
|
|
2916
|
+
);
|
|
2917
|
+
const start = performance.now();
|
|
2918
|
+
const durationMs = duration * 1e3;
|
|
2919
|
+
const animateTransition = (timestamp) => {
|
|
2920
|
+
const elapsed = timestamp - start;
|
|
2921
|
+
const rawT = Math.min(elapsed / durationMs, 1);
|
|
2922
|
+
const ease = easingFunction(rawT);
|
|
2923
|
+
setClipPath(interpolatePath(transitionMorph, ease, width, height));
|
|
2924
|
+
if (rawT < 1) {
|
|
2925
|
+
animRef.current = requestAnimationFrame(animateTransition);
|
|
2926
|
+
} else {
|
|
2927
|
+
animRef.current = null;
|
|
2928
|
+
}
|
|
2929
|
+
};
|
|
2930
|
+
if (animRef.current !== null) cancelAnimationFrame(animRef.current);
|
|
2931
|
+
animRef.current = requestAnimationFrame(animateTransition);
|
|
2932
|
+
} catch (e) {
|
|
2933
|
+
setClipPath(toClipPath(resolveShape(shape), width, height));
|
|
2934
|
+
}
|
|
2935
|
+
}, [shape, disabled, duration, easingFunction, width, height]);
|
|
2936
|
+
const prevDimsRef = useRef({ width, height, startShape, morph });
|
|
2593
2937
|
useEffect(() => {
|
|
2938
|
+
if (prevDimsRef.current.width === width && prevDimsRef.current.height === height && prevDimsRef.current.startShape === startShape && prevDimsRef.current.morph === morph) {
|
|
2939
|
+
return;
|
|
2940
|
+
}
|
|
2941
|
+
prevDimsRef.current = { width, height, startShape, morph };
|
|
2594
2942
|
if (morph) {
|
|
2595
2943
|
setClipPath(
|
|
2596
2944
|
interpolatePath(morph, currentProgressRef.current, width, height)
|
|
@@ -2684,41 +3032,6 @@ function useShapeMorph({
|
|
|
2684
3032
|
}, [morphTo, disabled, morphOn, activate, deactivate, isActive]);
|
|
2685
3033
|
return { clipPath, isActive, activate, deactivate, setProgress, handlers };
|
|
2686
3034
|
}
|
|
2687
|
-
function easeCubicInOut(t) {
|
|
2688
|
-
return t < 0.5 ? 4 * t * t * t : 1 - (-2 * t + 2) ** 3 / 2;
|
|
2689
|
-
}
|
|
2690
|
-
function cubicBezier(x1, y1, x2, y2) {
|
|
2691
|
-
return (t) => {
|
|
2692
|
-
if (t === 0 || t === 1) return t;
|
|
2693
|
-
let tParam = t;
|
|
2694
|
-
for (let i = 0; i < 8; i++) {
|
|
2695
|
-
const x = 3 * (1 - tParam) ** 2 * tParam * x1 + 3 * (1 - tParam) * tParam ** 2 * x2 + tParam ** 3;
|
|
2696
|
-
const dx = 3 * (1 - tParam) ** 2 * x1 + 6 * (1 - tParam) * tParam * (x2 - x1) + 3 * tParam ** 2 * (1 - x2);
|
|
2697
|
-
if (Math.abs(dx) < 1e-6) break;
|
|
2698
|
-
tParam -= (x - t) / dx;
|
|
2699
|
-
}
|
|
2700
|
-
return 3 * (1 - tParam) ** 2 * tParam * y1 + 3 * (1 - tParam) * tParam ** 2 * y2 + tParam ** 3;
|
|
2701
|
-
};
|
|
2702
|
-
}
|
|
2703
|
-
function getEasingFunction(easing) {
|
|
2704
|
-
if (Array.isArray(easing)) {
|
|
2705
|
-
if (easing.length === 4) {
|
|
2706
|
-
const [x1, y1, x2, y2] = easing;
|
|
2707
|
-
return cubicBezier(x1, y1, x2, y2);
|
|
2708
|
-
}
|
|
2709
|
-
return easeCubicInOut;
|
|
2710
|
-
}
|
|
2711
|
-
switch (easing) {
|
|
2712
|
-
case "linear":
|
|
2713
|
-
return (t) => t;
|
|
2714
|
-
case "ease-in":
|
|
2715
|
-
return (t) => t * t * t;
|
|
2716
|
-
case "ease-out":
|
|
2717
|
-
return (t) => 1 - (1 - t) ** 3;
|
|
2718
|
-
default:
|
|
2719
|
-
return easeCubicInOut;
|
|
2720
|
-
}
|
|
2721
|
-
}
|
|
2722
3035
|
function ShapeMedia({
|
|
2723
3036
|
shape,
|
|
2724
3037
|
morphTo,
|
|
@@ -2846,10 +3159,14 @@ function ShapeSvg({
|
|
|
2846
3159
|
style,
|
|
2847
3160
|
"aria-label": ariaLabel
|
|
2848
3161
|
}) {
|
|
3162
|
+
const resolvedTarget = useMemo(() => {
|
|
3163
|
+
if (morphTo === "auto") return getRecommendedMorphShape(shape);
|
|
3164
|
+
return morphTo;
|
|
3165
|
+
}, [morphTo, shape]);
|
|
2849
3166
|
const startPolygon = useMemo(() => resolveShape(shape), [shape]);
|
|
2850
3167
|
const endPolygon = useMemo(
|
|
2851
|
-
() =>
|
|
2852
|
-
[
|
|
3168
|
+
() => resolvedTarget ? resolveShape(resolvedTarget) : null,
|
|
3169
|
+
[resolvedTarget]
|
|
2853
3170
|
);
|
|
2854
3171
|
const morph = useMemo(
|
|
2855
3172
|
() => endPolygon ? new Morph(startPolygon, endPolygon) : null,
|
|
@@ -2883,7 +3200,177 @@ function ShapeSvg({
|
|
|
2883
3200
|
}
|
|
2884
3201
|
);
|
|
2885
3202
|
}
|
|
3203
|
+
function useShapeSequenceMorph({
|
|
3204
|
+
shapes,
|
|
3205
|
+
duration = 0.6,
|
|
3206
|
+
interval = 2,
|
|
3207
|
+
autoplay = false,
|
|
3208
|
+
loop = true,
|
|
3209
|
+
morphOptions,
|
|
3210
|
+
width,
|
|
3211
|
+
height,
|
|
3212
|
+
disabled = false
|
|
3213
|
+
}) {
|
|
3214
|
+
var _a;
|
|
3215
|
+
const resolvedDuration = (_a = morphOptions == null ? void 0 : morphOptions.duration) != null ? _a : duration;
|
|
3216
|
+
const [currentIndex, setCurrentIndex] = useState(0);
|
|
3217
|
+
const [nextIndex, setNextIndex] = useState(() => shapes.length > 1 ? 1 : 0);
|
|
3218
|
+
const [progress, setProgress] = useState(0);
|
|
3219
|
+
const [isPlaying, setIsPlaying] = useState(autoplay);
|
|
3220
|
+
const animRef = useRef(null);
|
|
3221
|
+
const timerRef = useRef(null);
|
|
3222
|
+
const startTimeRef = useRef(0);
|
|
3223
|
+
const isTransitioningRef = useRef(false);
|
|
3224
|
+
const numShapes = shapes.length;
|
|
3225
|
+
const currentShape = useMemo(() => {
|
|
3226
|
+
if (numShapes === 0) return resolveShape("circle");
|
|
3227
|
+
const safeIdx = (currentIndex % numShapes + numShapes) % numShapes;
|
|
3228
|
+
return resolveShape(shapes[safeIdx]);
|
|
3229
|
+
}, [shapes, currentIndex, numShapes]);
|
|
3230
|
+
const targetShape = useMemo(() => {
|
|
3231
|
+
if (numShapes === 0) return resolveShape("circle");
|
|
3232
|
+
const safeIdx = (nextIndex % numShapes + numShapes) % numShapes;
|
|
3233
|
+
return resolveShape(shapes[safeIdx]);
|
|
3234
|
+
}, [shapes, nextIndex, numShapes]);
|
|
3235
|
+
const morph = useMemo(() => {
|
|
3236
|
+
if (currentShape === targetShape) return null;
|
|
3237
|
+
return new Morph(currentShape, targetShape);
|
|
3238
|
+
}, [currentShape, targetShape]);
|
|
3239
|
+
const [clipPath, setClipPath] = useState(
|
|
3240
|
+
() => toClipPath(currentShape, width, height)
|
|
3241
|
+
);
|
|
3242
|
+
const prevDimsRef = useRef({ width, height, currentShape, morph, progress });
|
|
3243
|
+
useEffect(() => {
|
|
3244
|
+
if (prevDimsRef.current.width === width && prevDimsRef.current.height === height && prevDimsRef.current.currentShape === currentShape && prevDimsRef.current.morph === morph && prevDimsRef.current.progress === progress) {
|
|
3245
|
+
return;
|
|
3246
|
+
}
|
|
3247
|
+
prevDimsRef.current = { width, height, currentShape, morph, progress };
|
|
3248
|
+
if (morph && progress > 0) {
|
|
3249
|
+
setClipPath(interpolatePath(morph, progress, width, height));
|
|
3250
|
+
} else {
|
|
3251
|
+
setClipPath(toClipPath(currentShape, width, height));
|
|
3252
|
+
}
|
|
3253
|
+
}, [width, height, currentShape, morph, progress]);
|
|
3254
|
+
const transitionTo = useCallback(
|
|
3255
|
+
(toIndex) => {
|
|
3256
|
+
if (disabled || numShapes <= 1 || isTransitioningRef.current) return;
|
|
3257
|
+
const targetSafe = (toIndex % numShapes + numShapes) % numShapes;
|
|
3258
|
+
if (targetSafe === currentIndex) return;
|
|
3259
|
+
setNextIndex(targetSafe);
|
|
3260
|
+
if (prefersReducedMotion()) {
|
|
3261
|
+
setCurrentIndex(targetSafe);
|
|
3262
|
+
setNextIndex((targetSafe + 1) % numShapes);
|
|
3263
|
+
setProgress(0);
|
|
3264
|
+
setClipPath(
|
|
3265
|
+
toClipPath(resolveShape(shapes[targetSafe]), width, height)
|
|
3266
|
+
);
|
|
3267
|
+
return;
|
|
3268
|
+
}
|
|
3269
|
+
isTransitioningRef.current = true;
|
|
3270
|
+
startTimeRef.current = performance.now();
|
|
3271
|
+
const fromPoly = resolveShape(shapes[currentIndex]);
|
|
3272
|
+
const toPoly = resolveShape(shapes[targetSafe]);
|
|
3273
|
+
const activeMorph = new Morph(fromPoly, toPoly);
|
|
3274
|
+
const animate = (timestamp) => {
|
|
3275
|
+
const elapsed = timestamp - startTimeRef.current;
|
|
3276
|
+
const durationMs = resolvedDuration * 1e3;
|
|
3277
|
+
const rawT = Math.min(elapsed / durationMs, 1);
|
|
3278
|
+
const ease = easeCubicInOut(rawT);
|
|
3279
|
+
setProgress(rawT);
|
|
3280
|
+
setClipPath(interpolatePath(activeMorph, ease, width, height));
|
|
3281
|
+
if (rawT < 1) {
|
|
3282
|
+
animRef.current = requestAnimationFrame(animate);
|
|
3283
|
+
} else {
|
|
3284
|
+
animRef.current = null;
|
|
3285
|
+
isTransitioningRef.current = false;
|
|
3286
|
+
setCurrentIndex(targetSafe);
|
|
3287
|
+
setNextIndex((targetSafe + 1) % numShapes);
|
|
3288
|
+
setProgress(0);
|
|
3289
|
+
}
|
|
3290
|
+
};
|
|
3291
|
+
if (animRef.current !== null) cancelAnimationFrame(animRef.current);
|
|
3292
|
+
animRef.current = requestAnimationFrame(animate);
|
|
3293
|
+
},
|
|
3294
|
+
[
|
|
3295
|
+
disabled,
|
|
3296
|
+
numShapes,
|
|
3297
|
+
currentIndex,
|
|
3298
|
+
shapes,
|
|
3299
|
+
resolvedDuration,
|
|
3300
|
+
width,
|
|
3301
|
+
height
|
|
3302
|
+
]
|
|
3303
|
+
);
|
|
3304
|
+
const next = useCallback(() => {
|
|
3305
|
+
if (numShapes <= 1) return;
|
|
3306
|
+
if (!loop && currentIndex >= numShapes - 1) return;
|
|
3307
|
+
transitionTo((currentIndex + 1) % numShapes);
|
|
3308
|
+
}, [numShapes, loop, currentIndex, transitionTo]);
|
|
3309
|
+
const prev = useCallback(() => {
|
|
3310
|
+
if (numShapes <= 1) return;
|
|
3311
|
+
if (!loop && currentIndex <= 0) return;
|
|
3312
|
+
transitionTo((currentIndex - 1 + numShapes) % numShapes);
|
|
3313
|
+
}, [numShapes, loop, currentIndex, transitionTo]);
|
|
3314
|
+
const goTo = useCallback(
|
|
3315
|
+
(index) => {
|
|
3316
|
+
transitionTo(index);
|
|
3317
|
+
},
|
|
3318
|
+
[transitionTo]
|
|
3319
|
+
);
|
|
3320
|
+
const play = useCallback(() => {
|
|
3321
|
+
setIsPlaying(true);
|
|
3322
|
+
}, []);
|
|
3323
|
+
const pause = useCallback(() => {
|
|
3324
|
+
setIsPlaying(false);
|
|
3325
|
+
if (timerRef.current !== null) {
|
|
3326
|
+
clearTimeout(timerRef.current);
|
|
3327
|
+
timerRef.current = null;
|
|
3328
|
+
}
|
|
3329
|
+
}, []);
|
|
3330
|
+
useEffect(() => {
|
|
3331
|
+
if (!isPlaying || disabled || numShapes <= 1) return;
|
|
3332
|
+
timerRef.current = setTimeout(() => {
|
|
3333
|
+
if (!loop && currentIndex >= numShapes - 1) {
|
|
3334
|
+
setIsPlaying(false);
|
|
3335
|
+
return;
|
|
3336
|
+
}
|
|
3337
|
+
next();
|
|
3338
|
+
}, interval * 1e3);
|
|
3339
|
+
return () => {
|
|
3340
|
+
if (timerRef.current !== null) {
|
|
3341
|
+
clearTimeout(timerRef.current);
|
|
3342
|
+
timerRef.current = null;
|
|
3343
|
+
}
|
|
3344
|
+
};
|
|
3345
|
+
}, [isPlaying, disabled, numShapes, loop, currentIndex, interval, next]);
|
|
3346
|
+
useEffect(() => {
|
|
3347
|
+
return () => {
|
|
3348
|
+
if (animRef.current !== null) cancelAnimationFrame(animRef.current);
|
|
3349
|
+
if (timerRef.current !== null) clearTimeout(timerRef.current);
|
|
3350
|
+
};
|
|
3351
|
+
}, []);
|
|
3352
|
+
useEffect(() => {
|
|
3353
|
+
if (currentIndex >= shapes.length) {
|
|
3354
|
+
setCurrentIndex(0);
|
|
3355
|
+
setNextIndex(shapes.length > 1 ? 1 : 0);
|
|
3356
|
+
}
|
|
3357
|
+
}, [shapes.length, currentIndex]);
|
|
3358
|
+
const safeCurrentIndex = numShapes > 0 ? (currentIndex % numShapes + numShapes) % numShapes : 0;
|
|
3359
|
+
const safeNextIndex = numShapes > 0 ? (nextIndex % numShapes + numShapes) % numShapes : 0;
|
|
3360
|
+
return {
|
|
3361
|
+
clipPath,
|
|
3362
|
+
currentIndex: safeCurrentIndex,
|
|
3363
|
+
nextIndex: safeNextIndex,
|
|
3364
|
+
progress,
|
|
3365
|
+
isPlaying,
|
|
3366
|
+
next,
|
|
3367
|
+
prev,
|
|
3368
|
+
goTo,
|
|
3369
|
+
play,
|
|
3370
|
+
pause
|
|
3371
|
+
};
|
|
3372
|
+
}
|
|
2886
3373
|
|
|
2887
|
-
export { ANGLE_EPSILON, Cubic, DISTANCE_EPSILON, FLOAT_PI, MD3CornerRadius, MD3Shapes, MeasuredPolygon, Morph, MutableCubic, RoundedPolygon, ShapeIcon, ShapeMedia, ShapeMediaServer, ShapeSvg, UNROUNDED, addPoints, circle, clockwise, convex, cornerFeature, cornerRounding, createDoubleMapper, directionVector, distance, distanceSquared, dividePoint, dotProduct, edgeFeature, featureMapper, getDirection, getDistance, getDistanceSquared, interpolate, interpolatePath, lerpPoint, pill, pillStar, point, positiveModulo, radialToCartesian, rectangle, rotate90, scalePoint, square, star, subtractPoints, toClipPath, toSvgPath, transformFeature, transformPoint, useShapeMorph };
|
|
3374
|
+
export { ANGLE_EPSILON, Cubic, DISTANCE_EPSILON, FLOAT_PI, MD3CornerRadius, MD3Shapes, MD3_SHAPE_FAMILIES, MD3_SHAPE_PAIRINGS, MeasuredPolygon, Morph, MutableCubic, RoundedPolygon, ShapeIcon, ShapeMedia, ShapeMediaServer, ShapeSvg, UNROUNDED, addPoints, circle, clockwise, convex, cornerFeature, cornerRounding, createDoubleMapper, directionVector, distance, distanceSquared, dividePoint, dotProduct, edgeFeature, featureMapper, formatCoordinate, getDirection, getDistance, getDistanceSquared, getRecommendedMorphShape, interpolate, interpolatePath, lerpPoint, pill, pillStar, point, positiveModulo, radialToCartesian, rectangle, rotate90, scalePoint, square, star, subtractPoints, toClipPath, toSvgPath, transformFeature, transformPoint, useShapeMorph, useShapeSequenceMorph };
|
|
2888
3375
|
//# sourceMappingURL=shapes.mjs.map
|
|
2889
3376
|
//# sourceMappingURL=shapes.mjs.map
|