Jump to content
Search Community

jnemec

Members
  • Posts

    14
  • Joined

  • Last visited

Posts posted by jnemec

  1. I've done another project using GSAP in Next.js, but I had zero issues with importing. I started a new project and now I'm getting "SyntaxError: Cannot use import statement outside a module". I've tried importing using gsap/dist/ScrollTrigger and it removes the error but then ScrollTrigger doesn't work at all. I'm certain I set up my other project the same way, so I'm really confused. 

     

    My content is in the "components" folder.

     

    https://codesandbox.io/p/sandbox/staging-resonance-szbojy?file=%2Fcomponents%2FBody.tsx&selection=[{"endColumn"%3A23%2C"endLineNumber"%3A18%2C"startColumn"%3A23%2C"startLineNumber"%3A18}]

  2. 8 hours ago, GreenSock said:

    You were using a single timeline and cramming everything into that, so that timeline would just keep getting longer and longer and longer, never allowing the finished animations to get garbage collected. So there were various logic problems in your code. I think you could simplify it quite a bit: 

    https://codesandbox.io/p/sandbox/gsap-columns-forked-lhro3k?file=%2Fcomponents%2FHero.tsx

     

    Is that more like what you wanted? 

    In my original copy, I had the timeline as a ref. Is there any benefit of making it a ref? I've just seen some people doing that 

  3. 8 hours ago, GreenSock said:

    You were using a single timeline and cramming everything into that, so that timeline would just keep getting longer and longer and longer, never allowing the finished animations to get garbage collected. So there were various logic problems in your code. I think you could simplify it quite a bit: 

    https://codesandbox.io/p/sandbox/gsap-columns-forked-lhro3k?file=%2Fcomponents%2FHero.tsx

     

    Is that more like what you wanted? 

    That makes sense! Yes, that is exactly what I was wanting. THANK YOU!

  4. I have three boxes with text in them and I want the animation to be played on the box I click on. I have it almost how I want it except after I select a box and then click it again to reverse it; the next time I click on another box, the first box I clicked on animates and then the other box animates after. Anyone have any idea what is causing this? 

     

    (Select the "components" folder, then click on the "Hero.tsx" file to see the code)

     

    https://codesandbox.io/p/sandbox/gsap-columns-rgi7s2?file=%2Fcomponents%2FHero.tsx&selection=[{"endColumn"%3A17%2C"endLineNumber"%3A52%2C"startColumn"%3A17%2C"startLineNumber"%3A52}]

  5. On 3/14/2023 at 9:17 AM, Rodrigo said:

    Hi,

     

    As Cassie mentions this is mostly a scope issue. Right now you have this:

    {columns?.map((column, i) => (
      <div
        key={i}
        className={`column ${styles.center}`}
        onMouseEnter={() => handleMouseEnter(i)}
        onMouseLeave={() => handleMouseLeave(i)}
        style={{ backgroundColor: column.backgroundColor }}
      >
        <div className={styles.content}>
          <h2 className={styles.label}>{column?.label}</h2>
          <p className={`body-text ${styles.body}`} ref={bodyRef}>// <- This Ref
            {column?.body}
          </p>
        </div>
      </div>
    ))}

    That particular ref is just a single element, so everytime the loop runs it gets replaced with the current iteration. Logically this loop will end with the last element, that's why you're seeing this result which is totally expected. Unfortunately unlinke Vue, React doesn't create an array of refs when looping through a collection, so you have to take care of that manually.

     

    There are two ways to solve this. One is to use a callback ref:

    https://reactjs.org/docs/refs-and-the-dom.html#callback-refs

     

    The other is to use GSAP Context's scoping superpowers and enjoy life :D:

    const Hero = () => {
      const ctx = useRef(null);
      const columnRef = useRef(null);
      
      const handleMouseEnter = (i:Number) => {
        ctx.current.mouseEnterHandler(i);
      };
      const handleMouseLeave = (i:Number) => {
        ctx.current.mouseLeaveHandler(i);
      };
    
      useLayoutEffect(() => {
        ctx.current = gsap.context((self) => {
          gsap.from(columnRef.current.children, {
            opacity: 0,
            x: 200,
            stagger: 0.3,
          });
    
          const columns = gsap.utils.toArray(".column");
          const texts = columns.map((column: HTMLDivElement) =>
            column.querySelector(".body-text")
          );
          gsap.set(texts, {
            opacity: 0,
            y: 200,
          });
          self.add("mouseEnterHandler", (i) => {
            gsap.to(texts[i], {
              opacity: 1,
              y: 0,
              duration: 1,
            });
          });
          self.add("mouseLeaveHandler", (i) => {
            gsap.to(texts[i], {
              opacity: 0,
              y: 200,
              duration: 1,
            });
          });
        }, columnRef);
        return () => ctx.current.revert();
      }, []);
    
      return (
        <section className={styles.section}>
          <div className={styles.columns} ref={columnRef}>
            {columns?.map((column, i) => (
              <div
                key={i}
                className={`column ${styles.center}`}
                onMouseEnter={() => handleMouseEnter(i)}
                onMouseLeave={() => handleMouseLeave(i)}
                style={{ backgroundColor: column.backgroundColor }}
              >
                <div className={styles.content}>
                  <h2 className={styles.label}>{column?.label}</h2>
                  <p className={`body-text ${styles.body}`}>
                    {column?.body}
                  </p>
                </div>
              </div>
            ))}
          </div>
        </section>
      );
    };

    I forked your example so you can see it in action:

    https://codesandbox.io/p/sandbox/gsap-columns-forked-tl8bec?file=%2Fcomponents%2FHero.tsx

     

    Finally we'd like to know specifically what are the biggest pain points when integrating GSAP in your NextJS projects. Anything in the Docs and/or official React guides that seems confusing or hard to follow/understand. We thrive in improving the resources we have available for our users in order to ease GSAP integration in their projects.

     

    Happy Tweening!

    I'm still working on this, but you have been a lot of help.I was wondering why you created a ctx ref? I haven't seen that done before and just want to be able to understand what's going on. Thanks!

  6. Quote

    I've been getting the hang of using GSAP with Next.js, but I had a question about getting a class name by scoping. Is there a way I can select the "styles.content" class without having to add in a regular css class ("content") to be able to select it?

    import React, { useRef, useLayoutEffect } from 'react'
    
    import { SanityImageDiv } from '../../../../layout/shared'
    
    import { heroAnimate } from '../animate/animate'
    import { gsap } from 'gsap'
    
    import styles from './hero.module.scss'
    
    const Hero = ({
      body,
      buttons,
      heading,
      media = [],
      sectionID = { current: '' },
    }) => {
      const sectionRef = useRef(null)
    
      // GSAP
      useLayoutEffect(() => {
        const ctx = gsap.context(() => {
          const sectionContent = document.querySelector('.content')
          heroAnimate(sectionContent.children)
        })
        return () => ctx.revert()
      }, [])
    
      return (
        <section id={sectionID.current} className={styles.section} ref={sectionRef}>
          {media.map((item) =>
            item?._type === 'image' ? (
              <SanityImageDiv
                key={item._key}
                className={styles.image}
                image={item.asset}
                alt={item.alt}
              >
                <div className={`content ${styles.content}`}>
                  <h2 className={styles.heading}>{heading}</h2>
                  <p className={styles.body}>{body}</p>
                  <div className={styles.buttons}>
                    {buttons?.map((button) => (
                      <a
                        key={button._key}
                        href={`#${button?.sectionID}`}
                        className={styles.button}
                      >
                        {button?.label}
                      </a>
                    ))}
                  </div>
                </div>
              </SanityImageDiv>
            ) : (
              <div key={item._key}>Unsupported file type</div>
            )
          )}
        </section>
      )
    }
    
    export default Hero

     

  7. 9 minutes ago, Rodrigo said:

    Hi,

     

    I think we're talking about different things when we're referring to overflowing here, this is what I'm seeing:

    https://i.imgur.com/ECZSKG2.mp4

     

    What exactly is the issue in this particular case?

     

    That is exactly how I expect this to behave, so clearly I'm missing something here 🤷‍♂️

     

    Happy Tweening!

    That is my fault for not being clearer. I have added a media query to my sandbox. At <768px the columns will stack on top of each other to create three rows. I'm wanting the parent elements to expand with the text that slides up. In my current sandbox, the third row overflows and covers the other two rows. How can I make it so that the rows parent expands to match the amount of text? 

    https://codesandbox.io/p/sandbox/gsap-columns-forked-c87wb4?file=%2Fcomponents%2FHero.module.scss&selection=[{"endColumn"%3A19%2C"endLineNumber"%3A40%2C"startColumn"%3A19%2C"startLineNumber"%3A40}]

  8. 19 hours ago, Rodrigo said:

    Hi,

     

    This is more related with CSS than anything else, these styles seems to solve that:

    .body {
      position: absolute;
      margin-bottom: 0;
      line-height: 1.7;
      font-weight: 600;
      font-size: clamp(1rem, 0.15vw + 0.97rem, 1.13rem);
      color: #fff;
      opacity: 0;
      width: 100%;
      left: 0;
      padding: 5vw 3.5vw;
      overflow-wrap: break-word;
    }

     

    Hopefully this helps.

    Happy Tweening!

    I appreciate the help, but it's still overflowing for me.

    https://codesandbox.io/p/sandbox/gsap-columns-forked-c87wb4?file=%2Fcomponents%2FHero.module.scss&selection=[{"endColumn"%3A19%2C"endLineNumber"%3A40%2C"startColumn"%3A19%2C"startLineNumber"%3A40}]

  9. 1 hour ago, Cassie said:

    Heya!

     

    You're in JS-land with GSAP so you can use any JS method to get height or width or any other value and just plug that right into your tween.

     

    e.g. offsetheight
    https://www.w3schools.com/jsref/prop_element_offsetheight.asp

    I've also removed overflow on your container so you can see the text.


    https://codesandbox.io/p/sandbox/gsap-columns-forked-gnp5z5?file=%2Fcomponents%2FHero.tsx&selection=[{"endColumn"%3A16%2C"endLineNumber"%3A65%2C"startColumn"%3A16%2C"startLineNumber"%3A65}]

    Hope this helps!

    Yes, that works great! Thank you! One more questions. Would I need to add an animation to the entire column so that it grows to fit the text? I added more text to the last column and it is overflowing into the second column instead of growing to fit. Any ideas?

     

    https://codesandbox.io/p/sandbox/gsap-columns-forked-c87wb4?file=%2Fcomponents%2FHero.tsx&selection=[{"endColumn"%3A7%2C"endLineNumber"%3A16%2C"startColumn"%3A7%2C"startLineNumber"%3A16}]

  10. I was helped the other day, but I have run into another problem. In my link below, when you hover over a column, I have the p tag text sliding up, but if the text is too long, then it gets cut off. How would I make it so that I can use the height of the each columns text as the distance to slide up, so that it all fits in the column?

     

    Click in the "components" folder and select the "Hero.tsx" file to view what I have done so far.

    https://codesandbox.io/p/sandbox/gsap-columns-rgi7s2?file=%2Fcomponents%2FHero.tsx

  11. 56 minutes ago, Rodrigo said:

    Hi,

     

    As Cassie mentions this is mostly a scope issue. Right now you have this:

    {columns?.map((column, i) => (
      <div
        key={i}
        className={`column ${styles.center}`}
        onMouseEnter={() => handleMouseEnter(i)}
        onMouseLeave={() => handleMouseLeave(i)}
        style={{ backgroundColor: column.backgroundColor }}
      >
        <div className={styles.content}>
          <h2 className={styles.label}>{column?.label}</h2>
          <p className={`body-text ${styles.body}`} ref={bodyRef}>// <- This Ref
            {column?.body}
          </p>
        </div>
      </div>
    ))}

    That particular ref is just a single element, so everytime the loop runs it gets replaced with the current iteration. Logically this loop will end with the last element, that's why you're seeing this result which is totally expected. Unfortunately unlinke Vue, React doesn't create an array of refs when looping through a collection, so you have to take care of that manually.

     

    There are two ways to solve this. One is to use a callback ref:

    https://reactjs.org/docs/refs-and-the-dom.html#callback-refs

     

    The other is to use GSAP Context's scoping superpowers and enjoy life :D:

    const Hero = () => {
      const ctx = useRef(null);
      const columnRef = useRef(null);
      
      const handleMouseEnter = (i:Number) => {
        ctx.current.mouseEnterHandler(i);
      };
      const handleMouseLeave = (i:Number) => {
        ctx.current.mouseLeaveHandler(i);
      };
    
      useLayoutEffect(() => {
        ctx.current = gsap.context((self) => {
          gsap.from(columnRef.current.children, {
            opacity: 0,
            x: 200,
            stagger: 0.3,
          });
    
          const columns = gsap.utils.toArray(".column");
          const texts = columns.map((column: HTMLDivElement) =>
            column.querySelector(".body-text")
          );
          gsap.set(texts, {
            opacity: 0,
            y: 200,
          });
          self.add("mouseEnterHandler", (i) => {
            gsap.to(texts[i], {
              opacity: 1,
              y: 0,
              duration: 1,
            });
          });
          self.add("mouseLeaveHandler", (i) => {
            gsap.to(texts[i], {
              opacity: 0,
              y: 200,
              duration: 1,
            });
          });
        }, columnRef);
        return () => ctx.current.revert();
      }, []);
    
      return (
        <section className={styles.section}>
          <div className={styles.columns} ref={columnRef}>
            {columns?.map((column, i) => (
              <div
                key={i}
                className={`column ${styles.center}`}
                onMouseEnter={() => handleMouseEnter(i)}
                onMouseLeave={() => handleMouseLeave(i)}
                style={{ backgroundColor: column.backgroundColor }}
              >
                <div className={styles.content}>
                  <h2 className={styles.label}>{column?.label}</h2>
                  <p className={`body-text ${styles.body}`}>
                    {column?.body}
                  </p>
                </div>
              </div>
            ))}
          </div>
        </section>
      );
    };

    I forked your example so you can see it in action:

    https://codesandbox.io/p/sandbox/gsap-columns-forked-tl8bec?file=%2Fcomponents%2FHero.tsx

     

    Finally we'd like to know specifically what are the biggest pain points when integrating GSAP in your NextJS projects. Anything in the Docs and/or official React guides that seems confusing or hard to follow/understand. We thrive in improving the resources we have available for our users in order to ease GSAP integration in their projects.

     

    Happy Tweening!

    This worked great! Thank you so much. Personally they only thing I struggled with was trying to implement GSAP while mapping over an array of objects. I couldn't really find much information about that. I also think it would be awesome if there were more videos of using GSAP with React etc. 

    • Like 1
  12. 1 hour ago, Cassie said:

    I can't find the right file in that sandbox at a glance, That's quite a large project structure.

    But this sounds like a scoping issue, you're likely always targeting the last container
     

     

    THANK YOU! So the file is in the "components" folder and then the "Hero.tsx" file. What you sent is exactly what I am wanting. I'll take a look at it and hopefully I can get it working. Struggling a little with integrating GSAP in next.js a little bit 

    • Like 1
  13. Hello, I'm trying to place an animation (slide from right) on each column in a component (3 columns that animate individually) using Next.js. I'm mapping over dynamic data from a CMS, and I have gotten it to where it works how I would like using gsap.timeline(), except this component is more towards the middle of my site, so it's running when the page first loads. I can't get ScrollTrigger to work with my array of refs. Once I add scrolltrigger, it makes the animation happen on the columns at the same time. How can I make it so that these animations won't happen until scrolled to? I apologize for the limited amount of code. This is for a site at my job and I have been tasked with learning GSAP. 

     

    const tl = gsap.timeline()
    const columnRefs = useRef([])
    columnRefs.current = []
     
    useLayoutEffect(() => {
    const ctx = gsap.context(() => {
    columnRefs.current.forEach((e) => {
    tl.fromTo(
    e,
    { opacity: 0, x: 200 },
    {
    duration: 2,
    opacity: 1,
    x: 0,
    ease: 'none',
    stagger: 0.2,
    }
    )
    })
    })
    return () => ctx.revert()
    }, [])
     
    const addToRefs = (e) => {
    if (e && !columnRefs.current.includes(e)) {
    columnRefs.current.push(e)
    }
    }
     
    return (
    <section id={sectionID.current} className={styles.section}>
    <div className={styles.columns}>
    {columns?.map((column) => (
    <div
    key={column._key}
    className={styles.center}
    onMouseOver={() => handleHover(column._key)}
    onFocus={() => handleHover(column._key)}
    onMouseOut={() => handleHover([])}
    onBlur={() => handleHover([])}
    ref={addToRefs}
    >
    <SanityImageDiv
    className={styles.column}
    image={column?.image?.asset}
    >
    <div className={styles.content}>
    <h2 className={styles.label}>{column?.label}</h2>
    <p
    className={
    styles.body +
    `${current === column._key ? ` ${styles.show}` : ''}`
    }
    >
    {column?.body}
    </p>
    </div>
    </SanityImageDiv>
    <div
    className={
    styles.gradient +
    `${current === column._key ? ` ${styles.show}` : ''}`
    }
    ></div>
    </div>
    ))}
    </div>
    </section>
×
×
  • Create New...