From f27e3ae50dc70a36a0234ccc91e8a5883bd0515d Mon Sep 17 00:00:00 2001 From: Damiano Plebani Date: Wed, 10 Apr 2024 10:15:09 +0200 Subject: [PATCH] Change opacity transition from discrete to continuous --- example/src/pages/GradientScroll.tsx | 10 +++--- src/components/layout/GradientScrollView.tsx | 36 +++++++++++--------- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/example/src/pages/GradientScroll.tsx b/example/src/pages/GradientScroll.tsx index 2654d0a3..fd31ff06 100644 --- a/example/src/pages/GradientScroll.tsx +++ b/example/src/pages/GradientScroll.tsx @@ -23,7 +23,7 @@ export const GradientScroll = () => { Alert.alert("Primary action pressed! (⁠⁠ꈍ⁠ᴗ⁠ꈍ⁠)") @@ -31,11 +31,11 @@ export const GradientScroll = () => { secondary: { label: "Secondary", onPress: () => Alert.alert("Secondary action pressed! (⁠⁠ꈍ⁠ᴗ⁠ꈍ⁠)") - }, - tertiary: { - label: "Tertiary", - onPress: () => Alert.alert("Tertiary action pressed! (⁠⁠ꈍ⁠ᴗ⁠ꈍ⁠)") } + // tertiary: { + // label: "Tertiary", + // onPress: () => Alert.alert("Tertiary action pressed! (⁠⁠ꈍ⁠ᴗ⁠ꈍ⁠)") + // } }} >

Start

diff --git a/src/components/layout/GradientScrollView.tsx b/src/components/layout/GradientScrollView.tsx index 7113da5f..31b114b9 100644 --- a/src/components/layout/GradientScrollView.tsx +++ b/src/components/layout/GradientScrollView.tsx @@ -11,10 +11,11 @@ import { easeGradient } from "react-native-easing-gradient"; import LinearGradient from "react-native-linear-gradient"; import Animated, { Easing, + Extrapolate, + interpolate, useAnimatedScrollHandler, useAnimatedStyle, - useSharedValue, - withTiming + useSharedValue } from "react-native-reanimated"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { @@ -57,8 +58,11 @@ type GradientScrollView = WithTestID< }> >; +/* Percentage of scrolled content that triggers + the gradient opaciy transition */ +const gradientOpacityScrollTrigger = 0.85; /* Extended gradient area above the actions */ -export const gradientSafeAreaHeight: IOSpacingScale = 96; +const gradientSafeAreaHeight: IOSpacingScale = 96; /* End content margin before the actions */ const contentEndMargin: IOSpacingScale = 32; /* Margin between ButtonSolid and ButtonOutline */ @@ -102,7 +106,7 @@ export const GradientScrollView = ({ const theme = useIOTheme(); /* Shared Values for `reanimated` */ - const gradientOpacity = useSharedValue(1); + const scrollPositionPercentage = useSharedValue(0); /* Scroll position */ /* Total height of actions */ const [actionBlockHeight, setActionBlockHeight] = @@ -171,24 +175,22 @@ export const GradientScrollView = ({ const handleScroll = useAnimatedScrollHandler( ({ contentOffset, layoutMeasurement, contentSize }) => { - /* We use Math.floor because decimals used on Android - devices never change the `isEndReached` boolean value. - We have more consistent behavior across platforms - if we round these calculations ¯\_(ツ)_/¯ */ - const isEndReached = - Math.floor(layoutMeasurement.height + contentOffset.y) >= - Math.floor(contentSize.height); + const scrollPosition = contentOffset.y; + const maxScrollHeight = contentSize.height - layoutMeasurement.height; + const scrollPercentage = scrollPosition / maxScrollHeight; // eslint-disable-next-line functional/immutable-data - gradientOpacity.value = isEndReached ? 0 : 1; + scrollPositionPercentage.value = scrollPercentage; } ); const opacityTransition = useAnimatedStyle(() => ({ - opacity: withTiming(gradientOpacity.value, { - duration: 200, - easing: Easing.ease - }) + opacity: interpolate( + scrollPositionPercentage.value, + [0, gradientOpacityScrollTrigger, 1], + [1, 1, 0], + Extrapolate.CLAMP + ) })); return ( @@ -196,7 +198,7 @@ export const GradientScrollView = ({