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.