Jump to content
Search Community

Taun

Members
  • Posts

    9
  • Joined

  • Last visited

Posts posted by Taun

  1. 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?

  2. 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.
     
  3. Hi. I don't understand why my code isn't rendering? When I got it to render in VSCode, it rendered the images vertically in a column, not horizontally.
    I thought flex-direction:row would make the slider display in a row?

    Code Sandbox: 
    https://codesandbox.io/s/gsap-horizontalscrolltrigger-mz7d68?file=/src/components/scrollsection.scss:47-167

    .scroll-section-inner {
    height: 100vh;
    width: 100vw;
    display: flex;
    flex-direction: row;
    position: relative;
    }
  4. Hi. I'd like to create a portfolio website wherein there's a fixed sidebar with the portfolio titles and alongside that, centered to the page, is a Hero component that renders containers with thumbnail images. When the user clicks on a portfolio title it should scroll down to the thumbnail.
     

    In the test Codesandbox project, the button displays and works, but there's no styling for the containers? Do you know how I can show their colors? What CSS approach do you recommend to display the nav of portfolio items on the left side of the page? CSS grid?

    I know this a CSS query, though once I'm passed this stage I'm sure I'll have related GSAP questions.

    CodeSandbox: https://codesandbox.io/s/gsap-scrolltosection-mz7d68?file=/src/index.js

    See the Pen index.js by s (@s) on CodePen

×
×
  • Create New...