Initial commit
This commit is contained in:
108
src/components/cardStack/hooks/usePhoneAnimations.ts
Normal file
108
src/components/cardStack/hooks/usePhoneAnimations.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
import { useEffect, useRef } from "react";
|
||||
import { gsap } from "gsap";
|
||||
import { ScrollTrigger } from "gsap/ScrollTrigger";
|
||||
|
||||
gsap.registerPlugin(ScrollTrigger);
|
||||
|
||||
export interface TimelinePhoneViewItem {
|
||||
trigger: string;
|
||||
content: React.ReactNode;
|
||||
imageOne?: string;
|
||||
videoOne?: string;
|
||||
imageAltOne?: string;
|
||||
videoAriaLabelOne?: string;
|
||||
imageTwo?: string;
|
||||
videoTwo?: string;
|
||||
imageAltTwo?: string;
|
||||
videoAriaLabelTwo?: string;
|
||||
}
|
||||
|
||||
const getImageAnimationConfig = (itemIndex: number, imageIndex: number) => {
|
||||
const isFirstImage = imageIndex === 0;
|
||||
const isOddItem = itemIndex % 2 === 1;
|
||||
|
||||
if (isFirstImage) {
|
||||
return {
|
||||
from: { xPercent: -200, rotation: -45 },
|
||||
to: { rotation: isOddItem ? 10 : -10 },
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
from: { xPercent: 200, rotation: 45 },
|
||||
to: { rotation: isOddItem ? -10 : 10 },
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export const usePhoneAnimations = (items: TimelinePhoneViewItem[]) => {
|
||||
const imageRefs = useRef<(HTMLDivElement | null)[]>([]);
|
||||
const mobileImageRefs = useRef<(HTMLDivElement | null)[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
const mm = gsap.matchMedia();
|
||||
|
||||
const animatePhones = (isMobile: boolean) => {
|
||||
items.forEach((item, itemIndex) => {
|
||||
const images = [item.imageOne || item.videoOne, item.imageTwo || item.videoTwo];
|
||||
|
||||
images.forEach((_, imageIndex) => {
|
||||
const refIndex = itemIndex * 2 + imageIndex;
|
||||
const element = isMobile
|
||||
? mobileImageRefs.current[refIndex]
|
||||
: imageRefs.current[refIndex];
|
||||
|
||||
if (element) {
|
||||
const isFirstImage = imageIndex === 0;
|
||||
|
||||
const fromConfig = isMobile
|
||||
? {
|
||||
xPercent: isFirstImage ? -150 : 150,
|
||||
rotation: isFirstImage ? -25 : 25,
|
||||
}
|
||||
: getImageAnimationConfig(itemIndex, imageIndex).from;
|
||||
|
||||
const toConfig = isMobile
|
||||
? {
|
||||
xPercent: 0,
|
||||
rotation: 0,
|
||||
duration: 1,
|
||||
scrollTrigger: {
|
||||
trigger: element,
|
||||
start: "top 90%",
|
||||
end: "top 50%",
|
||||
scrub: 1,
|
||||
},
|
||||
}
|
||||
: {
|
||||
xPercent: 0,
|
||||
rotation: getImageAnimationConfig(itemIndex, imageIndex).to
|
||||
.rotation,
|
||||
scrollTrigger: {
|
||||
trigger: `.${item.trigger}`,
|
||||
start: "top bottom",
|
||||
end: "top top",
|
||||
scrub: 1,
|
||||
},
|
||||
};
|
||||
|
||||
gsap.fromTo(element, fromConfig, toConfig);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
mm.add("(max-width: 767px)", () => animatePhones(true));
|
||||
mm.add("(min-width: 768px)", () => animatePhones(false));
|
||||
|
||||
return () => {
|
||||
mm.revert();
|
||||
imageRefs.current = [];
|
||||
mobileImageRefs.current = [];
|
||||
};
|
||||
}, [items]);
|
||||
|
||||
return {
|
||||
imageRefs,
|
||||
mobileImageRefs,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user