react-native-molecules 0.5.0-beta.31 → 0.5.0-beta.32
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/components/Button/Button.tsx +3 -3
- package/components/DatePickerInline/AutoSizer.tsx +1 -1
- package/components/DatePickerInline/Swiper.tsx +12 -5
- package/components/DatePickerInline/dateUtils.tsx +1 -1
- package/components/LoadingIndicator/LoadingIndicator.tsx +80 -149
- package/components/LoadingIndicator/LoadingIndicator.web.tsx +58 -67
- package/components/LoadingIndicator/pathNormalize.ts +236 -0
- package/components/LoadingIndicator/utils.ts +0 -31
- package/package.json +1 -1
|
@@ -220,7 +220,7 @@ export const ButtonIcon = memo(
|
|
|
220
220
|
type={type}
|
|
221
221
|
name={name}
|
|
222
222
|
size={iconSizeResolved}
|
|
223
|
-
color={disabled ?
|
|
223
|
+
color={disabled ? 'onSurfaceDisabled' : colorResolved}
|
|
224
224
|
style={[buttonIconStyles.root, textRelatedStyle, style]}
|
|
225
225
|
{...rest}
|
|
226
226
|
/>
|
|
@@ -268,7 +268,7 @@ export const ButtonActivityIndicator = memo(
|
|
|
268
268
|
style,
|
|
269
269
|
...rest
|
|
270
270
|
}: Omit<ActivityIndicatorProps, 'animating'>) => {
|
|
271
|
-
const { iconSize, variant, state } = useContext(ButtonContext);
|
|
271
|
+
const { iconSize, variant, state, disabled } = useContext(ButtonContext);
|
|
272
272
|
|
|
273
273
|
const sizeResolved = sizeProp ?? iconSize;
|
|
274
274
|
// Default to onPrimary for contained variants, primary for others
|
|
@@ -287,7 +287,7 @@ export const ButtonActivityIndicator = memo(
|
|
|
287
287
|
return (
|
|
288
288
|
<ActivityIndicator
|
|
289
289
|
size={sizeResolved}
|
|
290
|
-
color={colorResolved}
|
|
290
|
+
color={disabled ? 'onSurfaceDisabled' : colorResolved}
|
|
291
291
|
style={activityIndicatorStyle}
|
|
292
292
|
{...rest}
|
|
293
293
|
/>
|
|
@@ -12,11 +12,18 @@ import {
|
|
|
12
12
|
} from 'react';
|
|
13
13
|
|
|
14
14
|
import AutoSizer from './AutoSizer';
|
|
15
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
beginOffset,
|
|
17
|
+
estimatedMonthHeight,
|
|
18
|
+
getGridCount,
|
|
19
|
+
getInitialIndex,
|
|
20
|
+
totalMonths,
|
|
21
|
+
} from './dateUtils';
|
|
16
22
|
import { addMonths, getRealIndex } from './dateUtils';
|
|
17
23
|
import { getIndexFromVerticalOffset, getMonthHeight, getVerticalMonthsOffset } from './Month';
|
|
18
24
|
import { useDatePickerInlineStore } from './store';
|
|
19
25
|
import type { SwiperProps } from './SwiperUtils';
|
|
26
|
+
import { weekSize } from './utils';
|
|
20
27
|
import { montHeaderHeight } from './utils';
|
|
21
28
|
|
|
22
29
|
function Swiper({ scrollMode, renderItem, renderHeader, renderFooter, initialIndex }: SwiperProps) {
|
|
@@ -59,7 +66,6 @@ const visibleHorizontalArray = (i: number) => [i - 1, i, i + 1];
|
|
|
59
66
|
|
|
60
67
|
function HorizontalScroller({
|
|
61
68
|
width,
|
|
62
|
-
height,
|
|
63
69
|
initialIndex,
|
|
64
70
|
renderItem,
|
|
65
71
|
}: {
|
|
@@ -152,9 +158,10 @@ function HorizontalScroller({
|
|
|
152
158
|
);
|
|
153
159
|
|
|
154
160
|
const { containerStyle, innerContainerStyle, itemContainerStyle } = useMemo(() => {
|
|
161
|
+
const currentHeight = getGridCount(visibleIndexes[1]) * weekSize;
|
|
155
162
|
return {
|
|
156
163
|
containerStyle: {
|
|
157
|
-
height,
|
|
164
|
+
height: currentHeight,
|
|
158
165
|
width,
|
|
159
166
|
overflowX: 'auto',
|
|
160
167
|
overflowY: 'hidden',
|
|
@@ -165,7 +172,7 @@ function HorizontalScroller({
|
|
|
165
172
|
},
|
|
166
173
|
innerContainerStyle: {
|
|
167
174
|
width: width * 3,
|
|
168
|
-
height,
|
|
175
|
+
height: currentHeight,
|
|
169
176
|
position: 'relative',
|
|
170
177
|
},
|
|
171
178
|
itemContainerStyle: (vi: number) => ({
|
|
@@ -177,7 +184,7 @@ function HorizontalScroller({
|
|
|
177
184
|
scrollSnapAlign: 'start',
|
|
178
185
|
}),
|
|
179
186
|
};
|
|
180
|
-
}, [
|
|
187
|
+
}, [visibleIndexes, width]);
|
|
181
188
|
|
|
182
189
|
return (
|
|
183
190
|
<div ref={parentRef} style={containerStyle as CSSProperties} onScroll={onScroll}>
|
|
@@ -133,7 +133,7 @@ export const gridCounts = new Array<number | undefined>(totalMonths);
|
|
|
133
133
|
export function getGridCount(index: number) {
|
|
134
134
|
const cHeight = gridCounts[index];
|
|
135
135
|
if (cHeight) {
|
|
136
|
-
return cHeight
|
|
136
|
+
return cHeight;
|
|
137
137
|
}
|
|
138
138
|
const monthDate = addMonths(new Date(), getRealIndex(index));
|
|
139
139
|
const h = getGridCountForDate(monthDate);
|
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { memo, useCallback, useEffect, useState } from 'react';
|
|
1
|
+
import { memo, useEffect } from 'react';
|
|
3
2
|
import { View } from 'react-native';
|
|
4
3
|
import Animated, {
|
|
5
4
|
cancelAnimation,
|
|
6
5
|
Easing,
|
|
6
|
+
useAnimatedProps,
|
|
7
7
|
useAnimatedStyle,
|
|
8
|
-
useDerivedValue,
|
|
9
|
-
useFrameCallback,
|
|
10
8
|
useSharedValue,
|
|
11
9
|
withRepeat,
|
|
12
10
|
withTiming,
|
|
@@ -14,100 +12,80 @@ import Animated, {
|
|
|
14
12
|
import Svg, { Path } from 'react-native-svg';
|
|
15
13
|
|
|
16
14
|
import {
|
|
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
|
-
const
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
//
|
|
49
|
-
|
|
15
|
+
normalizedCookie4Path,
|
|
16
|
+
normalizedCookie9Path,
|
|
17
|
+
normalizedOvalPath,
|
|
18
|
+
normalizedPentagonPath,
|
|
19
|
+
normalizedPillPath,
|
|
20
|
+
normalizedSoftBurstPath,
|
|
21
|
+
normalizedSunnyPath,
|
|
22
|
+
} from './pathNormalize';
|
|
23
|
+
import { loadingIndicatorStyles as componentStyles, type Props, useProcessProps } from './utils';
|
|
24
|
+
|
|
25
|
+
const AnimatedPath = Animated.createAnimatedComponent(Path);
|
|
26
|
+
|
|
27
|
+
// Pre-parse normalized paths to flat number arrays.
|
|
28
|
+
// All normalized paths share the same command structure: M(2 nums) + N×C(6 nums) + Z,
|
|
29
|
+
// so lerpPaths can reconstruct without a separate template.
|
|
30
|
+
const SHAPE_NUMS: number[][] = [
|
|
31
|
+
normalizedSoftBurstPath,
|
|
32
|
+
normalizedCookie9Path,
|
|
33
|
+
normalizedPentagonPath,
|
|
34
|
+
normalizedPillPath,
|
|
35
|
+
normalizedSunnyPath,
|
|
36
|
+
normalizedCookie4Path,
|
|
37
|
+
normalizedOvalPath,
|
|
38
|
+
normalizedSoftBurstPath, // wrap-back for the final segment's `to`
|
|
39
|
+
].map(d => (d.match(/-?(?:\d+\.?\d*|\.\d+)/g) ?? []).map(Number));
|
|
40
|
+
|
|
41
|
+
const ANIMATION_DURATION = 4550;
|
|
42
|
+
const PULSE_DURATION = 2275;
|
|
43
|
+
const SEGMENT_COUNT = 7;
|
|
44
|
+
const ROTATION_PER_SEGMENT = 1080 / SEGMENT_COUNT;
|
|
45
|
+
|
|
46
|
+
// Worklet: approximates cubic-bezier(0.42, 1.67, 0.21, 0.90) via Newton-Raphson.
|
|
47
|
+
// Output is clamped to [0,1] so overshoot stays visual-only.
|
|
48
|
+
function expressFastSpatialEase(t: number): number {
|
|
50
49
|
'worklet';
|
|
51
|
-
// Approximate cubic-bezier(0.42, 1.67, 0.21, 0.90) using a simplified function
|
|
52
|
-
// This creates the characteristic overshoot and fast settle
|
|
53
50
|
const p1x = 0.42,
|
|
54
51
|
p1y = 1.67,
|
|
55
52
|
p2x = 0.21,
|
|
56
53
|
p2y = 0.9;
|
|
57
|
-
|
|
58
|
-
// Simple cubic bezier approximation
|
|
59
54
|
const cx = 3.0 * p1x;
|
|
60
55
|
const bx = 3.0 * (p2x - p1x) - cx;
|
|
61
56
|
const ax = 1.0 - cx - bx;
|
|
62
|
-
|
|
63
57
|
const cy = 3.0 * p1y;
|
|
64
58
|
const by = 3.0 * (p2y - p1y) - cy;
|
|
65
59
|
const ay = 1.0 - cy - by;
|
|
66
|
-
|
|
67
|
-
// Sample the bezier curve
|
|
68
60
|
const sampleX = (x: number) => ((ax * x + bx) * x + cx) * x;
|
|
69
61
|
const sampleY = (x: number) => ((ay * x + by) * x + cy) * x;
|
|
70
|
-
|
|
71
|
-
// Newton-Raphson to find t for given x
|
|
72
62
|
let guess = t;
|
|
73
63
|
for (let i = 0; i < 4; i++) {
|
|
74
64
|
const currentX = sampleX(guess) - t;
|
|
75
|
-
const
|
|
76
|
-
if (Math.abs(
|
|
77
|
-
guess -= currentX /
|
|
65
|
+
const slope = (3.0 * ax * guess + 2.0 * bx) * guess + cx;
|
|
66
|
+
if (Math.abs(slope) < 1e-6) break;
|
|
67
|
+
guess -= currentX / slope;
|
|
78
68
|
}
|
|
69
|
+
return Math.max(0, Math.min(1, sampleY(guess)));
|
|
70
|
+
}
|
|
79
71
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
// Create interpolator with higher precision
|
|
95
|
-
const interpolator = flubberInterpolate(fromPath, toPath, {
|
|
96
|
-
maxSegmentLength: 1, // Higher precision for smoother morphing
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
for (let j = 0; j < FRAMES_PER_TRANSITION; j++) {
|
|
100
|
-
const t = j / FRAMES_PER_TRANSITION;
|
|
101
|
-
// Apply easing to match CSS animation
|
|
102
|
-
const easedT = expressFastSpatialEase(t);
|
|
103
|
-
frames.push(interpolator(Math.max(0, Math.min(1, easedT))));
|
|
104
|
-
}
|
|
72
|
+
// Worklet: lerp between two normalized path number arrays and serialize to a path string.
|
|
73
|
+
function lerpPaths(from: number[], to: number[], t: number): string {
|
|
74
|
+
'worklet';
|
|
75
|
+
const cmdCount = (from.length - 2) / 6;
|
|
76
|
+
let d = `M${from[0] + (to[0] - from[0]) * t} ${from[1] + (to[1] - from[1]) * t}`;
|
|
77
|
+
for (let i = 0; i < cmdCount; i++) {
|
|
78
|
+
const b = 2 + i * 6;
|
|
79
|
+
d +=
|
|
80
|
+
`C${from[b] + (to[b] - from[b]) * t}` +
|
|
81
|
+
` ${from[b + 1] + (to[b + 1] - from[b + 1]) * t}` +
|
|
82
|
+
` ${from[b + 2] + (to[b + 2] - from[b + 2]) * t}` +
|
|
83
|
+
` ${from[b + 3] + (to[b + 3] - from[b + 3]) * t}` +
|
|
84
|
+
` ${from[b + 4] + (to[b + 4] - from[b + 4]) * t}` +
|
|
85
|
+
` ${from[b + 5] + (to[b + 5] - from[b + 5]) * t}`;
|
|
105
86
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
const frames = precomputeFrames();
|
|
87
|
+
return `${d}Z`;
|
|
88
|
+
}
|
|
111
89
|
|
|
112
90
|
const LoadingIndicator = ({
|
|
113
91
|
animating = true,
|
|
@@ -118,102 +96,55 @@ const LoadingIndicator = ({
|
|
|
118
96
|
innerContainerProps,
|
|
119
97
|
...rest
|
|
120
98
|
}: Props) => {
|
|
121
|
-
const [currentPath, setCurrentPath] = useState(frames[0]);
|
|
122
|
-
|
|
123
99
|
const progress = useSharedValue(0);
|
|
124
100
|
const pulseScale = useSharedValue(1);
|
|
125
|
-
// Track last frame to avoid redundant updates
|
|
126
|
-
const lastFrameRef = useSharedValue(-1);
|
|
127
101
|
|
|
128
|
-
componentStyles.useVariants({
|
|
129
|
-
variant: variant as 'contained',
|
|
130
|
-
});
|
|
102
|
+
componentStyles.useVariants({ variant: variant as 'contained' });
|
|
131
103
|
|
|
132
|
-
const { size, strokeColor } = useProcessProps({
|
|
133
|
-
variant,
|
|
134
|
-
size: sizeProp,
|
|
135
|
-
color: colorProp,
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
const updatePathFromFrame = useCallback((frameIndex: number) => {
|
|
139
|
-
const safeIndex = Math.max(0, Math.min(frameIndex, frames.length - 1));
|
|
140
|
-
setCurrentPath(prevPath => {
|
|
141
|
-
const newPath = frames[safeIndex];
|
|
142
|
-
return prevPath === newPath ? prevPath : newPath;
|
|
143
|
-
});
|
|
144
|
-
}, []);
|
|
145
|
-
|
|
146
|
-
useFrameCallback(() => {
|
|
147
|
-
'worklet';
|
|
148
|
-
const frameIndex = Math.floor(progress.value * TOTAL_FRAMES) % TOTAL_FRAMES;
|
|
149
|
-
if (frameIndex !== lastFrameRef.value) {
|
|
150
|
-
lastFrameRef.value = frameIndex;
|
|
151
|
-
updatePathFromFrame(frameIndex);
|
|
152
|
-
}
|
|
153
|
-
});
|
|
104
|
+
const { size, strokeColor } = useProcessProps({ variant, size: sizeProp, color: colorProp });
|
|
154
105
|
|
|
155
106
|
useEffect(() => {
|
|
156
107
|
if (animating) {
|
|
157
|
-
// Main morphing and rotation animation
|
|
158
108
|
progress.value = 0;
|
|
159
109
|
progress.value = withRepeat(
|
|
160
|
-
withTiming(1, {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
}),
|
|
164
|
-
-1, // Infinite repeat
|
|
165
|
-
false, // Don't reverse
|
|
110
|
+
withTiming(1, { duration: ANIMATION_DURATION, easing: Easing.linear }),
|
|
111
|
+
-1,
|
|
112
|
+
false,
|
|
166
113
|
);
|
|
167
|
-
|
|
168
|
-
// Pulse animation
|
|
169
114
|
pulseScale.value = 1;
|
|
170
115
|
pulseScale.value = withRepeat(
|
|
171
|
-
withTiming(1.1, {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
}),
|
|
175
|
-
-1, // Infinite repeat
|
|
176
|
-
true, // Reverse (ping-pong effect)
|
|
116
|
+
withTiming(1.1, { duration: PULSE_DURATION, easing: Easing.inOut(Easing.ease) }),
|
|
117
|
+
-1,
|
|
118
|
+
true,
|
|
177
119
|
);
|
|
178
120
|
} else {
|
|
179
121
|
cancelAnimation(progress);
|
|
180
122
|
cancelAnimation(pulseScale);
|
|
181
123
|
progress.value = 0;
|
|
182
124
|
pulseScale.value = 1;
|
|
183
|
-
updatePathFromFrame(0);
|
|
184
125
|
}
|
|
185
|
-
|
|
186
126
|
return () => {
|
|
187
127
|
cancelAnimation(progress);
|
|
188
128
|
cancelAnimation(pulseScale);
|
|
189
129
|
};
|
|
190
|
-
}, [animating, progress, pulseScale
|
|
130
|
+
}, [animating, progress, pulseScale]);
|
|
191
131
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
const
|
|
196
|
-
const
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
const segmentIndex = Math.min(Math.floor(scaledProgress), segmentCount - 1);
|
|
201
|
-
const segmentProgress = scaledProgress - segmentIndex;
|
|
202
|
-
|
|
203
|
-
// Apply easing to this segment's progress
|
|
204
|
-
const easedSegmentProgress = expressFastSpatialEase(segmentProgress);
|
|
205
|
-
|
|
206
|
-
// Calculate rotation: base rotation for completed segments + eased progress within current segment
|
|
207
|
-
const rotationPerSegment = 1080 / segmentCount; // ~154.29 degrees per segment
|
|
208
|
-
const baseRotation = segmentIndex * rotationPerSegment;
|
|
209
|
-
const segmentRotation = easedSegmentProgress * rotationPerSegment;
|
|
210
|
-
|
|
211
|
-
return baseRotation + segmentRotation;
|
|
132
|
+
const animatedStyle = useAnimatedStyle(() => {
|
|
133
|
+
const scaledP = progress.value * SEGMENT_COUNT;
|
|
134
|
+
const segIdx = Math.min(Math.floor(scaledP), SEGMENT_COUNT - 1);
|
|
135
|
+
const easedT = expressFastSpatialEase(scaledP - segIdx);
|
|
136
|
+
const rotation = segIdx * ROTATION_PER_SEGMENT + easedT * ROTATION_PER_SEGMENT;
|
|
137
|
+
return {
|
|
138
|
+
transform: [{ scale: pulseScale.value }, { rotate: `${rotation}deg` }],
|
|
139
|
+
};
|
|
212
140
|
});
|
|
213
141
|
|
|
214
|
-
const
|
|
142
|
+
const animatedProps = useAnimatedProps(() => {
|
|
143
|
+
const scaledP = progress.value * SEGMENT_COUNT;
|
|
144
|
+
const segIdx = Math.min(Math.floor(scaledP), SEGMENT_COUNT - 1);
|
|
145
|
+
const easedT = expressFastSpatialEase(scaledP - segIdx);
|
|
215
146
|
return {
|
|
216
|
-
|
|
147
|
+
d: lerpPaths(SHAPE_NUMS[segIdx], SHAPE_NUMS[segIdx + 1], easedT),
|
|
217
148
|
};
|
|
218
149
|
});
|
|
219
150
|
|
|
@@ -243,7 +174,7 @@ const LoadingIndicator = ({
|
|
|
243
174
|
animatedStyle,
|
|
244
175
|
]}>
|
|
245
176
|
<Svg width={size} height={size} viewBox="0 0 38 38">
|
|
246
|
-
<
|
|
177
|
+
<AnimatedPath animatedProps={animatedProps} fill={strokeColor} />
|
|
247
178
|
</Svg>
|
|
248
179
|
</Animated.View>
|
|
249
180
|
</View>
|
|
@@ -2,22 +2,35 @@ import { memo, useId } from 'react';
|
|
|
2
2
|
import { View } from 'react-native';
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
5
|
+
normalizedCookie4Path,
|
|
6
|
+
normalizedCookie9Path,
|
|
7
|
+
normalizedOvalPath,
|
|
8
|
+
normalizedPentagonPath,
|
|
9
|
+
normalizedPillPath,
|
|
10
|
+
normalizedSoftBurstPath,
|
|
11
|
+
normalizedSunnyPath,
|
|
12
|
+
} from './pathNormalize';
|
|
13
|
+
import { loadingIndicatorStyles as componentStyles, type Props, useProcessProps } from './utils';
|
|
14
|
+
|
|
15
|
+
// 8 evenly-spaced keyframe beats matching the original CSS animation.
|
|
16
|
+
const SMIL_KEY_TIMES = '0; 0.1428; 0.2857; 0.4285; 0.5714; 0.7142; 0.8571; 1';
|
|
17
|
+
// Approximation of cubic-bezier(0.42, 1.67, 0.21, 0.90) clamped to [0,1]
|
|
18
|
+
// because SMIL keySplines does not allow y-values outside that range.
|
|
19
|
+
const SMIL_KEY_SPLINES = Array(7).fill('0.42 1 0.21 0.90').join('; ');
|
|
20
|
+
|
|
21
|
+
const SHAPE_ROTATION_VALUES =
|
|
22
|
+
'0 19 19; 154.29 19 19; 308.57 19 19; 462.86 19 19; 617.14 19 19; 771.43 19 19; 925.71 19 19; 1080 19 19';
|
|
17
23
|
|
|
18
24
|
/**
|
|
19
25
|
* Material 3 Expressive Loading Indicator for Web.
|
|
20
|
-
*
|
|
26
|
+
*
|
|
27
|
+
* Uses pure SMIL animations so both shape morphing and rotation work in Safari:
|
|
28
|
+
* - <animate attributeName="d"> for shape morphing
|
|
29
|
+
* - <animateTransform type="rotate"> with explicit center (19 19) for rotation
|
|
30
|
+
*
|
|
31
|
+
* CSS `d` property animation in @keyframes is not supported in Safari, and mixing
|
|
32
|
+
* CSS transform with SMIL on the same element causes the rotation pivot to shift
|
|
33
|
+
* as fill-box dimensions change during morphing.
|
|
21
34
|
*/
|
|
22
35
|
const LoadingIndicator = ({
|
|
23
36
|
animating = true,
|
|
@@ -40,6 +53,17 @@ const LoadingIndicator = ({
|
|
|
40
53
|
|
|
41
54
|
if (!animating) return null;
|
|
42
55
|
|
|
56
|
+
const shapeValues = [
|
|
57
|
+
normalizedSoftBurstPath,
|
|
58
|
+
normalizedCookie9Path,
|
|
59
|
+
normalizedPentagonPath,
|
|
60
|
+
normalizedPillPath,
|
|
61
|
+
normalizedSunnyPath,
|
|
62
|
+
normalizedCookie4Path,
|
|
63
|
+
normalizedOvalPath,
|
|
64
|
+
normalizedSoftBurstPath,
|
|
65
|
+
].join('; ');
|
|
66
|
+
|
|
43
67
|
return (
|
|
44
68
|
<View
|
|
45
69
|
style={[
|
|
@@ -66,67 +90,34 @@ const LoadingIndicator = ({
|
|
|
66
90
|
width={size}
|
|
67
91
|
height={size}
|
|
68
92
|
viewBox="0 0 38 38">
|
|
69
|
-
<path
|
|
93
|
+
<path d={normalizedSoftBurstPath} fill={strokeColor}>
|
|
94
|
+
<animate
|
|
95
|
+
attributeName="d"
|
|
96
|
+
dur="4.55s"
|
|
97
|
+
repeatCount="indefinite"
|
|
98
|
+
calcMode="spline"
|
|
99
|
+
keyTimes={SMIL_KEY_TIMES}
|
|
100
|
+
keySplines={SMIL_KEY_SPLINES}
|
|
101
|
+
values={shapeValues}
|
|
102
|
+
/>
|
|
103
|
+
<animateTransform
|
|
104
|
+
attributeName="transform"
|
|
105
|
+
type="rotate"
|
|
106
|
+
dur="4.55s"
|
|
107
|
+
repeatCount="indefinite"
|
|
108
|
+
calcMode="spline"
|
|
109
|
+
keyTimes={SMIL_KEY_TIMES}
|
|
110
|
+
keySplines={SMIL_KEY_SPLINES}
|
|
111
|
+
values={SHAPE_ROTATION_VALUES}
|
|
112
|
+
/>
|
|
113
|
+
</path>
|
|
70
114
|
</svg>
|
|
71
115
|
</View>
|
|
72
116
|
<style>
|
|
73
117
|
{`
|
|
74
118
|
.m3-expressive-svg-${id} {
|
|
75
|
-
transform-origin: center;
|
|
76
119
|
display: block;
|
|
77
120
|
}
|
|
78
|
-
|
|
79
|
-
.m3-expressive-path-${id} {
|
|
80
|
-
animation: m3-expressive-combined 4.55s linear infinite;
|
|
81
|
-
transform-origin: center;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
@keyframes m3-expressive-pulse-${id} {
|
|
85
|
-
0%, 100% { transform: scale(1); }
|
|
86
|
-
50% { transform: scale(1.1); }
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
@keyframes m3-expressive-combined {
|
|
90
|
-
0% {
|
|
91
|
-
animation-timing-function: ${expressFastSpatial};
|
|
92
|
-
transform: rotate(0deg);
|
|
93
|
-
d: path('${softBurstPath}');
|
|
94
|
-
}
|
|
95
|
-
14.28% {
|
|
96
|
-
animation-timing-function: ${expressFastSpatial};
|
|
97
|
-
transform: rotate(154.29deg);
|
|
98
|
-
d: path('${cookie9Path}');
|
|
99
|
-
}
|
|
100
|
-
28.57% {
|
|
101
|
-
animation-timing-function: ${expressFastSpatial};
|
|
102
|
-
transform: rotate(308.57deg);
|
|
103
|
-
d: path('${pentagonPath}');
|
|
104
|
-
}
|
|
105
|
-
42.85% {
|
|
106
|
-
animation-timing-function: ${expressFastSpatial};
|
|
107
|
-
transform: rotate(462.86deg);
|
|
108
|
-
d: path('${pillPath}');
|
|
109
|
-
}
|
|
110
|
-
57.14% {
|
|
111
|
-
animation-timing-function: ${expressFastSpatial};
|
|
112
|
-
transform: rotate(617.14deg);
|
|
113
|
-
d: path('${sunnyPath}');
|
|
114
|
-
}
|
|
115
|
-
71.42% {
|
|
116
|
-
animation-timing-function: ${expressFastSpatial};
|
|
117
|
-
transform: rotate(771.43deg);
|
|
118
|
-
d: path('${cookie4Path}');
|
|
119
|
-
}
|
|
120
|
-
85.71% {
|
|
121
|
-
animation-timing-function: ${expressFastSpatial};
|
|
122
|
-
transform: rotate(925.71deg);
|
|
123
|
-
d: path('${ovalPath}');
|
|
124
|
-
}
|
|
125
|
-
100% {
|
|
126
|
-
transform: rotate(1080deg);
|
|
127
|
-
d: path('${softBurstPath}');
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
121
|
`}
|
|
131
122
|
</style>
|
|
132
123
|
</View>
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Raw M3 Expressive shape paths (viewBox 0 0 38 38)
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
|
|
5
|
+
export const softBurstPath =
|
|
6
|
+
'M17.4683 2.83978C18.1732 1.72008 19.8268 1.72007 20.5317 2.83977L22.2214 5.52373C22.6848 6.25987 23.6197 6.55915 24.4338 6.23198L27.4021 5.0391C28.6404 4.54145 29.9781 5.49904 29.8804 6.8131L29.6461 9.96297C29.5819 10.8269 30.1597 11.6104 31.0135 11.8172L34.1265 12.571C35.4252 12.8855 35.9362 14.4349 35.0731 15.4414L33.0044 17.854C32.437 18.5158 32.437 19.4842 33.0044 20.146L35.0731 22.5586C35.9362 23.5651 35.4252 25.1145 34.1265 25.429L31.0135 26.1828C30.1597 26.3896 29.5819 27.1731 29.6461 28.037L29.8804 31.1869C29.9781 32.501 28.6404 33.4585 27.4021 32.9609L24.4338 31.768C23.6197 31.4408 22.6848 31.7401 22.2214 32.4763L20.5317 35.1602C19.8268 36.2799 18.1732 36.2799 17.4683 35.1602L15.7786 32.4763C15.3152 31.7401 14.3803 31.4408 13.5662 31.768L10.5979 32.9609C9.35964 33.4585 8.02187 32.501 8.1196 31.1869L8.35388 28.037C8.41814 27.1731 7.84033 26.3896 6.98651 26.1828L3.87348 25.429C2.57479 25.1145 2.0638 23.5651 2.92684 22.5586L4.99559 20.146C5.56299 19.4842 5.56299 18.5158 4.99559 17.854L2.92685 15.4414C2.0638 14.4349 2.57478 12.8855 3.87348 12.571L6.98651 11.8172C7.84033 11.6104 8.41814 10.8269 8.35388 9.96297L8.1196 6.81311C8.02187 5.49904 9.35964 4.54145 10.5979 5.0391L13.5662 6.23198C14.3803 6.55915 15.3152 6.25987 15.7786 5.52373L17.4683 2.83978Z';
|
|
7
|
+
|
|
8
|
+
export const cookie9Path =
|
|
9
|
+
'M15.3091 3.60363C15.4924 3.454 15.584 3.37919 15.6677 3.31603C17.6389 1.82799 20.3611 1.82799 22.3323 3.31603C22.416 3.37919 22.5076 3.454 22.6909 3.60363C22.7727 3.67042 22.8136 3.70381 22.8541 3.7356C23.7818 4.46445 24.9191 4.87748 26.0993 4.91409C26.1508 4.91569 26.2037 4.91634 26.3094 4.91765C26.5462 4.92059 26.6646 4.92206 26.7694 4.92733C29.2381 5.05162 31.3234 6.79743 31.8748 9.20154C31.8982 9.30358 31.9202 9.41966 31.9642 9.65183C31.9838 9.75547 31.9937 9.80729 32.0042 9.85759C32.2452 11.0109 32.8504 12.0567 33.7309 12.8416C33.7693 12.8759 33.8094 12.9103 33.8895 12.9791C34.069 13.1332 34.1588 13.2102 34.2357 13.2815C36.0467 14.96 36.5194 17.6347 35.393 19.8299C35.3451 19.9231 35.2872 20.0262 35.1714 20.2322C35.1196 20.3242 35.0938 20.3702 35.0694 20.4155C34.5111 21.4536 34.3009 22.6429 34.4697 23.8088C34.4771 23.8597 34.4856 23.9117 34.5027 24.0158C34.5409 24.249 34.56 24.3656 34.573 24.4695C34.879 26.9168 33.5179 29.2689 31.2407 30.2281C31.1441 30.2688 31.0333 30.3106 30.8118 30.3942C30.7129 30.4315 30.6635 30.4501 30.6156 30.4692C29.5192 30.9063 28.592 31.6826 27.9701 32.684C27.943 32.7277 27.916 32.7731 27.862 32.8637C27.741 33.0669 27.6806 33.1685 27.6236 33.2564C26.2814 35.3273 23.7234 36.2563 21.3609 35.5306C21.2606 35.4998 21.1489 35.4608 20.9253 35.3827C20.8256 35.3479 20.7757 35.3305 20.7268 35.3144C19.6052 34.9461 18.3948 34.9461 17.2732 35.3144C17.2243 35.3305 17.1744 35.3479 17.0747 35.3827C16.8511 35.4608 16.7394 35.4998 16.6391 35.5306C14.2767 36.2563 11.7186 35.3273 10.3764 33.2564C10.3194 33.1685 10.259 33.0669 10.138 32.8637C10.084 32.7731 10.057 32.7277 10.0299 32.684C9.40803 31.6826 8.48083 30.9063 7.38436 30.4692C7.33654 30.4501 7.28709 30.4315 7.18821 30.3942C6.96669 30.3106 6.85593 30.2688 6.75928 30.2281C4.48205 29.2689 3.12097 26.9168 3.42698 24.4695C3.43997 24.3656 3.45908 24.249 3.4973 24.0158C3.51436 23.9117 3.52289 23.8597 3.53026 23.8088C3.69906 22.6429 3.48889 21.4536 2.93056 20.4155C2.90621 20.3702 2.88035 20.3242 2.82863 20.2322C2.71278 20.0262 2.65485 19.9231 2.60704 19.8299C1.48057 17.6347 1.95327 14.96 3.76433 13.2815C3.8412 13.2102 3.93096 13.1332 4.11047 12.9791C4.19061 12.9103 4.23068 12.8759 4.26908 12.8416C5.14958 12.0567 5.75476 11.0109 5.99583 9.8576C6.00634 9.80729 6.01617 9.75547 6.03582 9.65183C6.07984 9.41966 6.10185 9.30358 6.12525 9.20154C6.67662 6.79743 8.76192 5.05162 11.2306 4.92733C11.3354 4.92206 11.4538 4.92059 11.6906 4.91765C11.7963 4.91634 11.8492 4.91569 11.9007 4.91409C13.0809 4.87748 14.2182 4.46445 15.1459 3.7356C15.1864 3.70381 15.2273 3.67042 15.3091 3.60363Z';
|
|
10
|
+
|
|
11
|
+
export const pentagonPath =
|
|
12
|
+
'M13.6481 4.93735C15.3067 3.72903 16.136 3.12486 17.022 2.82514C18.3035 2.39162 19.6965 2.39162 20.978 2.82514C21.864 3.12486 22.6933 3.72903 24.3519 4.93735L27.9794 7.58012L31.6049 10.0564C33.3249 11.2312 34.1849 11.8185 34.7598 12.5573C35.5914 13.6258 36.0282 14.9391 35.9986 16.2824C35.9781 17.211 35.6363 18.1817 34.9526 20.1232L33.529 24.1657L32.215 28.308C31.5983 30.2519 31.29 31.2239 30.7509 31.98C29.9712 33.0736 28.838 33.878 27.5341 34.2634C26.6327 34.5299 25.5936 34.5144 23.5154 34.4835L19 34.4162L14.4846 34.4835C12.4064 34.5144 11.3673 34.5299 10.4659 34.2634C9.16204 33.878 8.02882 33.0736 7.24909 31.98C6.71002 31.2239 6.40169 30.2519 5.78503 28.308L4.47101 24.1657L3.04741 20.1232C2.36372 18.1817 2.02187 17.211 2.00141 16.2824C1.97182 14.9391 2.40855 13.6258 3.24017 12.5573C3.8151 11.8185 4.67508 11.2312 6.39505 10.0564L10.0206 7.58012L13.6481 4.93735Z';
|
|
13
|
+
|
|
14
|
+
export const pillPath =
|
|
15
|
+
'M11.9857 7.77706C17.0217 2.74098 25.1869 2.74098 30.2229 7.77706C35.259 12.8131 35.259 20.9783 30.2229 26.0143L26.0143 30.2229C20.9783 35.259 12.8131 35.259 7.77706 30.2229C2.74098 25.1869 2.74098 17.0217 7.77706 11.9857L11.9857 7.77706Z';
|
|
16
|
+
|
|
17
|
+
export const sunnyPath =
|
|
18
|
+
'M28.1856 6.12375C29.2431 6.19561 29.7718 6.23154 30.1991 6.41844C30.8175 6.68887 31.3111 7.18252 31.5816 7.80088C31.7685 8.22823 31.8044 8.75694 31.8762 9.81438L32.0402 12.2275C32.0693 12.6552 32.0838 12.869 32.1303 13.0733C32.1975 13.3684 32.3142 13.6501 32.4754 13.9063C32.5869 14.0836 32.7279 14.2451 33.0097 14.5681L34.6001 16.3903C35.297 17.1889 35.6455 17.5881 35.8155 18.0225C36.0615 18.6509 36.0615 19.3491 35.8155 19.9775C35.6455 20.4119 35.297 20.8111 34.6001 21.6097L33.0097 23.4319C32.7279 23.7549 32.5869 23.9164 32.4754 24.0937C32.3142 24.3499 32.1975 24.6316 32.1303 24.9267C32.0838 25.131 32.0693 25.3448 32.0402 25.7725L31.8762 28.1856C31.8044 29.2431 31.7685 29.7718 31.5816 30.1991C31.3111 30.8175 30.8175 31.3111 30.1991 31.5816C29.7718 31.7685 29.2431 31.8044 28.1856 31.8762L25.7725 32.0402C25.3448 32.0693 25.131 32.0838 24.9267 32.1303C24.6316 32.1975 24.3499 32.3142 24.0937 32.4754C23.9164 32.5869 23.7549 32.7279 23.4319 33.0097L21.6097 34.6001C20.8111 35.297 20.4119 35.6455 19.9775 35.8155C19.3491 36.0615 18.6509 36.0615 18.0225 35.8155C17.5881 35.6455 17.1889 35.297 16.3903 34.6001L14.5681 33.0097C14.2451 32.7279 14.0836 32.5869 13.9063 32.4754C13.6501 32.3142 13.3684 32.1975 13.0733 32.1303C12.869 32.0838 12.6552 32.0693 12.2275 32.0402L9.81438 31.8762C8.75694 31.8044 8.22822 31.7685 7.80088 31.5816C7.18252 31.3111 6.68887 30.8175 6.41844 30.1991C6.23154 29.7718 6.19561 29.2431 6.12375 28.1856L5.95977 25.7725C5.93071 25.3448 5.91618 25.131 5.86969 24.9267C5.80251 24.6316 5.68584 24.3499 5.52463 24.0937C5.41306 23.9164 5.27213 23.7549 4.99027 23.4319L3.3999 21.6097C2.703 20.8111 2.35454 20.4119 2.18452 19.9775C1.93849 19.3491 1.93849 18.6509 2.18452 18.0225C2.35454 17.5881 2.703 17.1889 3.3999 16.3903L4.99027 14.5681C5.27213 14.2451 5.41306 14.0836 5.52463 13.9063C5.68584 13.6501 5.80251 13.3684 5.86969 13.0733C5.91618 12.869 5.93071 12.6552 5.95977 12.2275L6.12375 9.81438C6.19561 8.75694 6.23154 8.22823 6.41844 7.80088C6.68887 7.18252 7.18252 6.68887 7.80088 6.41844C8.22823 6.23154 8.75694 6.19561 9.81438 6.12375L12.2275 5.95977C12.6552 5.93071 12.869 5.91618 13.0733 5.86969C13.3684 5.80251 13.6501 5.68584 13.9063 5.52463C14.0836 5.41306 14.2451 5.27213 14.5681 4.99027L16.3903 3.3999C17.1889 2.703 17.5881 2.35454 18.0225 2.18452C18.6509 1.93849 19.3491 1.93849 19.9775 2.18452C20.4119 2.35454 20.8111 2.703 21.6097 3.3999L23.4319 4.99027C23.7549 5.27213 23.9164 5.41306 24.0937 5.52463C24.3499 5.68584 24.6316 5.80251 24.9267 5.86969C25.131 5.91618 25.3448 5.93071 25.7725 5.95977L28.1856 6.12375Z';
|
|
19
|
+
|
|
20
|
+
export const cookie4Path =
|
|
21
|
+
'M22.8729 5.6207C28.8872 3.00862 34.9914 9.11282 32.3793 15.1271L31.9474 16.1214C31.1499 17.9576 31.1499 20.0424 31.9474 21.8786L32.3793 22.8729C34.9914 28.8872 28.8872 34.9914 22.8729 32.3793L21.8786 31.9474C20.0424 31.1499 17.9576 31.1499 16.1214 31.9474L15.1271 32.3793C9.11281 34.9914 3.00862 28.8872 5.6207 22.8729L6.05257 21.8786C6.85006 20.0424 6.85005 17.9576 6.05257 16.1214L5.6207 15.1271C3.00862 9.11281 9.11282 3.00862 15.1271 5.6207L16.1214 6.05257C17.9576 6.85005 20.0424 6.85005 21.8786 6.05257L22.8729 5.6207Z';
|
|
22
|
+
|
|
23
|
+
export const ovalPath =
|
|
24
|
+
'M27.1309 27.1309C20.1705 34.0913 10.8877 36.0935 6.39707 31.6029C1.90648 27.1123 3.90867 17.8295 10.8691 10.8691C17.8295 3.90867 27.1123 1.90648 31.6029 6.39707C36.0935 10.8877 34.0913 20.1705 27.1309 27.1309Z';
|
|
25
|
+
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
// Normalization pipeline for cross-browser SMIL animation
|
|
28
|
+
//
|
|
29
|
+
// SMIL <animate attributeName="d"> requires all paths to have an identical
|
|
30
|
+
// sequence of commands. The pipeline:
|
|
31
|
+
// 1. Convert L commands to degenerate cubics (same visual shape, only C cmds).
|
|
32
|
+
// 2. Split shorter paths up to the maximum command count via de Casteljau —
|
|
33
|
+
// preserves real geometry, avoids zero-length padding artefacts.
|
|
34
|
+
// 3. Rotate each path's command sequence so it starts at the same angular
|
|
35
|
+
// position (-π/2, top of shape). Prevents SMIL from pairing spatially
|
|
36
|
+
// opposite commands, which collapses the morph through the centre.
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
|
|
39
|
+
interface Cmd {
|
|
40
|
+
t: string;
|
|
41
|
+
a: number[];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function tokenize(d: string): Cmd[] {
|
|
45
|
+
const out: Cmd[] = [];
|
|
46
|
+
const re = /([MLCZz])([^MLCZz]*)/g;
|
|
47
|
+
let m: RegExpExecArray | null;
|
|
48
|
+
while ((m = re.exec(d)) !== null) {
|
|
49
|
+
const t = m[1].toUpperCase();
|
|
50
|
+
const a = m[2].trim()
|
|
51
|
+
? m[2]
|
|
52
|
+
.trim()
|
|
53
|
+
.split(/[\s,]+/)
|
|
54
|
+
.map(Number)
|
|
55
|
+
: [];
|
|
56
|
+
out.push({ t, a });
|
|
57
|
+
}
|
|
58
|
+
return out;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function lToC(cmds: Cmd[]): Cmd[] {
|
|
62
|
+
const out: Cmd[] = [];
|
|
63
|
+
let cx = 0,
|
|
64
|
+
cy = 0;
|
|
65
|
+
for (const { t, a } of cmds) {
|
|
66
|
+
if (t === 'M') {
|
|
67
|
+
[cx, cy] = a;
|
|
68
|
+
out.push({ t, a });
|
|
69
|
+
} else if (t === 'C') {
|
|
70
|
+
out.push({ t, a });
|
|
71
|
+
cx = a[4];
|
|
72
|
+
cy = a[5];
|
|
73
|
+
} else if (t === 'L') {
|
|
74
|
+
out.push({ t: 'C', a: [cx, cy, a[0], a[1], a[0], a[1]] });
|
|
75
|
+
cx = a[0];
|
|
76
|
+
cy = a[1];
|
|
77
|
+
} else {
|
|
78
|
+
out.push({ t, a });
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return out;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function round4(n: number): number {
|
|
85
|
+
return Math.round(n * 10000) / 10000;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function splitCubic(p0x: number, p0y: number, a: number[]): [number[], number[]] {
|
|
89
|
+
const [x1, y1, x2, y2, x3, y3] = a;
|
|
90
|
+
const avg = (p: number, q: number) => (p + q) / 2;
|
|
91
|
+
const p01x = avg(p0x, x1),
|
|
92
|
+
p01y = avg(p0y, y1);
|
|
93
|
+
const p12x = avg(x1, x2),
|
|
94
|
+
p12y = avg(y1, y2);
|
|
95
|
+
const p23x = avg(x2, x3),
|
|
96
|
+
p23y = avg(y2, y3);
|
|
97
|
+
const p012x = avg(p01x, p12x),
|
|
98
|
+
p012y = avg(p01y, p12y);
|
|
99
|
+
const p123x = avg(p12x, p23x),
|
|
100
|
+
p123y = avg(p12y, p23y);
|
|
101
|
+
const midx = avg(p012x, p123x),
|
|
102
|
+
midy = avg(p012y, p123y);
|
|
103
|
+
return [
|
|
104
|
+
[round4(p01x), round4(p01y), round4(p012x), round4(p012y), round4(midx), round4(midy)],
|
|
105
|
+
[round4(p123x), round4(p123y), round4(p23x), round4(p23y), x3, y3],
|
|
106
|
+
];
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function splitAll(cmds: Cmd[]): Cmd[] {
|
|
110
|
+
const out: Cmd[] = [];
|
|
111
|
+
let cx = 0,
|
|
112
|
+
cy = 0;
|
|
113
|
+
for (const cmd of cmds) {
|
|
114
|
+
if (cmd.t === 'M') {
|
|
115
|
+
cx = cmd.a[0];
|
|
116
|
+
cy = cmd.a[1];
|
|
117
|
+
out.push(cmd);
|
|
118
|
+
} else if (cmd.t === 'C') {
|
|
119
|
+
const [l, ri] = splitCubic(cx, cy, cmd.a);
|
|
120
|
+
out.push({ t: 'C', a: l }, { t: 'C', a: ri });
|
|
121
|
+
cx = cmd.a[4];
|
|
122
|
+
cy = cmd.a[5];
|
|
123
|
+
} else {
|
|
124
|
+
out.push(cmd);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return out;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function splitN(cmds: Cmd[], n: number): Cmd[] {
|
|
131
|
+
const total = cmds.filter(c => c.t === 'C').length;
|
|
132
|
+
const stride = total / n;
|
|
133
|
+
const toSplit = new Set<number>();
|
|
134
|
+
for (let i = 0; i < n; i++) {
|
|
135
|
+
toSplit.add(Math.round(i * stride));
|
|
136
|
+
}
|
|
137
|
+
const out: Cmd[] = [];
|
|
138
|
+
let cx = 0,
|
|
139
|
+
cy = 0,
|
|
140
|
+
idx = 0;
|
|
141
|
+
for (const cmd of cmds) {
|
|
142
|
+
if (cmd.t === 'M') {
|
|
143
|
+
cx = cmd.a[0];
|
|
144
|
+
cy = cmd.a[1];
|
|
145
|
+
out.push(cmd);
|
|
146
|
+
} else if (cmd.t === 'C') {
|
|
147
|
+
if (toSplit.has(idx)) {
|
|
148
|
+
const [l, ri] = splitCubic(cx, cy, cmd.a);
|
|
149
|
+
out.push({ t: 'C', a: l }, { t: 'C', a: ri });
|
|
150
|
+
} else {
|
|
151
|
+
out.push(cmd);
|
|
152
|
+
}
|
|
153
|
+
cx = cmd.a[4];
|
|
154
|
+
cy = cmd.a[5];
|
|
155
|
+
idx++;
|
|
156
|
+
} else {
|
|
157
|
+
out.push(cmd);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return out;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function expandToCount(cmds: Cmd[], target: number): Cmd[] {
|
|
164
|
+
let cur = cmds;
|
|
165
|
+
while (true) {
|
|
166
|
+
const count = cur.filter(c => c.t === 'C').length;
|
|
167
|
+
if (count >= target) return cur;
|
|
168
|
+
const needed = target - count;
|
|
169
|
+
cur = needed >= count ? splitAll(cur) : splitN(cur, needed);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function serialize(cmds: Cmd[]): string {
|
|
174
|
+
return cmds.map(({ t, a }) => (a.length ? `${t}${a.join(' ')}` : t)).join('');
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function rotateByR(cmds: Cmd[], rot: number): Cmd[] {
|
|
178
|
+
if (rot === 0) return cmds;
|
|
179
|
+
const draws = cmds.filter(c => c.t === 'C');
|
|
180
|
+
const n = draws.length;
|
|
181
|
+
const rotated = [...draws.slice(rot), ...draws.slice(0, rot)];
|
|
182
|
+
const prev = draws[(rot - 1 + n) % n];
|
|
183
|
+
return [{ t: 'M', a: [prev.a[4], prev.a[5]] }, ...rotated, { t: 'Z', a: [] }];
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function alignToAngle(cmds: Cmd[], targetAngle: number, cx = 19, cy = 19): Cmd[] {
|
|
187
|
+
const draws = cmds.filter(c => c.t === 'C');
|
|
188
|
+
const n = draws.length;
|
|
189
|
+
const Mcmd = cmds.find(c => c.t === 'M')!;
|
|
190
|
+
|
|
191
|
+
let bestRot = 0,
|
|
192
|
+
bestDiff = Infinity;
|
|
193
|
+
for (let i = 0; i < n; i++) {
|
|
194
|
+
const sx = i === 0 ? Mcmd.a[0] : draws[i - 1].a[4];
|
|
195
|
+
const sy = i === 0 ? Mcmd.a[1] : draws[i - 1].a[5];
|
|
196
|
+
const angle = Math.atan2(sy - cy, sx - cx);
|
|
197
|
+
const diff = Math.abs(
|
|
198
|
+
((((angle - targetAngle) % (2 * Math.PI)) + 3 * Math.PI) % (2 * Math.PI)) - Math.PI,
|
|
199
|
+
);
|
|
200
|
+
if (diff < bestDiff) {
|
|
201
|
+
bestDiff = diff;
|
|
202
|
+
bestRot = i;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return rotateByR(cmds, bestRot);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Normalize an array of SVG path strings so they all have the same command
|
|
210
|
+
* sequence and phase-aligned start points — a requirement for smooth SMIL
|
|
211
|
+
* `<animate attributeName="d">` morphing across all browsers including Safari.
|
|
212
|
+
*/
|
|
213
|
+
export function normalizePaths(paths: string[]): string[] {
|
|
214
|
+
const allCubic = paths.map(p => lToC(tokenize(p)));
|
|
215
|
+
const max = Math.max(...allCubic.map(c => c.filter(x => x.t === 'C').length));
|
|
216
|
+
const expanded = allCubic.map(c => expandToCount(c, max));
|
|
217
|
+
return expanded.map(c => serialize(alignToAngle(c, -Math.PI / 2)));
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export const [
|
|
221
|
+
normalizedSoftBurstPath,
|
|
222
|
+
normalizedCookie9Path,
|
|
223
|
+
normalizedPentagonPath,
|
|
224
|
+
normalizedPillPath,
|
|
225
|
+
normalizedSunnyPath,
|
|
226
|
+
normalizedCookie4Path,
|
|
227
|
+
normalizedOvalPath,
|
|
228
|
+
] = normalizePaths([
|
|
229
|
+
softBurstPath,
|
|
230
|
+
cookie9Path,
|
|
231
|
+
pentagonPath,
|
|
232
|
+
pillPath,
|
|
233
|
+
sunnyPath,
|
|
234
|
+
cookie4Path,
|
|
235
|
+
ovalPath,
|
|
236
|
+
]);
|
|
@@ -4,9 +4,6 @@ import { StyleSheet } from 'react-native-unistyles';
|
|
|
4
4
|
import { getRegisteredComponentStylesWithFallback } from '../../core';
|
|
5
5
|
import { tokenStylesParser } from '../../utils/tokenStylesParser';
|
|
6
6
|
|
|
7
|
-
/**
|
|
8
|
-
* Props for the LoadingIndicator component.
|
|
9
|
-
*/
|
|
10
7
|
export type Props = ViewProps & {
|
|
11
8
|
/**
|
|
12
9
|
* Whether the indicator is animating. When false, the indicator is hidden.
|
|
@@ -34,34 +31,6 @@ export type Props = ViewProps & {
|
|
|
34
31
|
innerContainerProps?: ViewProps;
|
|
35
32
|
};
|
|
36
33
|
|
|
37
|
-
/* Soft Burst */
|
|
38
|
-
export const softBurstPath =
|
|
39
|
-
'M17.4683 2.83978C18.1732 1.72008 19.8268 1.72007 20.5317 2.83977L22.2214 5.52373C22.6848 6.25987 23.6197 6.55915 24.4338 6.23198L27.4021 5.0391C28.6404 4.54145 29.9781 5.49904 29.8804 6.8131L29.6461 9.96297C29.5819 10.8269 30.1597 11.6104 31.0135 11.8172L34.1265 12.571C35.4252 12.8855 35.9362 14.4349 35.0731 15.4414L33.0044 17.854C32.437 18.5158 32.437 19.4842 33.0044 20.146L35.0731 22.5586C35.9362 23.5651 35.4252 25.1145 34.1265 25.429L31.0135 26.1828C30.1597 26.3896 29.5819 27.1731 29.6461 28.037L29.8804 31.1869C29.9781 32.501 28.6404 33.4585 27.4021 32.9609L24.4338 31.768C23.6197 31.4408 22.6848 31.7401 22.2214 32.4763L20.5317 35.1602C19.8268 36.2799 18.1732 36.2799 17.4683 35.1602L15.7786 32.4763C15.3152 31.7401 14.3803 31.4408 13.5662 31.768L10.5979 32.9609C9.35964 33.4585 8.02187 32.501 8.1196 31.1869L8.35388 28.037C8.41814 27.1731 7.84033 26.3896 6.98651 26.1828L3.87348 25.429C2.57479 25.1145 2.0638 23.5651 2.92684 22.5586L4.99559 20.146C5.56299 19.4842 5.56299 18.5158 4.99559 17.854L2.92685 15.4414C2.0638 14.4349 2.57478 12.8855 3.87348 12.571L6.98651 11.8172C7.84033 11.6104 8.41814 10.8269 8.35388 9.96297L8.1196 6.81311C8.02187 5.49904 9.35964 4.54145 10.5979 5.0391L13.5662 6.23198C14.3803 6.55915 15.3152 6.25987 15.7786 5.52373L17.4683 2.83978Z';
|
|
40
|
-
|
|
41
|
-
/* 9-sided Cookie */
|
|
42
|
-
export const cookie9Path =
|
|
43
|
-
'M15.3091 3.60363C15.4924 3.454 15.584 3.37919 15.6677 3.31603C17.6389 1.82799 20.3611 1.82799 22.3323 3.31603C22.416 3.37919 22.5076 3.454 22.6909 3.60363C22.7727 3.67042 22.8136 3.70381 22.8541 3.7356C23.7818 4.46445 24.9191 4.87748 26.0993 4.91409C26.1508 4.91569 26.2037 4.91634 26.3094 4.91765C26.5462 4.92059 26.6646 4.92206 26.7694 4.92733C29.2381 5.05162 31.3234 6.79743 31.8748 9.20154C31.8982 9.30358 31.9202 9.41966 31.9642 9.65183C31.9838 9.75547 31.9937 9.80729 32.0042 9.85759C32.2452 11.0109 32.8504 12.0567 33.7309 12.8416C33.7693 12.8759 33.8094 12.9103 33.8895 12.9791C34.069 13.1332 34.1588 13.2102 34.2357 13.2815C36.0467 14.96 36.5194 17.6347 35.393 19.8299C35.3451 19.9231 35.2872 20.0262 35.1714 20.2322C35.1196 20.3242 35.0938 20.3702 35.0694 20.4155C34.5111 21.4536 34.3009 22.6429 34.4697 23.8088C34.4771 23.8597 34.4856 23.9117 34.5027 24.0158C34.5409 24.249 34.56 24.3656 34.573 24.4695C34.879 26.9168 33.5179 29.2689 31.2407 30.2281C31.1441 30.2688 31.0333 30.3106 30.8118 30.3942C30.7129 30.4315 30.6635 30.4501 30.6156 30.4692C29.5192 30.9063 28.592 31.6826 27.9701 32.684C27.943 32.7277 27.916 32.7731 27.862 32.8637C27.741 33.0669 27.6806 33.1685 27.6236 33.2564C26.2814 35.3273 23.7234 36.2563 21.3609 35.5306C21.2606 35.4998 21.1489 35.4608 20.9253 35.3827C20.8256 35.3479 20.7757 35.3305 20.7268 35.3144C19.6052 34.9461 18.3948 34.9461 17.2732 35.3144C17.2243 35.3305 17.1744 35.3479 17.0747 35.3827C16.8511 35.4608 16.7394 35.4998 16.6391 35.5306C14.2767 36.2563 11.7186 35.3273 10.3764 33.2564C10.3194 33.1685 10.259 33.0669 10.138 32.8637C10.084 32.7731 10.057 32.7277 10.0299 32.684C9.40803 31.6826 8.48083 30.9063 7.38436 30.4692C7.33654 30.4501 7.28709 30.4315 7.18821 30.3942C6.96669 30.3106 6.85593 30.2688 6.75928 30.2281C4.48205 29.2689 3.12097 26.9168 3.42698 24.4695C3.43997 24.3656 3.45908 24.249 3.4973 24.0158C3.51436 23.9117 3.52289 23.8597 3.53026 23.8088C3.69906 22.6429 3.48889 21.4536 2.93056 20.4155C2.90621 20.3702 2.88035 20.3242 2.82863 20.2322C2.71278 20.0262 2.65485 19.9231 2.60704 19.8299C1.48057 17.6347 1.95327 14.96 3.76433 13.2815C3.8412 13.2102 3.93096 13.1332 4.11047 12.9791C4.19061 12.9103 4.23068 12.8759 4.26908 12.8416C5.14958 12.0567 5.75476 11.0109 5.99583 9.8576C6.00634 9.80729 6.01617 9.75547 6.03582 9.65183C6.07984 9.41966 6.10185 9.30358 6.12525 9.20154C6.67662 6.79743 8.76192 5.05162 11.2306 4.92733C11.3354 4.92206 11.4538 4.92059 11.6906 4.91765C11.7963 4.91634 11.8492 4.91569 11.9007 4.91409C13.0809 4.87748 14.2182 4.46445 15.1459 3.7356C15.1864 3.70381 15.2273 3.67042 15.3091 3.60363Z';
|
|
44
|
-
|
|
45
|
-
/* Pentagon */
|
|
46
|
-
export const pentagonPath =
|
|
47
|
-
'M13.6481 4.93735C15.3067 3.72903 16.136 3.12486 17.022 2.82514C18.3035 2.39162 19.6965 2.39162 20.978 2.82514C21.864 3.12486 22.6933 3.72903 24.3519 4.93735L27.9794 7.58012L31.6049 10.0564C33.3249 11.2312 34.1849 11.8185 34.7598 12.5573C35.5914 13.6258 36.0282 14.9391 35.9986 16.2824C35.9781 17.211 35.6363 18.1817 34.9526 20.1232L33.529 24.1657L32.215 28.308C31.5983 30.2519 31.29 31.2239 30.7509 31.98C29.9712 33.0736 28.838 33.878 27.5341 34.2634C26.6327 34.5299 25.5936 34.5144 23.5154 34.4835L19 34.4162L14.4846 34.4835C12.4064 34.5144 11.3673 34.5299 10.4659 34.2634C9.16204 33.878 8.02882 33.0736 7.24909 31.98C6.71002 31.2239 6.40169 30.2519 5.78503 28.308L4.47101 24.1657L3.04741 20.1232C2.36372 18.1817 2.02187 17.211 2.00141 16.2824C1.97182 14.9391 2.40855 13.6258 3.24017 12.5573C3.8151 11.8185 4.67508 11.2312 6.39505 10.0564L10.0206 7.58012L13.6481 4.93735Z';
|
|
48
|
-
|
|
49
|
-
/* Pill */
|
|
50
|
-
export const pillPath =
|
|
51
|
-
'M11.9857 7.77706C17.0217 2.74098 25.1869 2.74098 30.2229 7.77706C35.259 12.8131 35.259 20.9783 30.2229 26.0143L26.0143 30.2229C20.9783 35.259 12.8131 35.259 7.77706 30.2229C2.74098 25.1869 2.74098 17.0217 7.77706 11.9857L11.9857 7.77706Z';
|
|
52
|
-
|
|
53
|
-
/* Sunny */
|
|
54
|
-
export const sunnyPath =
|
|
55
|
-
'M28.1856 6.12375C29.2431 6.19561 29.7718 6.23154 30.1991 6.41844C30.8175 6.68887 31.3111 7.18252 31.5816 7.80088C31.7685 8.22823 31.8044 8.75694 31.8762 9.81438L32.0402 12.2275C32.0693 12.6552 32.0838 12.869 32.1303 13.0733C32.1975 13.3684 32.3142 13.6501 32.4754 13.9063C32.5869 14.0836 32.7279 14.2451 33.0097 14.5681L34.6001 16.3903C35.297 17.1889 35.6455 17.5881 35.8155 18.0225C36.0615 18.6509 36.0615 19.3491 35.8155 19.9775C35.6455 20.4119 35.297 20.8111 34.6001 21.6097L33.0097 23.4319C32.7279 23.7549 32.5869 23.9164 32.4754 24.0937C32.3142 24.3499 32.1975 24.6316 32.1303 24.9267C32.0838 25.131 32.0693 25.3448 32.0402 25.7725L31.8762 28.1856C31.8044 29.2431 31.7685 29.7718 31.5816 30.1991C31.3111 30.8175 30.8175 31.3111 30.1991 31.5816C29.7718 31.7685 29.2431 31.8044 28.1856 31.8762L25.7725 32.0402C25.3448 32.0693 25.131 32.0838 24.9267 32.1303C24.6316 32.1975 24.3499 32.3142 24.0937 32.4754C23.9164 32.5869 23.7549 32.7279 23.4319 33.0097L21.6097 34.6001C20.8111 35.297 20.4119 35.6455 19.9775 35.8155C19.3491 36.0615 18.6509 36.0615 18.0225 35.8155C17.5881 35.6455 17.1889 35.297 16.3903 34.6001L14.5681 33.0097C14.2451 32.7279 14.0836 32.5869 13.9063 32.4754C13.6501 32.3142 13.3684 32.1975 13.0733 32.1303C12.869 32.0838 12.6552 32.0693 12.2275 32.0402L9.81438 31.8762C8.75694 31.8044 8.22822 31.7685 7.80088 31.5816C7.18252 31.3111 6.68887 30.8175 6.41844 30.1991C6.23154 29.7718 6.19561 29.2431 6.12375 28.1856L5.95977 25.7725C5.93071 25.3448 5.91618 25.131 5.86969 24.9267C5.80251 24.6316 5.68584 24.3499 5.52463 24.0937C5.41306 23.9164 5.27213 23.7549 4.99027 23.4319L3.3999 21.6097C2.703 20.8111 2.35454 20.4119 2.18452 19.9775C1.93849 19.3491 1.93849 18.6509 2.18452 18.0225C2.35454 17.5881 2.703 17.1889 3.3999 16.3903L4.99027 14.5681C5.27213 14.2451 5.41306 14.0836 5.52463 13.9063C5.68584 13.6501 5.80251 13.3684 5.86969 13.0733C5.91618 12.869 5.93071 12.6552 5.95977 12.2275L6.12375 9.81438C6.19561 8.75694 6.23154 8.22823 6.41844 7.80088C6.68887 7.18252 7.18252 6.68887 7.80088 6.41844C8.22823 6.23154 8.75694 6.19561 9.81438 6.12375L12.2275 5.95977C12.6552 5.93071 12.869 5.91618 13.0733 5.86969C13.3684 5.80251 13.6501 5.68584 13.9063 5.52463C14.0836 5.41306 14.2451 5.27213 14.5681 4.99027L16.3903 3.3999C17.1889 2.703 17.5881 2.35454 18.0225 2.18452C18.6509 1.93849 19.3491 1.93849 19.9775 2.18452C20.4119 2.35454 20.8111 2.703 21.6097 3.3999L23.4319 4.99027C23.7549 5.27213 23.9164 5.41306 24.0937 5.52463C24.3499 5.68584 24.6316 5.80251 24.9267 5.86969C25.131 5.91618 25.3448 5.93071 25.7725 5.95977L28.1856 6.12375Z';
|
|
56
|
-
|
|
57
|
-
/* 4-sided Cookie */
|
|
58
|
-
export const cookie4Path =
|
|
59
|
-
'M22.8729 5.6207C28.8872 3.00862 34.9914 9.11282 32.3793 15.1271L31.9474 16.1214C31.1499 17.9576 31.1499 20.0424 31.9474 21.8786L32.3793 22.8729C34.9914 28.8872 28.8872 34.9914 22.8729 32.3793L21.8786 31.9474C20.0424 31.1499 17.9576 31.1499 16.1214 31.9474L15.1271 32.3793C9.11281 34.9914 3.00862 28.8872 5.6207 22.8729L6.05257 21.8786C6.85006 20.0424 6.85005 17.9576 6.05257 16.1214L5.6207 15.1271C3.00862 9.11281 9.11282 3.00862 15.1271 5.6207L16.1214 6.05257C17.9576 6.85005 20.0424 6.85005 21.8786 6.05257L22.8729 5.6207Z';
|
|
60
|
-
|
|
61
|
-
/* Oval */
|
|
62
|
-
export const ovalPath =
|
|
63
|
-
'M27.1309 27.1309C20.1705 34.0913 10.8877 36.0935 6.39707 31.6029C1.90648 27.1123 3.90867 17.8295 10.8691 10.8691C17.8295 3.90867 27.1123 1.90648 31.6029 6.39707C36.0935 10.8877 34.0913 20.1705 27.1309 27.1309Z';
|
|
64
|
-
|
|
65
34
|
export const expressFastSpatial = 'cubic-bezier(0.42, 1.67, 0.21, 0.90)';
|
|
66
35
|
|
|
67
36
|
export const useProcessProps = ({
|