Taun Posted November 28, 2023 Share Posted November 28, 2023 Hi. How should I apply gsapContext() in this React project? I've read the docs - " Let's say you've got a big block of GSAP code that's creating a bunch of different animations and you need to be able to revert() them all... let ctx = gsap.context(() => { gsap.to(...); gsap.from(...); gsap.timeline().to(...).to(...); ... }); // then later... ctx.revert(); // BOOM! Every GSAP animation created in that function gets reverted!" When I try wrap the animation code with ctx, I get syntax errors. I don't know what I'm doing. Any suggestions? Codesandbox: https://codesandbox.io/p/github/taunhealy/Circa9_Website1/remote?layout=%7B%22sidebarPanel%22%3A%22EXPLORER%22%2C%22rootPanelGroup%22%3A%7B%22direction%22%3A%22horizontal%22%2C%22contentType%22%3A%22UNKNOWN%22%2C%22type%22%3A%22PANEL_GROUP%22%2C%22id%22%3A%22ROOT_LAYOUT%22%2C%22panels%22%3A%5B%7B%22type%22%3A%22PANEL_GROUP%22%2C%22contentType%22%3A%22UNKNOWN%22%2C%22direction%22%3A%22vertical%22%2C%22id%22%3A%22clpilwho400i7356he4lpemkn%22%2C%22sizes%22%3A%5B70%2C30%5D%2C%22panels%22%3A%5B%7B%22type%22%3A%22PANEL_GROUP%22%2C%22contentType%22%3A%22EDITOR%22%2C%22direction%22%3A%22horizontal%22%2C%22id%22%3A%22EDITOR%22%2C%22panels%22%3A%5B%7B%22type%22%3A%22PANEL%22%2C%22contentType%22%3A%22EDITOR%22%2C%22id%22%3A%22clpilwho400i2356h5bzguxv5%22%7D%5D%7D%2C%7B%22type%22%3A%22PANEL_GROUP%22%2C%22contentType%22%3A%22SHELLS%22%2C%22direction%22%3A%22horizontal%22%2C%22id%22%3A%22SHELLS%22%2C%22panels%22%3A%5B%7B%22type%22%3A%22PANEL%22%2C%22contentType%22%3A%22SHELLS%22%2C%22id%22%3A%22clpilwho400i4356hwsxjaz1b%22%7D%5D%2C%22sizes%22%3A%5B100%5D%7D%5D%7D%2C%7B%22type%22%3A%22PANEL_GROUP%22%2C%22contentType%22%3A%22DEVTOOLS%22%2C%22direction%22%3A%22vertical%22%2C%22id%22%3A%22DEVTOOLS%22%2C%22panels%22%3A%5B%7B%22type%22%3A%22PANEL%22%2C%22contentType%22%3A%22DEVTOOLS%22%2C%22id%22%3A%22clpilwho400i6356hy0h1wjva%22%7D%5D%2C%22sizes%22%3A%5B100%5D%7D%5D%2C%22sizes%22%3A%5B45.91051099661234%2C54.08948900338766%5D%7D%2C%22tabbedPanels%22%3A%7B%22clpilwho400i2356h5bzguxv5%22%3A%7B%22tabs%22%3A%5B%7B%22id%22%3A%22clpilwho400i1356hzmk7l9tz%22%2C%22mode%22%3A%22permanent%22%2C%22type%22%3A%22FILE%22%2C%22filepath%22%3A%22%2FREADME.md%22%2C%22state%22%3A%22IDLE%22%7D%5D%2C%22id%22%3A%22clpilwho400i2356h5bzguxv5%22%2C%22activeTabId%22%3A%22clpilwho400i1356hzmk7l9tz%22%7D%2C%22clpilwho400i6356hy0h1wjva%22%3A%7B%22tabs%22%3A%5B%7B%22id%22%3A%22clpilwho400i5356hmfd9pzie%22%2C%22mode%22%3A%22permanent%22%2C%22type%22%3A%22TASK_PORT%22%2C%22taskId%22%3A%22dev%22%2C%22port%22%3A3000%2C%22path%22%3A%22%2F%22%7D%5D%2C%22id%22%3A%22clpilwho400i6356hy0h1wjva%22%2C%22activeTabId%22%3A%22clpilwho400i5356hmfd9pzie%22%7D%2C%22clpilwho400i4356hwsxjaz1b%22%3A%7B%22tabs%22%3A%5B%7B%22id%22%3A%22clpilwho400i3356hrt4hf4ia%22%2C%22mode%22%3A%22permanent%22%2C%22type%22%3A%22TASK_LOG%22%2C%22taskId%22%3A%22dev%22%7D%5D%2C%22id%22%3A%22clpilwho400i4356hwsxjaz1b%22%2C%22activeTabId%22%3A%22clpilwho400i3356hrt4hf4ia%22%7D%7D%2C%22showDevtools%22%3Atrue%2C%22showShells%22%3Atrue%2C%22showSidebar%22%3Atrue%2C%22sidebarPanelSize%22%3A22.61421301650344%7D Relevant code: ``` "use client"; import React, { useState, useMemo, useEffect, useRef } from "react"; import gsap from "gsap"; import "./sliderswiper.css"; interface Item { id: number; title: string; img: string; date: string; brand: string; desc?: string; director?: string; producer?: string; cinematographer?: string; } interface SliderProps { items: Item[]; } const SliderSwiper: React.FC<SliderProps> = ({ items }) => { const [currentIndex, setCurrentIndex] = useState(0); const [selectedBrand, setSelectedBrand] = useState<string | null>("Recent"); const brands = useMemo(() => { const uniqueBrands = Array.from( new Set( items.filter((item) => item.brand !== "all").map((item) => item.brand) ) ); return ["Recent", ...uniqueBrands]; }, [items]); const filteredItems = useMemo(() => { if (selectedBrand === "Recent") { const validDateItems = items.filter((item) => item.date); return validDateItems .sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()) .slice(0, 9); } else { return items.filter((item) => item.brand === selectedBrand); } }, [items, selectedBrand]); const itemTitlesRef = useRef<NodeListOf<Element> | null>(null); const itemImageRef = useRef<HTMLImageElement | null>(null); const buttonPrevRef = useRef<HTMLButtonElement | null>(null); const buttonNextRef = useRef<HTMLButtonElement | null>(null); const brandFilterButtonsRef = useRef<NodeListOf<Element> | null>(null); useEffect(() => { // Store refs to elements itemTitlesRef.current = document.querySelectorAll(".item-titles"); itemImageRef.current = document.querySelector(".item-image"); buttonPrevRef.current = document.querySelector(".button-prev"); buttonNextRef.current = document.querySelector(".button-next"); brandFilterButtonsRef.current = document.querySelectorAll( ".brand-filter-buttons" ); }, []); const handleFilterChange = (brand: string | null): void => { gsap.to( [ itemTitlesRef.current, itemImageRef.current, buttonPrevRef.current, buttonNextRef.current, brandFilterButtonsRef.current, ], { opacity: 0, duration: 0.3, ease: "power3.out", onComplete: () => { setSelectedBrand(brand); setCurrentIndex(0); animateIn(); }, } ); }; const animateIn = () => { const timeline = gsap.timeline({ defaults: { ease: "power3.out" } }); timeline.fromTo( itemTitlesRef.current, { opacity: 0, y: 20 }, { opacity: 1, y: 0, duration: 1 }, "start" ); timeline.fromTo( itemImageRef.current, { opacity: 0, y: 20 }, { opacity: 1, y: 0, duration: 1 }, "start+=0.2" ); timeline.fromTo( buttonPrevRef.current, { opacity: 0, x: -20 }, { opacity: 1, x: 0, duration: 0.3, stagger: 0.1 }, "start+=0.5" ); timeline.fromTo( buttonNextRef.current, { opacity: 0, x: -20 }, { opacity: 1, x: 0, duration: 0.3, stagger: 0.1 }, "start+=0.5" ); timeline.fromTo( brandFilterButtonsRef.current, { opacity: 0, y: 20 }, { opacity: 1, y: 0, duration: 0.5 }, "start+=0.5" ); }; const handleNextItem = () => { gsap.to( [ itemTitlesRef.current, itemImageRef.current, buttonPrevRef.current, buttonNextRef.current, ], { opacity: 0, duration: 0.3, ease: "power3.out", onComplete: () => { setCurrentIndex( (prevIndex) => (prevIndex + 1) % filteredItems.length ); animateIn(); }, } ); }; const handlePrevItem = () => { gsap.to( [ itemImageRef.current, itemTitlesRef.current, buttonPrevRef.current, buttonNextRef.current, ], { opacity: 0, duration: 0.3, ease: "power3.out", onComplete: () => { setCurrentIndex( (prevIndex) => (prevIndex - 1 + filteredItems.length) % filteredItems.length ); animateIn(); }, } ); }; return ( <div className="item-background-container"> {filteredItems.length > 0 && ( <section className="item-titles"> <div className="brand-title">{filteredItems[currentIndex].brand}</div> <div className="item-title">{filteredItems[currentIndex].title}</div> </section> )} <div className="brand-filter-sidebar"> {brands.map((brand) => ( <button key={brand} className={`brand-filter-buttons ${ selectedBrand === brand ? "active" : "" }`} onClick={() => handleFilterChange(brand)} > {brand} </button> ))} </div> <div className="item-image-container"> {filteredItems.length > 0 && ( <img className="item-image" src={filteredItems[currentIndex].img} alt={filteredItems[currentIndex].title} /> )} </div> <div className="nextprev-button-wrapper"> <button type="button" className="button-prev" onClick={handlePrevItem}> <img src="/arrows/left-chevron-svgrepo-com.svg" alt="Previous" /> </button> <button type="button" className="button-next" onClick={handleNextItem}> <img src="/arrows/right-chevron-svgrepo-com.svg" alt="Next" /> </button> </div> </div> ); }; export default SliderSwiper;``` I'd like to prevent the flashing that sometimes happens when animations fire. Link to comment Share on other sites More sharing options...
GSAP Helper Posted November 28, 2023 Share Posted November 28, 2023 Hm, your CodeSandbox looks like it's private. Quote Unable to access this workspace Here are some React Starter Templates and please read this article. If you still need help, please make sure you provide a minimal demo that clearly illustrates the problem (and is accessible for us to view). Link to comment Share on other sites More sharing options...
Taun Posted November 28, 2023 Author Share Posted November 28, 2023 Thanks, I'll study those examples and read through the docs again tomorrow. Link to comment Share on other sites More sharing options...
Taun Posted November 29, 2023 Author Share Posted November 29, 2023 I've gone through the examples and read the docs. My confusion lies in the wrapping of animations with useLayoutEffect. The GSAP examples don't express the complexity of multiple React animations called inside and outside of useEffect. I think this may be more of a React question, but if you can help that'll be great. So I can't call the animateIn() function because it's defined inside of the useLayout. If I wrap all animations inside useLayout hooks, I can't call them outside of that hook, so I can't call it onClick that's defined in the return statement? Do I then have multiple gsap.context functions within each useLayout? What do you suggest? Link to comment Share on other sites More sharing options...
Toso Posted November 29, 2023 Share Posted November 29, 2023 I guess the docs cover most of the use cases there are 2 pages the react page and advanced techniques for react make sure you go through both also maybe try useGSAP() hook its new and not released yet but give it a try https://github.com/greensock/react after that i think its all on react and how you pass the animations and stuff 1 Link to comment Share on other sites More sharing options...
Taun Posted November 29, 2023 Author Share Posted November 29, 2023 Cool, thanks. It seems to be working now. I just needed some fresh air to notice the obvious. 2 Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now