Created
August 11, 2021 04:42
-
-
Save dheysonalves/120f3022f721ffd9f43cc71823164187 to your computer and use it in GitHub Desktop.
Animated Cards Testing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* eslint-disable guard-for-in */ | |
/* eslint-disable no-restricted-syntax */ | |
import React, { useState, useCallback } from 'react'; | |
import { useWindowDimensions } from 'react-native'; | |
import { PanGestureHandler } from 'react-native-gesture-handler'; | |
import Animated, { | |
cancelAnimation, | |
runOnJS, | |
scrollTo, | |
useAnimatedGestureHandler, | |
useAnimatedReaction, | |
useAnimatedRef, | |
useAnimatedScrollHandler, | |
useAnimatedStyle, | |
useSharedValue, | |
withSpring, | |
withTiming, | |
} from 'react-native-reanimated'; | |
import { useSafeAreaInsets } from 'react-native-safe-area-context'; | |
import { useTheme } from '#common/context'; | |
import { DashboardCard } from '#core/components'; | |
import { DashboardCardsProps } from './dashboardcards.type'; | |
const DASHBOARD_CARD_HEIGHT = 90.0; | |
const SCROLL_HEIGHT_THRESHOLD = DASHBOARD_CARD_HEIGHT; | |
const elementsArray = [ | |
{ | |
id: 'requested', | |
clientCardType: 'requested', | |
cardState: 'approved', | |
gradientColors: ['#fff', '#fff'], | |
headerTitle: 'Acompanhe seu cartão', | |
headerTitleColor: '#000', | |
position: 0, | |
}, | |
{ | |
id: 'credit', | |
clientCardType: 'credit', | |
cardState: 'analysis', | |
headerTitle: 'Cartão de Crédito', | |
headerTitleColor: '#fff', | |
gradientColors: ['#EC008C', '#62003A'], | |
position: 100, | |
}, | |
{ | |
id: 'digital', | |
clientCardType: 'requested', | |
cardState: 'approved', | |
gradientColors: ['#0D223A', '#1D57A5'], | |
headerTitle: 'Conta Digital', | |
headerTitleColor: '#fff', | |
position: 190, | |
}, | |
]; | |
function clamp(value: any, lowerBound: any, upperBound: any) { | |
'worklet'; | |
return Math.max(lowerBound, Math.min(value, upperBound)); | |
} | |
function objectMove(object: any, from: any, to: any) { | |
'worklet'; | |
const newObject = { ...object }; | |
for (const id in object) { | |
if (object[id] === from) { | |
newObject[id] = to; | |
} | |
if (object[id] === to) { | |
newObject[id] = from; | |
} | |
} | |
return newObject; | |
} | |
function listToObject(list: any) { | |
const values = Object.values(list); | |
const object = {}; | |
for (let i = 0; i < values.length; i++) { | |
object[values[i].id] = i; | |
} | |
return object; | |
} | |
function MovableCard({ | |
clientCardType, | |
cardState, | |
positions, | |
headerTitle, | |
id, | |
headerTitleColor, | |
gradientColors, | |
scrollY, | |
cardsCount, | |
}: { | |
clientCardType: string; | |
cardState: string; | |
headerTitle: string; | |
id: number | string; | |
headerTitleColor: string; | |
gradientColors: string[]; | |
positions: any; | |
scrollY: any; | |
cardsCount: number; | |
}) { | |
const [moving, setMoving] = useState(false); | |
const top = useSharedValue(positions.value[id] * DASHBOARD_CARD_HEIGHT); | |
const dimensions = useWindowDimensions(); | |
const insets = useSafeAreaInsets(); | |
useAnimatedReaction( | |
() => positions.value[id], | |
(currentPosition, previousPosition) => { | |
if (currentPosition !== previousPosition) { | |
if (!moving) { | |
top.value = withSpring(currentPosition * DASHBOARD_CARD_HEIGHT); | |
} | |
} | |
}, | |
[moving], | |
); | |
const gestureHandler = useAnimatedGestureHandler({ | |
onStart() { | |
runOnJS(setMoving)(true); | |
}, | |
onActive(event) { | |
const positionY = event.absoluteY + scrollY.value; | |
if (positionY <= scrollY.value + SCROLL_HEIGHT_THRESHOLD) { | |
// Scroll up | |
scrollY.value = withTiming(0, { duration: 1500 }); | |
} else if ( | |
positionY >= | |
scrollY.value + dimensions.height - SCROLL_HEIGHT_THRESHOLD | |
) { | |
// Scroll down | |
const contentHeight = cardsCount * DASHBOARD_CARD_HEIGHT; | |
const containerHeight = dimensions.height - insets.top - insets.bottom; | |
const maxScroll = contentHeight - containerHeight; | |
scrollY.value = withTiming(maxScroll, { duration: 1500 }); | |
} else { | |
cancelAnimation(scrollY); | |
} | |
top.value = withTiming(positionY - DASHBOARD_CARD_HEIGHT, { | |
duration: 16, | |
}); | |
// CRASHES HERE WHEM USING IT | |
const newPosition = clamp( | |
Math.floor(positionY / DASHBOARD_CARD_HEIGHT), | |
0, | |
cardsCount - 1, | |
); | |
// if (newPosition !== positions.value[id]) { | |
// positions.value = objectMove( | |
// positions.value, | |
// positions.value[id], | |
// newPosition, | |
// ); | |
// } | |
}, | |
onFinish() { | |
top.value = positions.value[id] * DASHBOARD_CARD_HEIGHT; | |
runOnJS(setMoving)(false); | |
}, | |
}); | |
const animatedStyle = useAnimatedStyle(() => { | |
return { | |
position: 'absolute', | |
left: 0, | |
right: 0, | |
top: top.value, | |
zIndex: moving ? 1 : 0, | |
shadowColor: 'black', | |
shadowOffset: { | |
height: 0, | |
width: 0, | |
}, | |
shadowOpacity: withSpring(moving ? 0.2 : 0), | |
shadowRadius: 10, | |
}; | |
}, [moving]); | |
return ( | |
<Animated.View style={animatedStyle}> | |
<PanGestureHandler onGestureEvent={gestureHandler}> | |
<Animated.View> | |
<DashboardCard | |
position="relative" | |
h="100%" | |
w="100%" | |
clientCardType={clientCardType} | |
cardState={cardState} | |
headerTitle={headerTitle} | |
headerTitleColor={headerTitleColor} | |
gradientColors={gradientColors} | |
zIndex={2} | |
/> | |
</Animated.View> | |
</PanGestureHandler> | |
</Animated.View> | |
); | |
} | |
const DashboardCards: React.FC<DashboardCardsProps> = () => { | |
const { theme } = useTheme(); | |
const [cardState, setCardState] = useState<string>('credit'); | |
const positionsY = useSharedValue(listToObject(elementsArray)); | |
const scrollY = useSharedValue(0); | |
const scrollViewRef = useAnimatedRef(); | |
// TODO Calculate the top position based on the viewPort | |
const handleCardState = useCallback((type: string) => { | |
setCardState(type); | |
}, []); | |
useAnimatedReaction( | |
() => scrollY.value, | |
(scrolling) => scrollTo(scrollViewRef, 0, scrolling, false), | |
); | |
const handleScroll = useAnimatedScrollHandler((event) => { | |
scrollY.value = event.contentOffset.y; | |
}); | |
return ( | |
<Animated.ScrollView | |
ref={scrollViewRef} | |
contentContainerStyle={{ | |
height: elementsArray.length * DASHBOARD_CARD_HEIGHT, | |
}} | |
onScroll={handleScroll} | |
scrollEventThrottle={16} | |
style={{ | |
flex: 1, | |
position: 'relative', | |
}} | |
> | |
{elementsArray.map((card, index) => ( | |
<MovableCard | |
id={card.id} | |
cardState={card.cardState} | |
headerTitle={card.headerTitle} | |
clientCardType={card.clientCardType} | |
headerTitleColor={card.headerTitleColor} | |
key={card.id} | |
gradientColors={card.gradientColors} | |
positions={positionsY} | |
scrollY={scrollY} | |
cardsCount={elementsArray.length} | |
/> | |
))} | |
</Animated.ScrollView> | |
); | |
}; | |
export { DashboardCards }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment