|
1 | 1 | import * as React from 'react'; |
2 | | -import { Extrapolate, interpolate } from 'react-native-reanimated'; |
3 | | -import Carousel from 'react-native-reanimated-carousel'; |
4 | | -import { View, Text } from 'react-native-ui-lib'; |
5 | | -import type { TAnimationStyle } from '../../../src/layouts/BaseLayout'; |
| 2 | +import { Pressable } from 'react-native'; |
| 3 | +import Animated, { |
| 4 | + Extrapolate, |
| 5 | + interpolate, |
| 6 | + interpolateColor, |
| 7 | + useAnimatedStyle, |
| 8 | + useSharedValue, |
| 9 | + withTiming, |
| 10 | +} from 'react-native-reanimated'; |
| 11 | +import Carousel, { ICarouselInstance } from 'react-native-reanimated-carousel'; |
| 12 | +import { Colors, View } from 'react-native-ui-lib'; |
| 13 | +import SButton from '../components/SButton'; |
6 | 14 | import { ElementsText, window } from '../constants'; |
7 | 15 | import { useToggleButton } from '../hooks/useToggleButton'; |
8 | 16 |
|
9 | | -const PAGE_WIDTH = 40; |
| 17 | +const PAGE_WIDTH = 60; |
10 | 18 | const PAGE_HEIGHT = 40; |
| 19 | +const DATA = ['周一', '周二', '周三', '周四', '周五', '周六', '周日']; |
11 | 20 |
|
12 | 21 | function Index() { |
| 22 | + const r = React.useRef<ICarouselInstance>(null); |
13 | 23 | const AutoPLay = useToggleButton({ |
14 | 24 | defaultValue: false, |
15 | 25 | buttonTitle: ElementsText.AUTOPLAY, |
16 | 26 | }); |
17 | | - |
18 | | - const animationStyle: TAnimationStyle = React.useCallback( |
19 | | - (value: number) => { |
20 | | - 'worklet'; |
21 | | - |
22 | | - const translateX = interpolate( |
23 | | - value, |
24 | | - [-1, 0, 1], |
25 | | - [-PAGE_WIDTH, 0, PAGE_WIDTH] |
26 | | - ); |
27 | | - |
28 | | - const opacity = interpolate( |
29 | | - value, |
30 | | - [-1, 0, 1], |
31 | | - [0.5, 1, 0.5], |
32 | | - Extrapolate.CLAMP |
33 | | - ); |
34 | | - |
35 | | - const scale = interpolate( |
36 | | - value, |
37 | | - [-1, 0, 1], |
38 | | - [0.8, 1.4, 0.8], |
39 | | - Extrapolate.CLAMP |
40 | | - ); |
41 | | - |
42 | | - return { |
43 | | - transform: [{ translateX }, { scale }], |
44 | | - opacity, |
45 | | - }; |
46 | | - }, |
47 | | - [] |
48 | | - ); |
| 27 | + const [loop, setLoop] = React.useState(false); |
49 | 28 |
|
50 | 29 | return ( |
51 | 30 | <View style={{ flex: 1 }}> |
52 | | - <Carousel |
53 | | - loop={false} |
| 31 | + <View style={{ marginVertical: 100 }}> |
| 32 | + <Carousel |
| 33 | + key={`${loop}`} |
| 34 | + ref={r} |
| 35 | + loop={loop} |
| 36 | + style={{ |
| 37 | + width: window.width, |
| 38 | + height: PAGE_HEIGHT, |
| 39 | + justifyContent: 'center', |
| 40 | + alignItems: 'center', |
| 41 | + borderBottomWidth: 1, |
| 42 | + borderBottomColor: Colors.blue30, |
| 43 | + }} |
| 44 | + width={PAGE_WIDTH} |
| 45 | + height={PAGE_HEIGHT} |
| 46 | + data={DATA} |
| 47 | + renderItem={({ item, animationValue }) => { |
| 48 | + return ( |
| 49 | + <Item |
| 50 | + animationValue={animationValue} |
| 51 | + label={item} |
| 52 | + onPress={() => |
| 53 | + r.current?.scrollTo({ |
| 54 | + count: animationValue.value, |
| 55 | + animated: true, |
| 56 | + }) |
| 57 | + } |
| 58 | + /> |
| 59 | + ); |
| 60 | + }} |
| 61 | + autoPlay={AutoPLay.status} |
| 62 | + /> |
| 63 | + </View> |
| 64 | + {AutoPLay.button} |
| 65 | + <SButton onPress={() => setLoop(!loop)}>{`Loop: ${loop}`}</SButton> |
| 66 | + <View |
54 | 67 | style={{ |
55 | | - width: window.width, |
56 | | - justifyContent: 'center', |
57 | | - alignItems: 'center', |
58 | | - paddingVertical: 110, |
59 | | - }} |
60 | | - width={PAGE_WIDTH} |
61 | | - height={PAGE_HEIGHT} |
62 | | - data={['周一', '周二', '周三', '周四', '周五', '周六', '周日']} |
63 | | - renderItem={({ item }) => { |
64 | | - return ( |
65 | | - <View center height={'100%'}> |
66 | | - <Text color={'#26292E'}>{item}</Text> |
67 | | - </View> |
68 | | - ); |
| 68 | + marginTop: 24, |
| 69 | + flexDirection: 'row', |
| 70 | + justifyContent: 'space-evenly', |
69 | 71 | }} |
70 | | - autoPlay={AutoPLay.status} |
71 | | - customAnimation={animationStyle} |
72 | | - /> |
73 | | - {AutoPLay.button} |
| 72 | + > |
| 73 | + <SButton onPress={() => r.current?.prev()}>{'Prev'}</SButton> |
| 74 | + <SButton onPress={() => r.current?.next()}>{'Next'}</SButton> |
| 75 | + </View> |
74 | 76 | </View> |
75 | 77 | ); |
76 | 78 | } |
77 | 79 |
|
78 | 80 | export default Index; |
| 81 | + |
| 82 | +interface Props { |
| 83 | + animationValue: Animated.SharedValue<number>; |
| 84 | + label: string; |
| 85 | + onPress?: () => void; |
| 86 | +} |
| 87 | + |
| 88 | +const Item: React.FC<Props> = (props) => { |
| 89 | + const { animationValue, label, onPress } = props; |
| 90 | + |
| 91 | + const translateY = useSharedValue(0); |
| 92 | + |
| 93 | + const containerStyle = useAnimatedStyle(() => { |
| 94 | + const opacity = interpolate( |
| 95 | + animationValue.value, |
| 96 | + [-1, 0, 1], |
| 97 | + [0.5, 1, 0.5], |
| 98 | + Extrapolate.CLAMP |
| 99 | + ); |
| 100 | + |
| 101 | + return { |
| 102 | + opacity, |
| 103 | + }; |
| 104 | + }, [animationValue]); |
| 105 | + |
| 106 | + const labelStyle = useAnimatedStyle(() => { |
| 107 | + const scale = interpolate( |
| 108 | + animationValue.value, |
| 109 | + [-1, 0, 1], |
| 110 | + [1, 1.25, 1], |
| 111 | + Extrapolate.CLAMP |
| 112 | + ); |
| 113 | + |
| 114 | + const color = interpolateColor( |
| 115 | + animationValue.value, |
| 116 | + [-1, 0, 1], |
| 117 | + [Colors.grey30, Colors.blue30, Colors.grey30] |
| 118 | + ); |
| 119 | + |
| 120 | + return { |
| 121 | + transform: [{ scale }, { translateY: translateY.value }], |
| 122 | + color, |
| 123 | + }; |
| 124 | + }, [animationValue, translateY]); |
| 125 | + |
| 126 | + const onPressIn = React.useCallback(() => { |
| 127 | + translateY.value = withTiming(-8, { duration: 250 }); |
| 128 | + }, [translateY]); |
| 129 | + |
| 130 | + const onPressOut = React.useCallback(() => { |
| 131 | + translateY.value = withTiming(0, { duration: 250 }); |
| 132 | + }, [translateY]); |
| 133 | + |
| 134 | + return ( |
| 135 | + <Pressable |
| 136 | + onPress={onPress} |
| 137 | + onPressIn={onPressIn} |
| 138 | + onPressOut={onPressOut} |
| 139 | + > |
| 140 | + <Animated.View |
| 141 | + style={[ |
| 142 | + { |
| 143 | + height: '100%', |
| 144 | + alignItems: 'center', |
| 145 | + justifyContent: 'center', |
| 146 | + }, |
| 147 | + containerStyle, |
| 148 | + ]} |
| 149 | + > |
| 150 | + <Animated.Text |
| 151 | + style={[{ fontSize: 18, color: '#26292E' }, labelStyle]} |
| 152 | + > |
| 153 | + {label} |
| 154 | + </Animated.Text> |
| 155 | + </Animated.View> |
| 156 | + </Pressable> |
| 157 | + ); |
| 158 | +}; |
0 commit comments