From e7b33b75c9572bfd75f70d30a2e23f0aa90e48d5 Mon Sep 17 00:00:00 2001 From: sasha-astiadi Date: Thu, 6 Nov 2025 21:40:26 +0100 Subject: [PATCH] feat: add animated transitions to cloud features tabs - Implemented slide and fade animations when switching between feature tabs using Framer Motion - Added animated background indicator that follows the selected tab - Enhanced hover states with scale transitions and outline effects for better interactivity --- src/pages/cloud/CloudFeaturesLight.tsx | 133 +++++++++++++++++++++---- 1 file changed, 116 insertions(+), 17 deletions(-) diff --git a/src/pages/cloud/CloudFeaturesLight.tsx b/src/pages/cloud/CloudFeaturesLight.tsx index 8bd5996..e5c095c 100644 --- a/src/pages/cloud/CloudFeaturesLight.tsx +++ b/src/pages/cloud/CloudFeaturesLight.tsx @@ -130,27 +130,111 @@ const features = [ ] +interface CustomAnimationProps { + isForwards: boolean + changeCount: number +} + +const maxZIndex = 2147483647 + +const bodyVariantBackwards: Variant = { + opacity: 0.4, + scale: 0.8, + zIndex: 0, + filter: 'blur(4px)', + transition: { duration: 0.4 }, +} + +const bodyAnimation: MotionProps = { + initial: 'initial', + animate: 'animate', + exit: 'exit', + variants: { + initial: (custom: CustomAnimationProps) => + custom.isForwards + ? { + y: '100%', + zIndex: maxZIndex - custom.changeCount, + transition: { duration: 0.4 }, + } + : bodyVariantBackwards, + animate: (custom: CustomAnimationProps) => ({ + y: '0%', + opacity: 1, + scale: 1, + zIndex: maxZIndex / 2 - custom.changeCount, + filter: 'blur(0px)', + transition: { duration: 0.4 }, + }), + exit: (custom: CustomAnimationProps) => + custom.isForwards + ? bodyVariantBackwards + : { + y: '100%', + zIndex: maxZIndex - custom.changeCount, + transition: { duration: 0.4 }, + }, + }, +} + +function usePrevious(value: T) { + const ref = useRef() + + useEffect(() => { + ref.current = value + }, [value]) + + return ref.current +} + /* Desktop Component */ function CloudFeaturesDesktop() { - const [selectedIndex, setSelectedIndex] = useState(0) + let [changeCount, setChangeCount] = useState(0) + let [selectedIndex, setSelectedIndex] = useState(0) + let prevIndex = usePrevious(selectedIndex) + let isForwards = prevIndex === undefined ? true : selectedIndex > prevIndex + + let onChange = useDebouncedCallback( + (selectedIndex: number) => { + setSelectedIndex(selectedIndex) + setChangeCount((changeCount) => changeCount + 1) + }, + 100, + { leading: true }, + ) return ( - + - {features.map((feature, i) => ( + {features.map((feature, featureIndex) => (
-
+ {featureIndex === selectedIndex && ( + + )} +
- {feature.name} + + + {feature.name} + {feature.description} @@ -160,15 +244,30 @@ function CloudFeaturesDesktop() { ))} -
+
- {features.map((feature, i) => ( - -
- -
-
- ))} + + {features.map((feature, featureIndex) => + selectedIndex === featureIndex ? ( + + + + + + ) : null, + )} +