From 7a0e3ab15fbe4a93ed81a84fdb3bccaafa2a2e16 Mon Sep 17 00:00:00 2001 From: Luke Walczak Date: Wed, 26 Jan 2022 20:35:47 +0100 Subject: [PATCH] fix: correct Card animation drivers related to elevation (#3041) --- example/src/Examples/CardExample.tsx | 20 ++++++++ example/src/index.tsx | 2 +- src/components/Card/Card.tsx | 77 ++++++++++++++++++++++------ 3 files changed, 81 insertions(+), 18 deletions(-) diff --git a/example/src/Examples/CardExample.tsx b/example/src/Examples/CardExample.tsx index 292cb3c3c8..92a1b5780b 100644 --- a/example/src/Examples/CardExample.tsx +++ b/example/src/Examples/CardExample.tsx @@ -10,6 +10,7 @@ import { Text, Switch, } from 'react-native-paper'; +import { PreferencesContext } from '..'; import ScreenWrapper from '../ScreenWrapper'; const CardExample = () => { @@ -19,6 +20,8 @@ const CardExample = () => { const [isOutlined, setIsOutlined] = React.useState(false); const mode = isOutlined ? 'outlined' : 'elevated'; + const preferences = React.useContext(PreferencesContext); + return ( @@ -120,6 +123,23 @@ const CardExample = () => { + { + preferences.toggleTheme(); + }} + mode={mode} + > + } + /> + + + This is pressable card. If you press me, I will switch the theme. + + + ); diff --git a/example/src/index.tsx b/example/src/index.tsx index 559e04211e..96dcf1ab6f 100644 --- a/example/src/index.tsx +++ b/example/src/index.tsx @@ -70,7 +70,7 @@ const CustomDefaultTheme = { }, }; -const PreferencesContext = React.createContext(null); +export const PreferencesContext = React.createContext(null); const DrawerContent = () => { return ( diff --git a/src/components/Card/Card.tsx b/src/components/Card/Card.tsx index 6a76c90782..d504524116 100644 --- a/src/components/Card/Card.tsx +++ b/src/components/Card/Card.tsx @@ -28,6 +28,8 @@ type ElevatedCardProps = { elevation?: number; }; +type HandlePressType = 'in' | 'out'; + type Props = React.ComponentProps & { /** * Resting elevation of the card which controls the drop shadow. @@ -111,30 +113,69 @@ const Card = ({ accessible, ...rest }: (OutlinedCardProps | ElevatedCardProps) & Props) => { + // Default animated value const { current: elevation } = React.useRef( new Animated.Value(cardElevation) ); + // Dark adaptive animated value, used in case of toggling the theme, + // it prevents animating the background with native drivers inside Surface + const { current: elevationDarkAdaptive } = React.useRef( + new Animated.Value(cardElevation) + ); const { animation, dark, mode, roundness } = theme; + const prevDarkRef = React.useRef(dark); + React.useEffect(() => { + prevDarkRef.current = dark; + }); + + const prevDark = prevDarkRef.current; + const isAdaptiveMode = mode === 'adaptive'; + const animationDuration = 150 * animation.scale; + + React.useEffect(() => { + /** + * Resets animations values if updating to dark adaptive mode, + * otherwise, any card that is in the middle of animation while + * toggling the theme will stay at that animated value until + * the next press-in + */ + if (dark && isAdaptiveMode && !prevDark) { + elevation.setValue(cardElevation); + elevationDarkAdaptive.setValue(cardElevation); + } + }, [ + prevDark, + dark, + isAdaptiveMode, + cardElevation, + elevation, + elevationDarkAdaptive, + ]); + + const runElevationAnimation = (pressType: HandlePressType) => { + const isPressTypeIn = pressType === 'in'; + if (dark && isAdaptiveMode) { + Animated.timing(elevationDarkAdaptive, { + toValue: isPressTypeIn ? 8 : cardElevation, + duration: animationDuration, + useNativeDriver: false, + }).start(); + } else { + Animated.timing(elevation, { + toValue: isPressTypeIn ? 8 : cardElevation, + duration: animationDuration, + useNativeDriver: true, + }).start(); + } + }; + const handlePressIn = () => { - const { - dark, - mode, - animation: { scale }, - } = theme; - Animated.timing(elevation, { - toValue: 8, - duration: 150 * scale, - useNativeDriver: !dark || mode === 'exact', - }).start(); + runElevationAnimation('in'); }; const handlePressOut = () => { - Animated.timing(elevation, { - toValue: cardElevation, - duration: 150 * animation.scale, - useNativeDriver: !dark || mode === 'exact', - }).start(); + runElevationAnimation('out'); }; const total = React.Children.count(children); @@ -143,15 +184,17 @@ const Card = ({ ? (child.type as any).displayName : null ); - const borderColor = color(theme.dark ? white : black) + const borderColor = color(dark ? white : black) .alpha(0.12) .rgb() .string(); + const computedElevation = + dark && isAdaptiveMode ? elevationDarkAdaptive : elevation; return (