react-native-ultra-carousel 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +81 -0
- package/src/animations/basic/fade.ts +51 -0
- package/src/animations/basic/overlap.ts +69 -0
- package/src/animations/basic/parallax.ts +65 -0
- package/src/animations/basic/peek.ts +79 -0
- package/src/animations/basic/scale.ts +63 -0
- package/src/animations/basic/scaleFade.ts +73 -0
- package/src/animations/basic/slide.ts +53 -0
- package/src/animations/basic/slideFade.ts +60 -0
- package/src/animations/basic/vertical.ts +50 -0
- package/src/animations/basic/verticalFade.ts +60 -0
- package/src/animations/index.ts +45 -0
- package/src/animations/registry.ts +175 -0
- package/src/animations/types.ts +11 -0
- package/src/animations/utils.ts +75 -0
- package/src/components/AutoPlayController.tsx +38 -0
- package/src/components/Carousel.tsx +371 -0
- package/src/components/CarouselItem.tsx +98 -0
- package/src/components/Pagination/BarPagination.tsx +141 -0
- package/src/components/Pagination/CustomPagination.tsx +48 -0
- package/src/components/Pagination/DotPagination.tsx +137 -0
- package/src/components/Pagination/NumberPagination.tsx +117 -0
- package/src/components/Pagination/Pagination.tsx +82 -0
- package/src/components/Pagination/ProgressPagination.tsx +70 -0
- package/src/components/Pagination/index.ts +11 -0
- package/src/components/ParallaxImage.tsx +89 -0
- package/src/gestures/FlingGestureManager.ts +49 -0
- package/src/gestures/PanGestureManager.ts +202 -0
- package/src/gestures/ScrollViewCompat.ts +28 -0
- package/src/gestures/types.ts +6 -0
- package/src/hooks/useAnimationProgress.ts +33 -0
- package/src/hooks/useAutoPlay.ts +115 -0
- package/src/hooks/useCarousel.ts +118 -0
- package/src/hooks/useCarouselGesture.ts +109 -0
- package/src/hooks/useItemAnimation.ts +44 -0
- package/src/hooks/usePagination.ts +39 -0
- package/src/hooks/useSnapPoints.ts +31 -0
- package/src/hooks/useVirtualization.ts +63 -0
- package/src/index.ts +71 -0
- package/src/plugins/PluginManager.ts +150 -0
- package/src/plugins/types.ts +6 -0
- package/src/types/animation.ts +72 -0
- package/src/types/carousel.ts +188 -0
- package/src/types/gesture.ts +42 -0
- package/src/types/index.ts +41 -0
- package/src/types/pagination.ts +65 -0
- package/src/types/plugin.ts +27 -0
- package/src/utils/accessibility.ts +71 -0
- package/src/utils/constants.ts +45 -0
- package/src/utils/layout.ts +115 -0
- package/src/utils/math.ts +78 -0
- package/src/utils/platform.ts +33 -0
package/package.json
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "react-native-ultra-carousel",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "The Ultimate Carousel Ecosystem for React Native. 35+ animation presets, plugin system, CLI tool. Built on Reanimated.",
|
|
5
|
+
"main": "lib/commonjs/index.js",
|
|
6
|
+
"module": "lib/module/index.js",
|
|
7
|
+
"types": "lib/typescript/index.d.ts",
|
|
8
|
+
"react-native": "src/index.ts",
|
|
9
|
+
"source": "src/index.ts",
|
|
10
|
+
"files": [
|
|
11
|
+
"src",
|
|
12
|
+
"lib",
|
|
13
|
+
"!**/__tests__",
|
|
14
|
+
"!**/__fixtures__",
|
|
15
|
+
"!**/__mocks__"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "bob build",
|
|
19
|
+
"test": "jest",
|
|
20
|
+
"typecheck": "tsc --noEmit",
|
|
21
|
+
"lint": "eslint src/ --ext .ts,.tsx",
|
|
22
|
+
"clean": "del-cli lib"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"react-native",
|
|
26
|
+
"carousel",
|
|
27
|
+
"slider",
|
|
28
|
+
"swiper",
|
|
29
|
+
"animation",
|
|
30
|
+
"reanimated",
|
|
31
|
+
"expo",
|
|
32
|
+
"ios",
|
|
33
|
+
"android",
|
|
34
|
+
"typescript",
|
|
35
|
+
"gesture",
|
|
36
|
+
"3d",
|
|
37
|
+
"parallax",
|
|
38
|
+
"coverflow",
|
|
39
|
+
"tinder",
|
|
40
|
+
"stack"
|
|
41
|
+
],
|
|
42
|
+
"repository": {
|
|
43
|
+
"type": "git",
|
|
44
|
+
"url": "https://github.com/toankhontech/react-native-ultra-carousel"
|
|
45
|
+
},
|
|
46
|
+
"author": "toankhontech",
|
|
47
|
+
"license": "MIT",
|
|
48
|
+
"homepage": "https://github.com/toankhontech/react-native-ultra-carousel",
|
|
49
|
+
"bugs": {
|
|
50
|
+
"url": "https://github.com/toankhontech/react-native-ultra-carousel/issues"
|
|
51
|
+
},
|
|
52
|
+
"peerDependencies": {
|
|
53
|
+
"react": ">=18.0.0",
|
|
54
|
+
"react-native": ">=0.72.0",
|
|
55
|
+
"react-native-reanimated": ">=3.0.0",
|
|
56
|
+
"react-native-gesture-handler": ">=2.9.0"
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"@types/react": "^18.2.0",
|
|
60
|
+
"react": "^18.2.0",
|
|
61
|
+
"react-native": "^0.73.0",
|
|
62
|
+
"react-native-reanimated": "^3.6.0",
|
|
63
|
+
"react-native-gesture-handler": "^2.14.0",
|
|
64
|
+
"react-native-builder-bob": "^0.23.0",
|
|
65
|
+
"jest": "^29.7.0",
|
|
66
|
+
"@testing-library/react-native": "^12.0.0",
|
|
67
|
+
"typescript": "^5.4.0",
|
|
68
|
+
"del-cli": "^5.1.0",
|
|
69
|
+
"@types/jest": "^29.5.0",
|
|
70
|
+
"react-test-renderer": "^18.2.0"
|
|
71
|
+
},
|
|
72
|
+
"react-native-builder-bob": {
|
|
73
|
+
"source": "src",
|
|
74
|
+
"output": "lib",
|
|
75
|
+
"targets": [
|
|
76
|
+
"commonjs",
|
|
77
|
+
"module",
|
|
78
|
+
["typescript", { "project": "tsconfig.build.json" }]
|
|
79
|
+
]
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Fade animation preset
|
|
3
|
+
* @description Crossfade transition between items — items stack and opacity changes
|
|
4
|
+
* @preset fade
|
|
5
|
+
* @difficulty Easy
|
|
6
|
+
* @phase 1
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { interpolate, Extrapolation } from 'react-native-reanimated';
|
|
10
|
+
import type { AnimatedItemStyle } from '../types';
|
|
11
|
+
import { computeZIndex } from '../utils';
|
|
12
|
+
|
|
13
|
+
/** Configuration for the fade animation */
|
|
14
|
+
export interface FadeConfig {
|
|
15
|
+
/** Minimum opacity for inactive items (default: 0) */
|
|
16
|
+
minOpacity: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Default fade configuration */
|
|
20
|
+
export const FADE_DEFAULTS: FadeConfig = {
|
|
21
|
+
minOpacity: 0,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Fade animation worklet.
|
|
26
|
+
* Items stack on top of each other, only opacity transitions.
|
|
27
|
+
*
|
|
28
|
+
* @param progress - Normalized item progress: 0 = active, -1 = prev, +1 = next
|
|
29
|
+
* @param config - Optional overrides for default configuration
|
|
30
|
+
* @returns Animated style to apply to the carousel item
|
|
31
|
+
*/
|
|
32
|
+
export const fadeAnimation = (
|
|
33
|
+
progress: number,
|
|
34
|
+
config?: Partial<FadeConfig>
|
|
35
|
+
): AnimatedItemStyle => {
|
|
36
|
+
'worklet';
|
|
37
|
+
|
|
38
|
+
const c = { ...FADE_DEFAULTS, ...config };
|
|
39
|
+
|
|
40
|
+
const opacity = interpolate(
|
|
41
|
+
Math.abs(progress),
|
|
42
|
+
[0, 1],
|
|
43
|
+
[1, c.minOpacity],
|
|
44
|
+
Extrapolation.CLAMP
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
opacity,
|
|
49
|
+
zIndex: computeZIndex(progress),
|
|
50
|
+
};
|
|
51
|
+
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Overlap animation preset
|
|
3
|
+
* @description Items overlap each other by 30%, creating a stacked card appearance
|
|
4
|
+
* @preset overlap
|
|
5
|
+
* @difficulty Medium
|
|
6
|
+
* @phase 1
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { interpolate, Extrapolation } from 'react-native-reanimated';
|
|
10
|
+
import type { AnimatedItemStyle } from '../types';
|
|
11
|
+
import { computeZIndex } from '../utils';
|
|
12
|
+
|
|
13
|
+
/** Configuration for the overlap animation */
|
|
14
|
+
export interface OverlapConfig {
|
|
15
|
+
/** Overlap ratio — 0.7 means 30% overlap (default: 0.7) */
|
|
16
|
+
overlapRatio: number;
|
|
17
|
+
/** Scale of non-active items (default: 0.95) */
|
|
18
|
+
minScale: number;
|
|
19
|
+
/** Item width for offset calculations (default: 300) */
|
|
20
|
+
itemWidth: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Default overlap configuration */
|
|
24
|
+
export const OVERLAP_DEFAULTS: OverlapConfig = {
|
|
25
|
+
overlapRatio: 0.7,
|
|
26
|
+
minScale: 0.95,
|
|
27
|
+
itemWidth: 300,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Overlap animation worklet.
|
|
32
|
+
* Items translate at a reduced distance so they overlap.
|
|
33
|
+
* Active item sits on top with highest zIndex.
|
|
34
|
+
*
|
|
35
|
+
* @param progress - Normalized item progress: 0 = active, -1 = prev, +1 = next
|
|
36
|
+
* @param config - Optional overrides for default configuration
|
|
37
|
+
* @returns Animated style to apply to the carousel item
|
|
38
|
+
*/
|
|
39
|
+
export const overlapAnimation = (
|
|
40
|
+
progress: number,
|
|
41
|
+
config?: Partial<OverlapConfig>
|
|
42
|
+
): AnimatedItemStyle => {
|
|
43
|
+
'worklet';
|
|
44
|
+
|
|
45
|
+
const c = { ...OVERLAP_DEFAULTS, ...config };
|
|
46
|
+
|
|
47
|
+
const translateX = interpolate(
|
|
48
|
+
progress,
|
|
49
|
+
[-1, 0, 1],
|
|
50
|
+
[-(c.itemWidth * c.overlapRatio), 0, c.itemWidth * c.overlapRatio],
|
|
51
|
+
Extrapolation.CLAMP
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
const scale = interpolate(
|
|
55
|
+
Math.abs(progress),
|
|
56
|
+
[0, 1],
|
|
57
|
+
[1, c.minScale],
|
|
58
|
+
Extrapolation.CLAMP
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
transform: [
|
|
63
|
+
{ translateX },
|
|
64
|
+
{ scale },
|
|
65
|
+
],
|
|
66
|
+
zIndex: computeZIndex(progress),
|
|
67
|
+
opacity: 1,
|
|
68
|
+
};
|
|
69
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Parallax animation preset
|
|
3
|
+
* @description Multi-layer parallax effect with foreground and background moving at different speeds
|
|
4
|
+
* @preset parallax
|
|
5
|
+
* @difficulty Medium
|
|
6
|
+
* @phase 1
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { interpolate, Extrapolation } from 'react-native-reanimated';
|
|
10
|
+
import type { AnimatedItemStyle } from '../types';
|
|
11
|
+
|
|
12
|
+
/** Configuration for the parallax animation */
|
|
13
|
+
export interface ParallaxConfig {
|
|
14
|
+
/** Speed ratio for parallax layers (default: 0.3) — lower = more parallax */
|
|
15
|
+
parallaxFactor: number;
|
|
16
|
+
/** Slide distance in pixels (default: 300) */
|
|
17
|
+
distance: number;
|
|
18
|
+
/** Minimum opacity for inactive items (default: 0.8) */
|
|
19
|
+
minOpacity: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Default parallax configuration */
|
|
23
|
+
export const PARALLAX_DEFAULTS: ParallaxConfig = {
|
|
24
|
+
parallaxFactor: 0.3,
|
|
25
|
+
distance: 300,
|
|
26
|
+
minOpacity: 0.8,
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Parallax animation worklet.
|
|
31
|
+
* Background content moves slower than the container for a depth effect.
|
|
32
|
+
* The foreground (item container) translates at full speed while the inner
|
|
33
|
+
* content translates at a reduced rate (parallaxFactor).
|
|
34
|
+
*
|
|
35
|
+
* @param progress - Normalized item progress: 0 = active, -1 = prev, +1 = next
|
|
36
|
+
* @param config - Optional overrides for default configuration
|
|
37
|
+
* @returns Animated style to apply to the carousel item
|
|
38
|
+
*/
|
|
39
|
+
export const parallaxAnimation = (
|
|
40
|
+
progress: number,
|
|
41
|
+
config?: Partial<ParallaxConfig>
|
|
42
|
+
): AnimatedItemStyle => {
|
|
43
|
+
'worklet';
|
|
44
|
+
|
|
45
|
+
const c = { ...PARALLAX_DEFAULTS, ...config };
|
|
46
|
+
|
|
47
|
+
const translateX = interpolate(
|
|
48
|
+
progress,
|
|
49
|
+
[-1, 0, 1],
|
|
50
|
+
[-c.distance * c.parallaxFactor, 0, c.distance * c.parallaxFactor],
|
|
51
|
+
Extrapolation.CLAMP
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
const opacity = interpolate(
|
|
55
|
+
Math.abs(progress),
|
|
56
|
+
[0, 1],
|
|
57
|
+
[1, c.minOpacity],
|
|
58
|
+
Extrapolation.CLAMP
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
transform: [{ translateX }],
|
|
63
|
+
opacity,
|
|
64
|
+
};
|
|
65
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Peek animation preset
|
|
3
|
+
* @description Active item centered with adjacent items peeking from the sides
|
|
4
|
+
* @preset peek
|
|
5
|
+
* @difficulty Medium
|
|
6
|
+
* @phase 1
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { interpolate, Extrapolation } from 'react-native-reanimated';
|
|
10
|
+
import type { AnimatedItemStyle } from '../types';
|
|
11
|
+
|
|
12
|
+
/** Configuration for the peek animation */
|
|
13
|
+
export interface PeekConfig {
|
|
14
|
+
/** How much of the adjacent item is visible — 0.2 = 20% (default: 0.2) */
|
|
15
|
+
peekAmount: number;
|
|
16
|
+
/** Scale of peeking items (default: 0.85) */
|
|
17
|
+
peekScale: number;
|
|
18
|
+
/** Opacity of peeking items (default: 0.7) */
|
|
19
|
+
peekOpacity: number;
|
|
20
|
+
/** Item width for peek offset calculations (default: 300) */
|
|
21
|
+
itemWidth: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Default peek configuration */
|
|
25
|
+
export const PEEK_DEFAULTS: PeekConfig = {
|
|
26
|
+
peekAmount: 0.2,
|
|
27
|
+
peekScale: 0.85,
|
|
28
|
+
peekOpacity: 0.7,
|
|
29
|
+
itemWidth: 300,
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Peek animation worklet.
|
|
34
|
+
* Centers the active item while showing edges of adjacent items.
|
|
35
|
+
* Peeking items are scaled down and slightly faded.
|
|
36
|
+
*
|
|
37
|
+
* @param progress - Normalized item progress: 0 = active, -1 = prev, +1 = next
|
|
38
|
+
* @param config - Optional overrides for default configuration
|
|
39
|
+
* @returns Animated style to apply to the carousel item
|
|
40
|
+
*/
|
|
41
|
+
export const peekAnimation = (
|
|
42
|
+
progress: number,
|
|
43
|
+
config?: Partial<PeekConfig>
|
|
44
|
+
): AnimatedItemStyle => {
|
|
45
|
+
'worklet';
|
|
46
|
+
|
|
47
|
+
const c = { ...PEEK_DEFAULTS, ...config };
|
|
48
|
+
|
|
49
|
+
const peekOffset = c.itemWidth * (1 - c.peekAmount);
|
|
50
|
+
|
|
51
|
+
const translateX = interpolate(
|
|
52
|
+
progress,
|
|
53
|
+
[-1, 0, 1],
|
|
54
|
+
[-peekOffset, 0, peekOffset],
|
|
55
|
+
Extrapolation.CLAMP
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
const scale = interpolate(
|
|
59
|
+
Math.abs(progress),
|
|
60
|
+
[0, 1],
|
|
61
|
+
[1, c.peekScale],
|
|
62
|
+
Extrapolation.CLAMP
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
const opacity = interpolate(
|
|
66
|
+
Math.abs(progress),
|
|
67
|
+
[0, 1],
|
|
68
|
+
[1, c.peekOpacity],
|
|
69
|
+
Extrapolation.CLAMP
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
transform: [
|
|
74
|
+
{ translateX },
|
|
75
|
+
{ scale },
|
|
76
|
+
],
|
|
77
|
+
opacity,
|
|
78
|
+
};
|
|
79
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Scale animation preset
|
|
3
|
+
* @description Active item at full scale, neighbors shrink — creates depth emphasis
|
|
4
|
+
* @preset scale
|
|
5
|
+
* @difficulty Easy
|
|
6
|
+
* @phase 1
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { interpolate, Extrapolation } from 'react-native-reanimated';
|
|
10
|
+
import type { AnimatedItemStyle } from '../types';
|
|
11
|
+
|
|
12
|
+
/** Configuration for the scale animation */
|
|
13
|
+
export interface ScaleConfig {
|
|
14
|
+
/** Scale of inactive items (default: 0.8) */
|
|
15
|
+
minScale: number;
|
|
16
|
+
/** Horizontal spacing between items (default: 50) */
|
|
17
|
+
spacing: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Default scale configuration */
|
|
21
|
+
export const SCALE_DEFAULTS: ScaleConfig = {
|
|
22
|
+
minScale: 0.8,
|
|
23
|
+
spacing: 50,
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Scale animation worklet.
|
|
28
|
+
* Active item is full size, adjacent items scale down to create a size hierarchy.
|
|
29
|
+
*
|
|
30
|
+
* @param progress - Normalized item progress: 0 = active, -1 = prev, +1 = next
|
|
31
|
+
* @param config - Optional overrides for default configuration
|
|
32
|
+
* @returns Animated style to apply to the carousel item
|
|
33
|
+
*/
|
|
34
|
+
export const scaleAnimation = (
|
|
35
|
+
progress: number,
|
|
36
|
+
config?: Partial<ScaleConfig>
|
|
37
|
+
): AnimatedItemStyle => {
|
|
38
|
+
'worklet';
|
|
39
|
+
|
|
40
|
+
const c = { ...SCALE_DEFAULTS, ...config };
|
|
41
|
+
|
|
42
|
+
const scale = interpolate(
|
|
43
|
+
Math.abs(progress),
|
|
44
|
+
[0, 1],
|
|
45
|
+
[1, c.minScale],
|
|
46
|
+
Extrapolation.CLAMP
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
const translateX = interpolate(
|
|
50
|
+
progress,
|
|
51
|
+
[-1, 0, 1],
|
|
52
|
+
[-c.spacing, 0, c.spacing],
|
|
53
|
+
Extrapolation.CLAMP
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
transform: [
|
|
58
|
+
{ translateX },
|
|
59
|
+
{ scale },
|
|
60
|
+
],
|
|
61
|
+
opacity: 1,
|
|
62
|
+
};
|
|
63
|
+
};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Scale-fade animation preset
|
|
3
|
+
* @description Combines scale and opacity for an elegant depth + fade effect
|
|
4
|
+
* @preset scale-fade
|
|
5
|
+
* @difficulty Easy
|
|
6
|
+
* @phase 1
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { interpolate, Extrapolation } from 'react-native-reanimated';
|
|
10
|
+
import type { AnimatedItemStyle } from '../types';
|
|
11
|
+
|
|
12
|
+
/** Configuration for the scale-fade animation */
|
|
13
|
+
export interface ScaleFadeConfig {
|
|
14
|
+
/** Scale of inactive items (default: 0.85) */
|
|
15
|
+
minScale: number;
|
|
16
|
+
/** Minimum opacity for inactive items (default: 0.5) */
|
|
17
|
+
minOpacity: number;
|
|
18
|
+
/** Horizontal spacing (default: 40) */
|
|
19
|
+
spacing: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Default scale-fade configuration */
|
|
23
|
+
export const SCALE_FADE_DEFAULTS: ScaleFadeConfig = {
|
|
24
|
+
minScale: 0.85,
|
|
25
|
+
minOpacity: 0.5,
|
|
26
|
+
spacing: 40,
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Scale-fade animation worklet.
|
|
31
|
+
* Combines scale reduction with opacity fade for a polished transition.
|
|
32
|
+
*
|
|
33
|
+
* @param progress - Normalized item progress: 0 = active, -1 = prev, +1 = next
|
|
34
|
+
* @param config - Optional overrides for default configuration
|
|
35
|
+
* @returns Animated style to apply to the carousel item
|
|
36
|
+
*/
|
|
37
|
+
export const scaleFadeAnimation = (
|
|
38
|
+
progress: number,
|
|
39
|
+
config?: Partial<ScaleFadeConfig>
|
|
40
|
+
): AnimatedItemStyle => {
|
|
41
|
+
'worklet';
|
|
42
|
+
|
|
43
|
+
const c = { ...SCALE_FADE_DEFAULTS, ...config };
|
|
44
|
+
|
|
45
|
+
const scale = interpolate(
|
|
46
|
+
Math.abs(progress),
|
|
47
|
+
[0, 1],
|
|
48
|
+
[1, c.minScale],
|
|
49
|
+
Extrapolation.CLAMP
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
const opacity = interpolate(
|
|
53
|
+
Math.abs(progress),
|
|
54
|
+
[0, 1],
|
|
55
|
+
[1, c.minOpacity],
|
|
56
|
+
Extrapolation.CLAMP
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
const translateX = interpolate(
|
|
60
|
+
progress,
|
|
61
|
+
[-1, 0, 1],
|
|
62
|
+
[-c.spacing, 0, c.spacing],
|
|
63
|
+
Extrapolation.CLAMP
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
transform: [
|
|
68
|
+
{ translateX },
|
|
69
|
+
{ scale },
|
|
70
|
+
],
|
|
71
|
+
opacity,
|
|
72
|
+
};
|
|
73
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Slide animation preset
|
|
3
|
+
* @description Standard horizontal slide transition — the default carousel behavior
|
|
4
|
+
* @preset slide
|
|
5
|
+
* @difficulty Easy
|
|
6
|
+
* @phase 1
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { interpolate, Extrapolation } from 'react-native-reanimated';
|
|
10
|
+
import type { AnimatedItemStyle } from '../types';
|
|
11
|
+
|
|
12
|
+
/** Configuration for the slide animation */
|
|
13
|
+
export interface SlideConfig {
|
|
14
|
+
/** Slide distance multiplier relative to item width (default: 1) */
|
|
15
|
+
distance: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Default slide configuration */
|
|
19
|
+
export const SLIDE_DEFAULTS: SlideConfig = {
|
|
20
|
+
distance: 1,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/** Default item width used for slide calculations */
|
|
24
|
+
const DEFAULT_ITEM_WIDTH = 300;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Slide animation worklet.
|
|
28
|
+
* Translates items horizontally based on scroll progress.
|
|
29
|
+
*
|
|
30
|
+
* @param progress - Normalized item progress: 0 = active, -1 = prev, +1 = next
|
|
31
|
+
* @param config - Optional overrides for default configuration
|
|
32
|
+
* @returns Animated style to apply to the carousel item
|
|
33
|
+
*/
|
|
34
|
+
export const slideAnimation = (
|
|
35
|
+
progress: number,
|
|
36
|
+
config?: Partial<SlideConfig>
|
|
37
|
+
): AnimatedItemStyle => {
|
|
38
|
+
'worklet';
|
|
39
|
+
|
|
40
|
+
const c = { ...SLIDE_DEFAULTS, ...config };
|
|
41
|
+
|
|
42
|
+
const translateX = interpolate(
|
|
43
|
+
progress,
|
|
44
|
+
[-1, 0, 1],
|
|
45
|
+
[-DEFAULT_ITEM_WIDTH * c.distance, 0, DEFAULT_ITEM_WIDTH * c.distance],
|
|
46
|
+
Extrapolation.CLAMP
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
transform: [{ translateX }],
|
|
51
|
+
opacity: 1,
|
|
52
|
+
};
|
|
53
|
+
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Slide-fade animation preset
|
|
3
|
+
* @description Combines horizontal slide with opacity fade for an elegant transition
|
|
4
|
+
* @preset slide-fade
|
|
5
|
+
* @difficulty Easy
|
|
6
|
+
* @phase 1
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { interpolate, Extrapolation } from 'react-native-reanimated';
|
|
10
|
+
import type { AnimatedItemStyle } from '../types';
|
|
11
|
+
|
|
12
|
+
/** Configuration for the slide-fade animation */
|
|
13
|
+
export interface SlideFadeConfig {
|
|
14
|
+
/** Slide distance in pixels (default: 200) */
|
|
15
|
+
distance: number;
|
|
16
|
+
/** Minimum opacity for inactive items (default: 0.3) */
|
|
17
|
+
minOpacity: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Default slide-fade configuration */
|
|
21
|
+
export const SLIDE_FADE_DEFAULTS: SlideFadeConfig = {
|
|
22
|
+
distance: 200,
|
|
23
|
+
minOpacity: 0.3,
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Slide-fade animation worklet.
|
|
28
|
+
* Combines translateX movement with opacity reduction for smooth transitions.
|
|
29
|
+
*
|
|
30
|
+
* @param progress - Normalized item progress: 0 = active, -1 = prev, +1 = next
|
|
31
|
+
* @param config - Optional overrides for default configuration
|
|
32
|
+
* @returns Animated style to apply to the carousel item
|
|
33
|
+
*/
|
|
34
|
+
export const slideFadeAnimation = (
|
|
35
|
+
progress: number,
|
|
36
|
+
config?: Partial<SlideFadeConfig>
|
|
37
|
+
): AnimatedItemStyle => {
|
|
38
|
+
'worklet';
|
|
39
|
+
|
|
40
|
+
const c = { ...SLIDE_FADE_DEFAULTS, ...config };
|
|
41
|
+
|
|
42
|
+
const translateX = interpolate(
|
|
43
|
+
progress,
|
|
44
|
+
[-1, 0, 1],
|
|
45
|
+
[-c.distance, 0, c.distance],
|
|
46
|
+
Extrapolation.CLAMP
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
const opacity = interpolate(
|
|
50
|
+
Math.abs(progress),
|
|
51
|
+
[0, 1],
|
|
52
|
+
[1, c.minOpacity],
|
|
53
|
+
Extrapolation.CLAMP
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
transform: [{ translateX }],
|
|
58
|
+
opacity,
|
|
59
|
+
};
|
|
60
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Vertical animation preset
|
|
3
|
+
* @description Vertical slide transition for vertical carousels
|
|
4
|
+
* @preset vertical
|
|
5
|
+
* @difficulty Easy
|
|
6
|
+
* @phase 1
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { interpolate, Extrapolation } from 'react-native-reanimated';
|
|
10
|
+
import type { AnimatedItemStyle } from '../types';
|
|
11
|
+
|
|
12
|
+
/** Configuration for the vertical animation */
|
|
13
|
+
export interface VerticalConfig {
|
|
14
|
+
/** Vertical distance multiplier (default: 250) */
|
|
15
|
+
distance: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Default vertical configuration */
|
|
19
|
+
export const VERTICAL_DEFAULTS: VerticalConfig = {
|
|
20
|
+
distance: 250,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Vertical animation worklet.
|
|
25
|
+
* Translates items vertically instead of horizontally.
|
|
26
|
+
*
|
|
27
|
+
* @param progress - Normalized item progress: 0 = active, -1 = prev, +1 = next
|
|
28
|
+
* @param config - Optional overrides for default configuration
|
|
29
|
+
* @returns Animated style to apply to the carousel item
|
|
30
|
+
*/
|
|
31
|
+
export const verticalAnimation = (
|
|
32
|
+
progress: number,
|
|
33
|
+
config?: Partial<VerticalConfig>
|
|
34
|
+
): AnimatedItemStyle => {
|
|
35
|
+
'worklet';
|
|
36
|
+
|
|
37
|
+
const c = { ...VERTICAL_DEFAULTS, ...config };
|
|
38
|
+
|
|
39
|
+
const translateY = interpolate(
|
|
40
|
+
progress,
|
|
41
|
+
[-1, 0, 1],
|
|
42
|
+
[-c.distance, 0, c.distance],
|
|
43
|
+
Extrapolation.CLAMP
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
transform: [{ translateY }],
|
|
48
|
+
opacity: 1,
|
|
49
|
+
};
|
|
50
|
+
};
|