Jump to content

Pollux Septimus

  • Posts

  • Joined

  • Last visited

Recent Profile Visitors

The recent visitors block is disabled and is not being shown to other users.

Pollux Septimus's Achievements

  1. I believe I have found a viable solution. I have implemented a short delay so that everything is invisible during the refresh process. Additionally, I have modified the animation based on this delay. As for the dependency passed as a constant in the array, it is actually a custom hook that returns a boolean value depending on whether the user is on a mobile device or not. I didn't want to copy the entire hook, so I just set it to a random string to avoid any errors. Thank you all very much for your help.
  2. @Rodrigo Hello, Thank you for your time. I am aware that the code is quite a lot, I was hoping this is something common with React and it has a simple solution. Regarding the useLayoutEffect, I had more issues using it than not. I will try to find a solution myself and If I will I'll let you know.
  3. This is all I could do. For some reason, the styling does not work, but at least the animations work and also the issue is visible. Hope it helps.
  4. useEffect(() => { ScrollTrigger.refresh(); }, [activeTab, isMobile]); Forgot to also show the code.
  5. Thank you! I manage to fix the issue by refreshing the scrollTrigger whenever the tabs are changed. Unfortunately, this creates another issue where the tabs animation runs twice. The first time runs because it's supposed to and the second time because of the refresh. I hope it's clear in the video. Untitled (2).mp4
  6. @GSAP Helper Hello, The templates were very nice and I manage to build this minimal demo. It behaves exactly like it does in my project. @Cassie Hello Cassie, I have added ScrollTrigger.refresh() to the onComplete callback function of the last tween in the animation sequence, on the first component. There is a very high chance that I haven't done it right. I also apologize for the improper grammar. I meant to say that if a component's height changes through any means, and there is a different component below it that uses ScrollTrigger, the start indicator that is visible with the markers: true option does not update to reflect the new position of the component. With the minimal demo that I've linked above it should be clear. Thank you for your answer
  7. Hello, I am using ScrollTrigger in React and I've noticed that the start position doesn't update when a component before it changes height. I've tried refreshing the ScrollTrigger on Complete, but since the different sections are in different components, this doesn't work for me. Sorry for the lack of a minimal demo but the animations are a bit complex for me to be able to quickly put a minimal demo together. Component that changes size import { useState, useEffect, useRef } from 'react'; import gsap from 'gsap'; import { ScrollTrigger } from 'gsap/all'; import useIsMobile from 'hooks/useIsMobile'; import styles from './styles.module.css'; const Tabs = ({ config }) => { const [activeTab, setActiveTab] = useState(0); const [tabsHeight, setTabsHeight] = useState(); const isMobile = useIsMobile(501); const isTablet = useIsMobile(1200); const tl = useRef(); const tabsContainerRef = useRef(); const tabsBG = useRef(); useEffect(() => { setTabsHeight(tabsBG.current.getBoundingClientRect().height); }, [isMobile]); useEffect(() => { gsap.registerPlugin(ScrollTrigger); const context = gsap.context(() => { tl.current = gsap.timeline({ scrollTrigger: { trigger: tabsContainerRef.current, start: 'top bottom-=50', end: 'bottom', toggleActions: 'restart none none reverse', }, }); tl.current .from(tabsBG.current, { width: 45, height: 45, duration: 0.5, }) .from('#tabsID', { opacity: 0, }); }); return () => context.revert(); }, [isMobile, isTablet]); return ( <div ref={tabsContainerRef} className={styles.tabsContainer}> <div className={styles.tabsNav}> <div ref={tabsBG} className={styles.tabsBG}> {config.map((item, index) => ( <div key={index} id='tabsID' className={`${styles.tab} ${ activeTab === index && styles.tabActive }`} onClick={() => setActiveTab(index)} > {item.tab} </div> ))} </div> </div> <div>{config[activeTab]?.content}</div> </div> ); }; export default Tabs; The Component below: import { useState, useEffect, useRef } from 'react'; import AnimatedImage from 'components/AnimatedImage'; import gsap from 'gsap'; import { Link } from 'react-router-dom'; import useIsMobile from 'hooks/useIsMobile'; import styles from './styles.module.css'; const ProjectsSectionDesktop = ({ projectsData }) => { const [hover, setHover] = useState(false); const [animLength, setAnimLength] = useState(false); const [height, setHeight] = useState(); const projectsContainerRef = useRef(); const fadeInTl = useRef(); const scrollTl = useRef(); const trackRef = useRef(); const titleRef = useRef(); const pinContainerRef = useRef(); const isMobile = useIsMobile(1200); useEffect(() => { setAnimLength(trackRef.current.offsetWidth); console.log(height); const context = gsap.context(() => { const projects = gsap.utils.toArray('#projectsContainer'); gsap.set(projects, { xPercent: 50, opacity: 0, }); fadeInTl.current = gsap.timeline({ scrollTrigger: { trigger: pinContainerRef.current, start: 'top bottom', preventOverlaps: true, markers: true, }, }); fadeInTl.current .to(titleRef.current, { opacity: 0.1, duration: 2, ease: 'power1.out', }) .to( projects, { xPercent: 0, opacity: 1, duration: 1, stagger: 0.25, ease: 'power2.out', }, '<' ); }); return () => context.revert(); }, [projectsData, isMobile]); useEffect(() => { setHeight(projectsContainerRef.current.getBoundingClientRect().top); const scrollContext = gsap.context(() => { scrollTl.current = gsap.timeline({ scrollTrigger: { trigger: projectsContainerRef.current, start: 'top top', end: `+=${animLength}`, pin: true, scrub: 0.5, preventOverlaps: true, }, }); scrollTl.current .to(trackRef.current, { xPercent: -100, ease: 'none', }) .to( titleRef.current, { xPercent: -50, ease: 'none', }, '<' ); }); return () => scrollContext.revert(); }, [projectsData, animLength, isMobile, height]); return ( <div ref={pinContainerRef} style={{ border: '1px solid red' }}> <div ref={projectsContainerRef} className={styles.projectsContainer}> <h1 ref={titleRef} className={styles.title}> Projects </h1> <div ref={trackRef} className={styles.projectsTrack}> {projectsData?.map((project, index) => ( <div id='projectsContainer' key={index} className={styles.projectContainer} > <Link to={`/projects/${project.id}`} className={styles.link}> <div id='projectImageContainer' className={styles.projectImageContainer} onMouseEnter={() => setHover(true)} onMouseLeave={() => setHover(false)} > <AnimatedImage src={project.image} playAnim={hover} /> </div> </Link> </div> ))} </div> </div> </div> ); }; export default ProjectsSectionDesktop;
  8. Hello, I need some help from someone that knows and understand more about gsap scrollperProxy than I do because I am stuck with a problem that I can't seem to get around it. And honestly, I don't know if this is a gsap related question, and if it isn't I apologize in advance. In project I am using React Router and React Router keeps the same offsetHeight position when changing routes and I would like when the route has changed to have a scroll that scrolls to the Top. The issue is that I am using locomotive-scroll in combination with gsap's scrollTrigger. In order for the scrollTrigger to work with locomotive-scroll I have created a custom hook (which I will leave the code at the end because it's quite long), where I essentially tell scrollTrigger what the current locomotive-scroll, scroll position is using scrollerProxy. Unfortunately, if I try to scroll to the top of the page, for example, I can't. And the console doesn't even throw an error. I tried both using gsap scrollToPlugin and locomotive-scroll's scrollTo function but they only work If I don't use the hook that I've created. using gsap: useLocoScroll(true); const { pathname } = useLocation(); useEffect(() => { gsap.registerPlugin(ScrollToPlugin); gsap.to('.App', { duration: 0, scrollTo: 0, delay: 0, ease: 'none' }); }, [pathname]); using loco scroll: useLocoScroll(true); const { pathname } = useLocation(); const scrollRef = useRef(null); useEffect(() => { const scroll = new LocomotiveScroll(); scrollRef.current = scroll; if (scrollRef.current) { scrollRef.current.scrollTo('top', { offset: 0, duration: 600, easing: [0.25, 0.0, 0.35, 1.0], disableLerp: true, callback: () => { scrollRef.current.on('scroll', ScrollTrigger.update); }, }); } }, [pathname]); (Both of the options above work if useLocoScroll is set to false / `useLocoScroll(false)`) Locomotive-scroll scrollTo function also takes in an object with a callback where I tried to update the ScrollTrigger in various ways but nothing worked. So I was wondering if there is a way I could update the locomotive-scroll base on where the ScrollToPlugin is. Basically, doing the reverse of what I did in the custom hook? Here is the code for the custom hook: import { useLayoutEffect } from 'react'; import LocomotiveScroll from 'locomotive-scroll'; import gsap from 'gsap'; import ScrollTrigger from 'gsap/ScrollTrigger'; const useLocoScroll = (start) => { gsap.registerPlugin(ScrollTrigger); useLayoutEffect(() => { if (!start) return; const scrollEl = document.querySelector('.App'); let locoScroll = new LocomotiveScroll({ el: scrollEl, smoothMobile: false, smooth: true, multiplier: 1, }); locoScroll.on('scroll', ScrollTrigger.update); ScrollTrigger.scrollerProxy(scrollEl, { scrollTop(value) { if (locoScroll) { return arguments.length ? locoScroll.scrollTo(value, 0, 0) : locoScroll.scroll.instance.scroll.y; } return null; }, scrollLeft(value) { if (locoScroll) { return arguments.length ? locoScroll.scrollTo(value, 0, 0) : locoScroll.scroll.instance.scroll.x; } return null; }, getBoundingClientRect() { return { top: 0, left: 0, width: window.innerWidth, height: window.innerHeight, }; }, pinType: document.querySelector('.App').style.transform ? 'transform' : 'fixed', }); const locoScrollUpdate = () => { if (locoScroll) { locoScroll.update(); } }; new ResizeObserver(() => { if (locoScroll) { locoScroll.update(); } }).observe(document.querySelector('[data-scroll-container]')); ScrollTrigger.addEventListener('refresh', locoScrollUpdate); ScrollTrigger.refresh(); return () => { if (locoScroll) { ScrollTrigger.removeEventListener('refresh', locoScrollUpdate); locoScroll.destroy(); locoScroll = null; } }; }, [start]); }; export default useLocoScroll;
  9. Awesome! Thank you! Unfortunately, it had some issues. One of which was that the animation in reverse would play instantly.
  10. It did help! The question was about the `utils.toArray` method in gsap, specifically whether it might cause any problems with react's rerendering process. However, I resolved the issue by using `querySelectorAll` on the parent ref and initializing an empty array state when the component is rendered and adding that state to the dependency array of the useEffect where I keep all the gsap animation. This way the `links` are 100% rendered when gsap "does stuff to them" Although, There is still an issue where I get a flesh of the navbar before `gsap.set` sets all the initial stylings. Thank you for your answer!
  11. This is awesome! I will take a look. Thank you!
  12. I am working on this animation for a navbar where I have a list of links that I would like to fade in from `opacity 0 and y - some px` to `opacity 1 and y 0` in a staggered way. The issue is that when I refresh the page neither the `gsap.set` nor the animation from the TL seems to work properly. The only time they work as expected is when I do changes to TL and save. For example, I change the `duration` from 0.3 to 0.2 and save it. I am not sure what is the issue but I think it has something to do with the `toArray` method and the way react rerenders component. Is this something common with react? Is there a fix for it? Here is a code sandbox: https://codesandbox.io/s/compassionate-microservice-7u6vv3?file=/src/App.js
  13. @GreenSock Hi, Sorry for the delayed response, I was busy with work. Here is the minimal demo you asked for: https://codesandbox.io/s/compassionate-microservice-7u6vv3?file=/src/App.js. I was curious if I could replace the background with an svg and animate its path so that when it changes position from y-100 to y0 but the bottom of the "square" warps downwards a bit. I guess @Carl answered my question with some pretty good examples. Thank you very much!