Jump to content
GreenSock

Search In
  • More options...
Find results that contain...
Find results in...

Search the Community

Showing results for tags 'react'.

  • Search By Tags

    Type tags separated by commas.
  • Search By Author

Content Type


Forums

  • GreenSock Forums
    • GSAP
    • Banner Animation
    • Jobs & Freelance
  • Flash / ActionScript Archive
    • GSAP (Flash)
    • Loading (Flash)
    • TransformManager (Flash)

Product Groups

  • Club GreenSock
  • TransformManager
  • Supercharge

Categories

There are no results to display.


Find results in...

Find results that contain...


Date Created

  • Start

    End


Last Updated

  • Start

    End


Filter by number of...

Joined

  • Start

    End


Group


Personal Website


Twitter


CodePen


Company Website


Location


Interests

  1. I've been having this weird bug/ issue in combination with ScrollTrigger and routing between different pages in Gatsby. When routing to a different page while clicking on a link the animation starts before the trigger is met. So when I scroll down and expect to see blocks animate in I see blocks already in their end state. **Note: This only happens when routing to a page. If you open a page and/or refresh this doesn't happen (the normal behaviour with ScrollTrigger happens). I've made a simple example for you guys to have a look at in CodeSandbox: https://codesandbox.io/s/gatsby-gsap-scrolltrigger-issue-q19r6?file=/src/pages/index.js
  2. Are you working with React and looking to really advance your GSAP animation skills? You're in the right place. This guide contains advanced techniques and some handy tips from expert animators in our community. This is not a tutorial, so feel free to dip in and out as you learn. Think of it as a collection of recommended techniques and best practices to use in your projects. Why GSAP? Animating with GSAP gives you unprecedented levels of control and flexibility. You can reach for GSAP to animate everything — from simple DOM transitions to SVG, three.js, canvas or WebGL — your imagination is the limit. More importantly, you can rely on us. We obsess about performance, optimizations and browser compatibility so that you can focus on the fun stuff. We've actively maintained and refined our tools for over a decade and there are no plans to stop. Lastly, if you ever get stuck, our friendly forum community is there to help. Going forward we will assume a comfortable understanding of both GSAP and React. If you're starting out we highly recommend reading our foundational article first - First Steps & Handy Techniques.. Quick Links Component Communication Passing down a timeline prop Passing down callback to build a timeline React Context Imperative Communication Creating Reusable Animations registerEffect() Exit Animations Custom Hooks useSelector useArrayRef useStateRef useIsomorphicLayoutEffect Online PlaygroundsOnline Playgrounds Get started quickly by forking one of these starter templates: CodePen CodeSandbox CodeSandbox + Bonus Plugins Component CommunicationComponent Communication In the last article, we covered creating our first animation, and how to create and control timelines within a React component. But there are times where you may need to share a timeline across multiple components or construct animations from elements that exist in different components. In order to achieve this, we need a way to communicate between our components. There are 2 basic approaches to this. a parent component can send down props, e.g. a timeline a parent component can pass down a callback for the child to call, which could add animations to a timeline. Passing down a timeline propPassing down a timeline prop Note that we are using useState instead of useRef with the timeline. This is to ensure the timeline will be available when the child renders for the first time. function Box({ children, timeline, index }) { const el = useRef(); // add 'left 100px' animation to timeline useEffect(() => { timeline.to(el.current, { x: -100 }, index * 0.1); }, [timeline]); return <div className="box" ref={el}>{children}</div>; } function Circle({ children, timeline, index, rotation }) { const el = useRef(); // add 'right 100px, rotate 360deg' animation to timeline useEffect(() => { timeline.to(el.current, { rotate: rotation, x: 100 }, index * 0.1); }, [timeline, rotation]); return <div className="circle" ref={el}>{children}</div>; } function App() { const [tl, setTl] = useState(() => gsap.timeline()); return ( <div className="app"> <Box timeline={tl} index={0}>Box</Box> <Circle timeline={tl} rotation={360} index={1}>Circle</Circle> </div> ); } See the Pen React Tutorial 3a by GreenSock (@GreenSock) on CodePen. Passing down a callback to build a timelinePassing down a callback to build a timeline function Box({ children, addAnimation, index }) { const el = useRef(); // return a 'left 100px' tween useEffect(() => { const animation = gsap.to(el.current, { x: -100 }); addAnimation(animation, index); return () => animation.progress(0).kill(); }, [addAnimation, index]); return <div className="box" ref={el}>{children}</div>; } function Circle({ children, addAnimation, index, rotation }) { const el = useRef(); // return a 'right 100px, rotate 360deg' tween useEffect(() => { const animation = gsap.to(el.current, { rotate: rotation, x: 100 }); addAnimation(animation, index); return () => animation.progress(0).kill(); }, [addAnimation, index, rotation]); return <div className="circle" ref={el}>{children}</div>; } function App() { // define a timeline const [tl, setTl] = useState(() => gsap.timeline()); // pass a callback to child elements, this will add animations to the timeline const addAnimation = useCallback((animation, index) => { tl.add(animation, index * 0.1); }, [tl]); return ( <div className="app"> <Box addAnimation={addAnimation} index={0}>Box</Box> <Circle addAnimation={addAnimation} index={1} rotation="360">Circle</Circle> </div> ); } See the Pen Passing down a callback to build a timeline. by GreenSock (@GreenSock) on CodePen. React ContextReact Context Passing down props or callbacks might not be ideal for every situation. The component you're trying to communicate with may be deeply nested inside other components, or in a completely different tree. For situations like this, you can use React's Context. Whatever value your Context Provider provides will be available to any child component that uses the useContext hook. const SelectedContext = createContext(); function Box({ children, id }) { const el = useRef(); const selected = useContext(SelectedContext); useEffect(() => { gsap.to(el.current, { // animate x by 200 if the box ID matches the selected context value x: selected === id ? 200 : 0 }); }, [selected, id]); return <div className="box" ref={el}>{children}</div>; } function App() { // Any component can read the value passed to the provider, no matter how deeply nested. // In this example, we're passing "2" as the current value. return ( <SelectedContext.Provider value="2"> <Box id="1">Box 1</Box> <Box id="2">Box 2</Box> <Box id="3">Box 3</Box> </SelectedContext.Provider> ); } See the Pen React Tutorial 3c by GreenSock (@GreenSock) on CodePen. Imperative CommunicationImperative Communication Passing around props or using Context works well in most situations, but using those mechanisms cause re-renders, which could hurt performance if you're constantly changing a value, like something based on the mouse position. To bypass React’s rendering phase, we can use the useImperativeHandle hook, and create an API for our component. const Circle = forwardRef((props, ref) => { const el = useRef(); useImperativeHandle(ref, () => { // return our API return { moveTo(x, y) { gsap.to(el.current, { x, y }); } }; }, []); return <div className="circle" ref={el}></div>; }); Whatever value the imperative hook returns will be forwarded as a ref function App() { const circleRef = useRef(); useEffect(() => { // doesn't trigger a render! circleRef.current.moveTo(300, 100); }, []); return ( <div className="app"> <Circle ref={circleRef} /> </div> ); } See the Pen React Tutorial 3d by GreenSock (@GreenSock) on CodePen. Creating reusable animationsCreating reusable animations Creating reusable animations is a great way to keep your code clean while reducing your app’s file size. The simplest way to do this would be to call a function to create an animation. function fadeIn(target, vars) { return gsap.from(target, { opacity: 0, ...vars }); } function App() { const box = useRef(); useLayoutEffect(() => { const animation = fadeIn(box.current, { x: 100 }); }, []); return <div className="box" ref={box}>Hello</div>; } For a more declarative approach, you can create a component to handle the animation. function FadeIn({ children, vars }) { const el = useRef(); useLayoutEffect(() => { gsap.from(el.current.children, { opacity: 0, ...vars }); }, []); return <span ref={el}>{children}</span>; } function App() { return ( <FadeIn vars={{ x: 100 }}> <div className="box">Box</div> </FadeIn> ); } See the Pen React Reusable 1 by GreenSock (@GreenSock) on CodePen. If you want to use a React Fragment or animate a function component, you should pass in a ref for the target(s). Using gsap.effectsUsing gsap effects GSAP provides a way to create reusable animations with registerEffect() function GsapEffect({ children, targetRef, effect, vars }) { useLayoutEffect(() => { if (gsap.effects[effect]) { gsap.effects[effect](targetRef.current, vars); } }, [effect]); return <>{children}</>; } function App() { const box = useRef(); return ( <GsapEffect targetRef={box} effect="spin"> <Box ref={box}>Hello</Box> </GsapEffect> ); } See the Pen React Reusable 6 by GreenSock (@GreenSock) on CodePen. Exit animationsExit animations To animate elements that are exiting the DOM, we need to delay when React removes the element. We can do this by changing the component’s state after the animation has completed. function App() { const boxRef = useRef(); const [active, setActive] = useState(true); const remove = () => { gsap.to(boxRef.current, { opacity: 0, onComplete: () => setActive(false) }); }; return ( <div> <button onClick={remove}>Remove</button> { active ? <div ref={boxRef}>Box</div> : null } </div> ); } See the Pen React fade out 1 by GreenSock (@GreenSock) on CodePen. The same approach can be used when rendering elements from an array. function App() { const [items, setItems] = useState([ { id: 0 }, { id: 1 }, { id: 2 } ]); const removeItem = (value) => { setItems(prev => prev.filter(item => item !== value)); } const remove = (item, target) => { gsap.to(target, { opacity: 0, onComplete: () => removeItem(item) }); }; return ( <div> {items.map((item) => ( <div key={item.id} onClick={(e) => remove(item, e.currentTarget)}> Click Me </div> ))} </div> ); } See the Pen React fade out 2 by GreenSock (@GreenSock) on CodePen. However - you may have noticed the layout shift - this is typical of exit animations. The Flip plugin can be used to smooth this out. In this demo, we’re tapping into Flip’s onEnter and onLeave to define our animations. To trigger onLeave, we have to set display: none on the elements we want to animate out. See the Pen React Flip 2 by GreenSock (@GreenSock) on CodePen. Custom HooksCustom Hooks If you find yourself reusing the same logic over and over again, there’s a good chance you can extract that logic into a custom hook. Building your own Hooks lets you extract component logic into reusable functions. Let's take another look at registerEffect() with a custom hook function useGsapEffect(target, effect, vars) { const [animation, setAnimation] = useState(); useLayoutEffect(() => { setAnimation(gsap.effects[effect](target.current, vars)); }, [effect]); return animation; } function App() { const box = useRef(); const animation = useGsapEffect(box, "spin"); return <Box ref={box}>Hello</Box>; } See the Pen React Reusable 7 by GreenSock (@GreenSock) on CodePen. Here are some custom hooks we've written that we think you may find useful: useSelectoruseSelector Memoises GSAP’s selector utility. see demo on codepen function useSelector() { const ref = useRef(); const q = useMemo(() => gsap.utils.selector(ref), [ref]); return [q, ref]; } Usage: function App() { const [q, ref] = useSelector(); useEffect(() => { gsap.to(q(".box"), { x: 200 }); }, []); return ( <div ref={ref}> <div className="box">Hello</div> </div> ); } useArrayRefuseArrayRef Adds refs to an array. see demo on codepen function useArrayRef() { const refs = useRef([]); refs.current = []; return [refs, (ref) => ref && refs.current.push(ref)]; } Usage: function App() { const [refs, setRef] = useArrayRef(); useEffect(() => { gsap.to(refs.current, { x: 200 }); }, []); return ( <div> <div className="box" ref={setRef}>Box 1</div> <div className="box" ref={setRef}>Box 2</div> <div className="box" ref={setRef}>Box 3</div> </div> ); } useStateRefuseStateRef This hook helps solve the problem of accessing stale values in your callbacks. It works exactly like useState, but returns a third value, a ref with the current state. see demo on codepen function useStateRef(defaultValue) { const [state, setState] = useState(defaultValue); const ref = useRef(state); const dispatch = useCallback((value) => { ref.current = typeof value === "function" ? value(ref.current) : value; setState(ref.current); }, []); return [state, dispatch, ref]; } Usage: const [count, setCount, countRef] = useStateRef(5); const [gsapCount, setGsapCount] = useState(0); useEffect(() => { gsap.to(box.current, { x: 200, repeat: -1, onRepeat: () => setGsapCount(countRef.current) }); }, []); useIsomorphicLayoutEffectuseStateRef You might see a warning if you use server-side rendering (SSR) with useLayoutEffect. You can get around this by conditionally using useEffect during server rendering. This hook will return useLayoutEffect when the code is running in the browser, and useEffect on the server. caveat: Any "from" state that doesn't match the server-side rendered HTML/CSS content will still suffer from a flash of unstyled content while the JavaScript is being parsed, run and hydrated. read more about useLayoutEffect and server rendering see demo on codepen const useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect; Usage: function App() { const box = useRef(); useIsomorphicLayoutEffect(() => { gsap.from(box.current, { opacity: 0 }); }, []); return ( <div> <div className="box" ref={box}>Hello</div> </div> ); } If there is anything you'd like to see included in this article, or if you have any feedback, please leave a comment below so that we can smooth out the learning curve for future animators. Good luck with your React projects and happy tweening!
  3. import { observer } from 'mobx-react-lite'; import { useRef, useEffect } from 'react'; import { gsap } from 'gsap'; import Titanium from './components/Titanium_Honeycomb.js'; const HoneycombCard = (() => { const innerRef = useRef(); const animate = () => { var rows = []; for (var i = 0; i < 15; i++) { // We're doing 15 columns var idx = 1 + Math.floor(Math.random() * 24); if (rows.includes(idx)) { continue; } rows.push(idx); var tl = animate_line(idx); tl.repeatDelay(Math.random()); tl.repeat(-1); } }; function animate_line(start) { const q = gsap.utils.selector(innerRef.current); var tl = gsap.timeline(); tl.to( q('#prefix__comb' + start.toString().padStart(3, 0)), { fillOpacity: 0.0, duration: 0.0 }, 0 ); for (let i = start; i <= 456; i += 24) { var id = '#prefix__comb' + i.toString().padStart(3, 0); gsap.set(q(id), { attr: { fill: '#f802b', 'fill-opacity': 0.0 } }); tl.to( q(id), { fillOpacity: 0.5, duration: 0.1, }, '<' ); tl.to( q(id), { fillOpacity: 0.0, duration: 1.0, }, '>' ); } return tl; } useEffect(() => { if ( innerRef && innerRef.current ) { animate(); } }); return ( <Titanium ref={innerRef} className='selected-card' /> ); }); export default HoneycombCard; I am trying to animate a honeycomb image. I was able to access the path, but was not able to get the animation working. This is my code for the animation. prefix__combxxx is the honeycomb ids.
  4. I am trying to animate svg images in React. I am using object tags since I was not able to get Ids from the image using img tag, but this is affecting the caching of the images and is taking time to load. Is there any way I can use image tag and get it to animate or Is there any other way to get it working?
  5. React is a hugely popular framework choice, and as evidenced by many of the sites in our showcase - React and GSAP can be a powerful combination. However, utilizing GSAP in React requires a different way of thinking than a vanilla JS project. We've written this guide to help you get started using GSAP within a React project. This is not a tutorial, so feel free to dip in and out as you learn. Think of it as a collection of recommended techniques and best practices to use in your projects. Why GSAP? Animating with GSAP gives you unprecedented levels of control and flexibility. You can reach for GSAP to animate everything — from simple DOM transitions to SVG, three.js, canvas or WebGL — your imagination is the limit. More importantly, you can rely on us. We obsess about performance, optimizations and browser compatibility so that you can focus on the fun stuff. We've actively maintained and refined our tools for over a decade and there are no plans to stop. Lastly, if you ever get stuck, our friendly forum community is there to help. Going forward we will assume a basic understanding of GSAP and React. If you're just getting going with React, this tutorial from the React team is a great place to start. Need a GSAP refresher? Take a break and read about tweens and timelines. We’ll be here when you get back. Feeling confident? Skip straight to part 2 - GSAP + React, advanced animation techniques. Quick Links Getting set up Targeting a DOM element for animation Creating our first animation Targeting descendant elements Creating a timeline Controlling when React runs our animation with useEffect Reacting to changes in state Animating on interaction Avoiding flash of unstyled content (FOUC) Cleaning up Online PlaygroundsOnline Playgrounds Get started quickly by forking one of these starter templates: CodePen CodeSandbox CodeSandbox + Bonus Plugins Create a new React AppCreate a new React App If you prefer to work locally, Create React App provides a comfortable setup for experimenting with React and GSAP. To create a project, run: npx create-react-app gsap-app cd gsap-app npm start Once the project is set up we can install GSAP through npm, npm i gsap npm start then import it into our app. import React from "react"; import { gsap } from "gsap"; export default function App() { return ( <div className="app"> <div className="box">Hello</div> </div> ); } More detailed information about getting started with React Additional GSAP installation documentation Targeting elementsTargeting elements In order to animate using GSAP we need access to the element in the DOM. Refs provide a way for us to interact with and store references to DOM nodes in a React component. const boxRef = useRef(); return <div className="box" ref={boxRef}>Hello</div>; Read more about refs in the React docs Creating our first animationCreating our first animation GSAP updates inline style properties, so it’s important to make sure the DOM has been rendered before trying to animate anything. If we ask GSAP to animate an element that hasn’t been rendered, we’ll get this warning in the console. GSAP target not found. In order to avoid targeting a null element, we can use the useEffect hook. This hook tells React that our component needs to do something after rendering. function App() { // store a reference to the box div const boxRef = useRef(); // wait until DOM has been rendered useEffect(() => { gsap.to(boxRef.current, { rotation: "+=360" }); }); // DOM to render return <div className="box" ref={boxRef}>Hello</div>; } In this example, React will first render the box element to the DOM, then GSAP will rotate the box 360deg. See the Pen React & GSAP Starter Template by GreenSock (@GreenSock) on CodePen. Targeting descendant elementsTargeting descendant elements gsap.utils.selector() Creating a ref for each and every element we want to animate can add a lot of noise to our code. We can avoid this by making use of GSAP’s selector utility to easily select descendant elements. const el = useRef(); const q = gsap.utils.selector(el); useEffect(() => { // Target ALL descendants with the class of .box gsap.to(q(".box"), { x: 100 }); }, []); See the Pen gsap.utils.selector() by GreenSock (@GreenSock) on CodePen. Forwarding refs gsap.utils.selector() will target all descendants in the component tree. Within a component based system, you may need more granular control over the elements you're targeting. You can use ref forwarding to get access to specific nested elements. See the Pen Forwarding refs by GreenSock (@GreenSock) on CodePen. Creating and controlling timelinesCreating a timeline Up until now we've just used refs to store references to DOM elements, but they're not just for elements. Refs exists outside of the render loop - so they can be used to store any value that you would like to persist for the life of a component. If you're coming from class based components, this should be familiar to you as it’s essentially the same as using ‘this’. In order to avoid creating a new timeline on every render, it's important to create the timeline inside an effect and store it in a ref. function App() { const el = useRef(); const q = gsap.utils.selector(el); const tl = useRef(); useEffect(() => { tl.current = gsap.timeline() .to(q(".box"), { rotate: 360 }) .to(q(".circle"), { x: 100 }); }, []); return ( <div className="app" ref={el}> <Box>Box</Box> <Circle>Circle</Circle> </div> ); } This will also allow us to access the timeline in a different effect and toggle the timeline direction. See the Pen React Tutorial 2f by GreenSock (@GreenSock) on CodePen. Controlling when React runs our animation.controlling when react runs our animation By default useEffect runs both after the first render and after every update. So every time our component’s state changes, it will cause a re-render, which will run our effect again. We can control when useEffect should run by passing in an array of dependencies. To only run once after the first render, we pass in an empty array. // only runs after first render useEffect(() => { gsap.to(q(".box-1"), { rotation: "+=360" }); }, []); // runs after first render and every time `someProp` changes useEffect(() => { gsap.to(q(".box-2"), { rotation: "+=360" }); }, [someProp]); // runs after every render useEffect(() => { gsap.to(q(".box-3"), { rotation: "+=360" }); }); See the Pen React Tutorial 1b by GreenSock (@GreenSock) on CodePen. Reacting to changes in statereacting to changes in state Now that we know how to control when an effect fires, we can use this pattern to react to changes in our component. This is especially useful when passing down props. function Box({ children, endX}) { const boxRef = useRef(); // run when `endX` changes useEffect(() => { gsap.to(boxRef.current, { x: endX }); }, [endX]); return ( <div className="box" ref={boxRef}>{children}</div> ); } See the Pen React Tutorial 1c by GreenSock (@GreenSock) on CodePen. Animating on interactionAnimating on interaction Interaction is one of the most exciting things about animating on the web! In order to hook into user interactions like hover, we can use callbacks. const onEnter = ({ currentTarget }) => { gsap.to(currentTarget, { backgroundColor: "#e77614" }); }; const onLeave = ({ currentTarget }) => { gsap.to(currentTarget, { backgroundColor: "#28a92b" }); }; return ( <div className="box" onMouseEnter={onEnter} onMouseLeave={onLeave}> Hover Me </div> ); See the Pen React Tutorial 1d by GreenSock (@GreenSock) on CodePen. Avoiding flash of unstyled content (FOUC)avoiding fouc As useEffect fires after the DOM has been painted, when fading in elements you may notice an undesired flash of unstyled content. In order to avoid the flash, we can replace useEffect with useLayoutEffect. useLayoutEffect functions exactly the same as useEffect, but React doesn’t run it until the DOM has been painted. See the Pen Avoiding FOUC with useLayoutEffect() by GreenSock (@GreenSock) on CodePen. useLayoutEffect is especially useful when you need to make DOM measurements, so we highly recommend it when using our ScrollTrigger and FLIP plugins. More information about useEffect vs useLayoutEffect. Cleaning UpCleaning up It’s a good idea to return a cleanup function in your effects to kill off any running animations and anything else that could cause a memory leak, like an event listener. This is particularly important if an animation runs for a really long time, makes use of ScrollTrigger, or changes the state in a component. useEffect(() => { const animation1 = gsap.to(".box1", { rotation: "+=360" }); const animation2 = gsap.to(".box2", { scrollTrigger: { ... } }); const onMove = () => { ... }; window.addEventListener("pointermove", onMove); // cleanup function will be called when component is removed return () => { animation1.kill(); animation2.scrollTrigger.kill(); window.removeEventListener("pointermove", onMove); }; }, []); We hope this article was helpful - If you have any feedback please leave us a comment below so we can smooth out the learning curve for future animators! Feeling confident and want to learn more? Check out our follow up article - GSAP + React, advanced animation techniques.
  6. I am integrating simple gsap animations into my React application. I can trigger a animation.play() fn on onMouseEnter, but the animation.reverse() fn is not working. Here's my set up: const hoverScaleAnimation = ( ref: MutableRefObject<HTMLDivElement | null> ) => { return gsap.to(ref.current, { scale: 1.05 }); }; const onEnter = (ref: MutableRefObject<HTMLDivElement | null>) => { hoverScaleAnimation(ref).play(); }; const onLeave = (ref: MutableRefObject<HTMLDivElement | null>) => { hoverScaleAnimation(ref).reverse(); }; Originally I was just triggering new gsap.to animations in the hover functions, but I thought this would be cleaner/less need to calculate how to scale and de-scale. I am following these react & gsap guidelines from greensock, and tried to use reverse() in line with the recommendations here and here. Code sandbox
  7. React has updated and introduced hooks since this article was written. We recommend reading our updated guide to animating with GSAP in React. This page was also created for GSAP version 2. We have since released GSAP 3 with many improvements. While it is backward compatible with most GSAP 2 features, some parts may need to be updated to work properly. Please see the GSAP 3 release notes for details Preface This guide assumes a basic knowledge of both the GreenSock Animation Platform (GSAP) and React, as well as some common tools used to develop a React app. As GSAP becomes the de-facto standard for creating rich animations and UI's on the web, developers must learn how to integrate it with other tools like React which has become popular because it allows developers to write their apps in a modular, declarative and re-usable fashion. As a moderator in the GreenSock forums, I've noticed that there are a few common hurdles to getting the two working together seamlessly, like referencing the DOM element appropriately, doing things The React Way, etc. which is why I'm writing this article. We won't delve into how a React app should be structured since our focus is on using GSAP, but the techniques used throughout this guide follow the official guidelines and have been reviewed by maintainers of the React Transition Group tool. We'll start simple and get more complex toward the end. How GSAP Works GSAP basically updates numeric properties of an object many times per second which creates the illusion of animation. For DOM elements, GSAP updates the the inline style properties. const myElement = document.getElementById("my-element"); TweenLite.to(myElement, 1, {width: 100, backgroundColor: "red"}); As you can see this means that we need access to the actual DOM node rendered in the document in order to pass it to the TweenLite.to() method. How React Works Explaining how React works is beyond the scope of this article, but let's focus on how React gets the JSX code we write and puts that in the DOM. <div className="my-class"> Some content here </div> With React, we normally don't pass an id attribute to the element because we use a declarative way to access methods, instances, props and state. It's through the component's (or the application's) state that we can change how things are represented in the DOM. There's no direct DOM manipulation, so typically there's no need to actually access the DOM. The React team has given developers ways to access the DOM nodes when needed, and the API changed a bit over the years as React matured. At this time (September, 2018) the latest version of React (16.4.2) allows developers to use Refs to access the DOM nodes. In this guide we'll mainly use the Callback Refs to create a reference to the DOM node and then feed it into GSAP animations because it's much faster for GSAP to directly manipulate properties rather than funneling them through React's state machine. Creating Our First Animation We'll use the ref to access the DOM node and the componentDidMount() lifecycle method of the component to create our first animation, because this will guarantee that the node has been added to the DOM tree and is ready to be passed into a GSAP animation. class MyComponent extends Component { constructor(props){ super(props); // reference to the DOM node this.myElement = null; // reference to the animation this.myTween = null; } componentDidMount(){ // use the node ref to create the animation this.myTween = TweenLite.to(this.myElement, 1, {x: 100, y: 100}); } render(){ return <div ref={div => this.myElement = div} />; } } Not that difficult, right? Let's go through the code so we can understand what is happening. First when we create an instance of this class, two properties are added to it: myElement and myTween, but both are equal to null. Why? Because at this point the node has not been added to the DOM tree and if we try to pass this node to a GSAP animation, we'll get an error indicating that GSAP cannot tween a null target. After the new instance has been initialized, the render() method runs. In the render method we're using the ref attribute that is basically a function that has a single parameter – the DOM node being added to the DOM tree. At this point we update the reference to the DOM node created in the class constructor. After that, this reference is no longer null and can be used anywhere we need it in our component. Finally, the componentDidMount() method runs and updates the reference to myTween with a TweenLite tween whose target is the internal reference to the DOM node that should animate. Simple, elegant and very React-way of us! It is worth mentioning that we could have created a one-run-animation by not creating a reference to the TweenLite tween in the constructor method. We could have just created a tween in the componentDidMount method and it would run immediately, like this: componentDidMount(){ TweenLite.to(this.myElement, 1, {x: 100, y: 100}); } The main benefit of storing a TweenLite tween as a reference in the component, is that this pattern allows us to use any of the methods GSAP has to offer like: play(), pause(), reverse(), restart(), seek(), change the speed (timeScale), etc., to get full control of the animations. Also this approach allows us to create any GSAP animation (TweenLite, TweenMax, TimelineLite, etc.) in the constructor. For example, we could use a timeline in order to create a complex animation: constructor(props){ super(props); this.myElement = null; this.myTween = TimelineLite({paused: true}); } componentDidMount(){ this.myTween .to(this.myElement, 0.5, {x: 100}) .to(this.myElement, 0.5, {y: 100, rotation: 180}) .play(); } With this approach we create a paused Timeline in the constructor and add the individual tweens using the shorthand methods. Since the Timeline was paused initially, we play it after adding all the tweens to it. We could also leave it paused and control it somewhere else in our app. The following example shows this technique: Simple Tween Demo Animating a Group of Elements One of the perks of using React is that allows us to add a group of elements using the array.map() method, which reduces the amount of HTML we have to write. This also can help us when creating an animation for all those elements. Let's say that you want to animate a group of elements onto the screen in a staggered fashion. It's simple: constructor(props){ super(props); this.myTween = new TimelineLite({paused: true}); this.myElements = []; } componentDidMount(){ this.myTween.staggerTo(this.myElements, 0.5, {y: 0, autoAlpha: 1}, 0.1); } render(){ return <div> <ul> {elementsArray.map((element, index) => <li key={element.id} ref={li => this.myElements[index] = li} > {element.name} </li>)} </ul> </div>; } This looks a bit more complex but we're using the same pattern to access each DOM node. The only difference is that instead of using a single reference for each element, we add each element to an array. In the componentDidMount() method we use TimelineLite.staggerTo() and GSAP does its magic to create a staggered animation! Multiple Elements Demo Creating a Complex Sequence We won't always get all the elements in an array so sometimes we might need to create a complex animation using different elements. Just like in the first example we store a reference in the constructor for each element and create our timeline in the componentDidMount() method: Timeline Sequence Demo Note how in this example we use a combination of methods. Most of the elements are stored as an instance property using this.element = null, but also we're adding a group of elements using an array.map(). Instead of using the map() callback to create tweens in the timeline (which is completely possible), we're adding them to an array that is passed in the staggerFrom() method to create the stagger effect. Animating Via State The most commonly used pattern to update a React app is through changing the state of its components. So it's easy to control when and how elements are animated based on the app state. It's not very difficult to listen to state changes and control a GSAP animation depending on state, using the componentDidUpdate() lifecycle method. Basically we compare the value of a state property before the update and after the update, and control the animation accordingly. componentDidUpdate(prevProps, prevState) { if (prevState.play !== this.state.play) { this.myTween.play(); } } Control Through State Demo In this example we compare the value of different state properties (one for each control method implemented in the component) to control the animation as those values are updated. It's important to notice that this example is a bit convoluted for doing something that can be achieved by calling a method directly in an event handler (such as onClick). The main idea is to show the proper way of controlling things through the state. A cleaner and simpler way to control an animation is by passing a prop from a parent component or through an app state store such as Redux or MobX. This modal samples does exactly that: // parent component <ModalComponent visible={this.state.modalVisible} close={this.setModalVisible.bind(null, false)} /> // ModalComponent constructor(props){ super(props); this.modalTween = new TimelineLite({ paused: true }); } componentDidMount() { this.modalTween .to(this.modalWrap, 0.01, { autoAlpha: 1 }) .to(this.modalDialog, 0.25, { y: 50, autoAlpha: 1 }, 0) .reversed(true) .paused(false); } componentDidUpdate(){ this.modalTween.reversed(!this.props.visible); } As you can see the modal animation is controlled by updating the visible prop passed by its parent, as well as a close method passed as a prop. This code is far simpler and reduces the chance of error. State Modal Demo Using React Transition Group React Transition Group(RTG) is a great tool that allows another level of control when animating an element in a React app. This is referred to as the capacity to mount and unmount either the element being animated or an entire component. This might not seem like much when animating a single image or a div, but this could mean a significant performance enhancement in our app in some cases. SIMPLE TRANSITION DEMO In this example the <Transition> component wraps the element we want to animate. This element remains unmounted while it's show prop is false. When the value changes to true, it is mounted and then the animation starts. Then when the prop is set to false again, another animation starts and when this is completed it can also use the <Transition> component to wrap the entire component. RTG also provides the <TransitionGroup> component, which allows us to control a group of <Transition> components, in the same way a single <Transition> component allows to control the mounting and unmounting of a component. This is a good alternative for animating dynamic lists that could have elements added and/or removed, or lists based on data filtering. Transition Group Demo <Transition timeout={1000} mountOnEnter unmountOnExit in={show} addEndListener={(node, done) => { TweenLite.to(node, 0.35, { y: 0, autoAlpha: show ? 1 : 0, onComplete: done, delay: !show ? 0 : card.init ? props.index * 0.15 : 0 }); }} > In this example we use the addEndListener() callback from the <Transition> component. This gives us two parameters, the node element being added in the DOM tree and the done callback, which allows to control the inner state of the <Transition> component as the element is mounted and unmounted. The entire animation is controlled by the in prop, which triggers the addEndListener() and ultimately the animation. You may notice that we're not creating two different animations for the enter/exit state of the component. We create a single animation that uses the same DOM node and the same properties. By doing this, GSAP's overwrite manager kills any existing animation affecting the same element and properties, giving us a seamless transition between the enter and exit animations. Finally, using RTG allows us for a more fine-grained code, because we can use all the event callbacks provided by GSAP (onStart, onUpdate, onComplete, onReverse, onReverseComplete) to run all the code we want, before calling the done callback (is extremely important to notify that the animation has completed). Animating Route Changes Routing is one of the most common scenarios in a React app. Route changes in a React app means that an entirely different view is rendered depending on the path in the browser's address bar which is the most common pattern to render a completely different component in a route change. Obviously animating those changes gives a very professional look and feel to our React apps. Rendering a new component based on a route change means that the component of the previous route is unmounted and the one for the next route is mounted. We already covered animating components animations tied to mount/unmount using the <Transition> component from RTG, so this is a very good option to animate route changes. <BrowserRouter> <div> <Route path="/" exact> { ({ match }) => <Home show={match !== null} /> } </Route> <Route path="/services"> { ({ match }) => <Services show={match !== null} /> } </Route> <Route path="/contact"> { ({ match }) => <Contact show={match !== null} /> } </Route> </div> </BrowserRouter> This main component uses React Router's <BrowserRouter> and <Route> and checks the match object passed as a prop to every <Route> component, while returning the component that should be rendered for each URL. Also we pass the show property to each component, in the same way we did in the transition example. <Transition unmountOnExit in={props.show} timeout={1000} onEnter={node => TweenLite.set(node, startState)} addEndListener={ (node, done) => { TweenLite.to(node, 0.5, { autoAlpha: props.show ? 1 : 0, y: props.show ? 0 : 50, onComplete: done }); }} > As you can see, the code is basically the same used to animate a single component; the only difference is that now we have two animations happening in different components at the same time. Route Animation Demo It's worth noting that the animations used in this example are quite simple but you can use any type of animation even complex, nested animations. As you can see by now, using GSAP and React can play together nicely. With all the tools and plugins GSAP has to offer the sky is the limit for creating compelling and amazing React applications! FAQ What is this "Virtual DOM" thing, that is referred so much when it comes to React Apps? Can GSAP work with this virtual dom? The Virtual DOM is what React uses to update the DOM in a fast and efficient way. In order to learn more about it check this article and the React Docs. GSAP can't work with the virtual DOM because the elements in the Virtual DOM are not exactly DOM nodes per-se. I often read about the declarative nature of React. Does that affect how we use GSAP in a React APP? Yes. React works by updating the rendered DOM through changes in the App state, so when creating an animation using GSAP, instead of reaching out directly to the DOM, like in most other cases, we need to wait for those changes in the app state and the DOM to be rendered, in order to use the current representation of the app state and create the animation. To learn more about how declarative and imperative code work read this article. In the second sample I see this code in the ref callback ref={div => this.cards = div}. Why is the index being used instead of just pushing the element in the array? The reason for that is that every time a React component is re-rendered, the render method is executed, but the original instance remains unchanged. The array used to create the animation is created in the component's constructor. The GSAP animation (a TimelineLite) is created in the componentDidMount hook. These two elements are created just once in the App's lifecycle, while the render method is executed on every re-render. Therefore if we push the elements to the array on every re-render, even though the Timeline instance won't change, the array will get bigger and bigger every time the component is re-rendered. This could cause a memory issue, especially for large collections. In the guide one of the samples triggers animations via the state of a component or the app. Is it possible to update the state of the component/app using GSAP? Absolutely! All you have to do is use one of the many callback events GSAP has to offer. The only precaution is to be aware of infinite loops. That is if an animation is started on the render method of a component and a callback from that animation updates the state of the component then that will trigger a re-render, which will start the animation again. You can check this simple example of how that can be done. Is it possible to trigger a route change using GSAP? It is possible using React Router's API. Although is not recommended because using React Router's API directly will prevent triggering the route change animations when using the browser's back and forward buttons. However, using React Transition Group with GSAP does trigger the route change animations with the native navigation methods. Can I use other GSAP plugins and tools in a React App? This guide shows only TweenMax, Timeline and the CSS Plugin? Yes, any GSAP tool or plugin you want can be used in a React app. Just be sure to follow the same patterns and guidelines from this article and you'll be fine. I tried the code in the guide and samples, but it doesn't work. What can I do? Head to the GreenSock forums where all your questions will be answered as fast as possible. I want to contribute or post an issue to this guide. Where can I do that? Even though this guide was reviewed by GreenSock and React experts, perhaps something might have slipped away, or with time and new versions, some things should or could be done differently. For those cases please head to this GitHub Repo and inform any issues or create a Pull Request with the changes you think should be added. New to GSAP? Check out the Getting Started Guide. Got questions? Head over to the GreenSock forums where there's a fantastic community of animators. Acknowledgments I'd like to thank the three developers that took time from their work and lives to review this guide as well as the samples in it. I couldn't have done this without their help and valuable input. Please be sure to follow them: Xiaoyan Wang: A very talented React developer. While Xiaoyan doesn't have a very active online social life (Twitter, Facebook, etc), you can follow what he does on GitHub. Jason Quense: One of the maintainers of React Transition Group and part of the React Bootstrap Team. Jason also collaborates in many other React-related projects. Check Jason's GitHub profile for more info. Matija Marohnić: The most active contributor and maintainer of React Transition Group and Part of the Yeoman Team. Matija also contributes in a lot of React-related projects as well as many other open source software. Be sure to follow Matija in GitHub and Twitter. A guest post from Rodrigo Hernando, a talented animator/developer who has years of experience solving animation challenges. Rodrigo is a seasoned React developer and he was one of our first moderators, serving the GreenSock community since 2011 with his expert help and friendly charm.
  8. Hello everyone, This is a reformulation of a topic that I created a few days ago. I am creating a new one here because I think it is more a ScrollTrigger+React-related problem and that it might benefit from being referenced as such. Context There are two consecutive "sections" that are both a 100vw/100vh. Each section is a React component (in the CodeSandbox below, they're called respectively WorkOverview and HomeAbout). They both get pinned one after the other. Problem The second element gets pinned too early, exactly as if the padding of the first section's .pin-spacer wasn't taken into account. The weirdest thing is that it doesn't happen all the time (but must of the time). Please note that (1) the ScrollTriggers are created in the order they happen on the page and that (2) it is not caused by any asynchronously-loaded content on what the sections' sizing might rely (images are inside a pre-sized container). Here's a video that illustrate the problem: Here's the CodeSandbox link https://codesandbox.io/s/clever-rhodes-16ic1. Note: if you don't see the problem, refresh the page 1 or 2 times. Thanks in advance for you precious help!
  9. What I'm trying to do? I've got 2 images on top of each other I want to alpha-in (opacity) via the blend-mix-mode layer with ScrollTrigger. position: relative; height: 100vh; width: 100vw; overflow-y: scroll; } img { min-width: 100vw; max-width: 100vw; height: 200vh; position: absolute; object-fit: cover; &#imageOne{ overflow-y: scroll; } &#imageTwo { overflow-y: scroll; mix-blend-mode: multiply; } } I'm having trouble just getting the images to animate with a scroll bar. I'm using big images about 5000px in size becuase I want them to be in a container with a scroll bar that I can use as the 'Scroller' for the Opacity for the image with blend modes on. What I've come up with: So far not really much luck. Here is a running view of what I've got so far. https://react-pvbx1r.stackblitz.io/ I have the project here you can see the code at the bottom is the images and the styling is in BackgroundStyle. I feel I have got the right Idea with GSAP in using useEffect to set options after the DOM has been rendered and using useRef for referencing Nodes to give to GSAP. But I don't think my problem is with React. I just cant seem to get the scroll and the animation to work. Here is the Editor link https://stackblitz.com/edit/react-pvbx1r?file=src/components/landing.js Thanks P.S I'm learning a bunch of frameworks at the moemnt so there is a chance im using quite a lot of this stuff wrong, if you spot any bits that aren't written well I would be very gratefull if you highlighted them! All the best
  10. I created a timeline animation which runs on click event. Animations are working smoothly only after few refresh in most of the browsers. Below is my function with timeline.. function exploreTimeline() { gsap.timeline() . to("body", { position: "static", overflow: "auto" }) . to(".intro-content", { duration: 0.3, autoAlpha: 0.01, marginLeft: 50, ease: "power1" }, 0) . to(".intro-content", { duration: 0.8, height: 0, marginBottom: 0, ease: "power2.in" }, 0) . to(".intro-section", { background: "none" }, 0) . to(".globe", { filter: "blur(5px)" }, 0) . to(".magical-slider-container", { duration: 0.8, bottom: "5%", ease: "none", onComplete: () => { ScrollTrigger.refresh(); } }, 0) }
  11. Hi, I want to use gsap to animate the two pseudo elements (::before and ::after) of my buttons. The each button element is inside a Group component in my react project. I want to play the animations on ::before and ::after alternately. Here is my code: import React, { useState } from "react"; import { gsap } from "gsap"; import { CSSRulePlugin } from "gsap/CSSRulePlugin"; const animation = (qseudoElement) => { gsap.registerPlugin(CSSRulePlugin); const qseudo = CSSRulePlugin.getRule(qseudoElement); const tl = gsap.timeline(); tl.fromTo( qseudo, { cssRule: { opacity: 0, scale: 0 } }, { cssRule: { opacity: 1, scale: 1 }, duration: 1 } ); tl.to(qseudo, { cssRule: { scale: 0, opacity: 0 }, duration: 1 }); }; const Group = () => { const [count, setCount] = useState(0); const handleClick = () => { setCount(count + 1); if (count % 2 === 0) { animation(".btn::before"); } else { animation(".btn::after"); } }; return ( <div> <button className="btn" onClick={() => handleClick()}>Click Me</button> </div> ); }; export default Group; And I have multiple Group components in my App. import Group from "./components/Group"; import "./styles.css"; export default function App() { return ( <div className="App"> <Group /> <Group /> <Group /> </div> ); } The problem is that if I click on any button, the animations are fire on all buttons. I know this is because of they have the same css class. How can I select the button that is clicked and animate its pseudo elements? I want to achieve something like this: const handleClick = (e) => { setCount(count + 1); if(count % 2 === 0){ animation(e::before); } else { animation(e::after); } } I appreciate your help and I made a demo here: codesandbox
  12. Hello everyone, I would like to develop this kind of animation for my background. Anybody suggest me about this how can I develop this? Also how can I add liquid hover effect on images. https://s.muz.li/NzNjY2YzNGRi Thanks
  13. Hi guys, I am currently rebuilding my website and i am asking myself what is the bestway to share one timeline through multiple components ? let's say i have this architecture --Layout ----Header ----Component1 --------Component2 --------Component3 ----Footer And i want part of my header timeline starting after the one executed in component3 ? Currently i am using context but it feels a bit strange to me Provider.js const [globalTimeline, setGlobalTimeline] = useState( gsap.timeline({ paused: true }) ) // Global Timeline for animation <myContext.Provider value={{ globalTimeline, setGlobalTimeline: tl => setGlobalTimeline(tl), }} > {children} </myContext.Provider> Layout.js useEffect(() => { globalTimeline.addLabel('start'') globalTimeline.play() }, []) I call my context in every component and add my animation to the globalTimeline. Thank you
  14. Hi ! Apologies in advance, I'm a musician / digital artist and a total beginner with gsap (like 2 days ago) but also very new to Js, React and Three.js. I'm really amazed how easily gsap handles animation of any params compared to other solutions I found in the past. It will make my life so much easier, I'm already considering to join the Greensock club soon...! Since yesterday I've been hitting my head against the wall and I can't figure this out. Here's the sandbox : https://7cuco.csb.app I have this button that triggers random variations of color and rotation speed of a cube. I wanted gsap to smooth every random value by a few seconds. I managed to make this happen very easily for the rotation speed (I guess because I worked from a simple object) but for some reason it doesn't work as excepted with the Three.color object. Every click resets the color to black, before moving gradually to the selected color. Almost... I'm really sorry in advance, I expect this to be a really stupid mistake. Thanks in advance for your help !
  15. Hi All, I am trying to animate something using scroll trigger (minimal reproducible example in codepen link ). I want to scale the circle to 1 when scroll progress is 25%. And for that reason I tried setting the timeline duration to 4 and the animation duration to 1 but it doesn't work. It still takes the full progress of the scroll trigger to make it scale to 1. Your help is highly appreciated. Thanks Edit: Codepen doesn't seem to be working. Here is a code sandbox instead https://codesandbox.io/s/gsap-scrolll-blimx?file=/src/App.js:0-1097
  16. I am having an issue I have seen described in other contexts but haven't found a solution for. My project is using the Next.js framework for react, Smooth Scrollbar, and ScrollTrigger. I have a .to() with a scrollTrigger object inside that is in a <Navigation /> component. The text at the top of the page is just supposed to pin itself and scroll with the window, but it only works in some cases. I think I'm not fully understanding some lifecycle thing here. All these methods of reproducing my errors are also commented at the top of the index.js file in the sandbox With Smooth Scrollbar enabled, the scrollTrigger object stored in the <Navigation /> component, and the <Navigation /> component imported and nested in the main functional component, the effect does not work after refreshing the page. If you go into the scrollTrigger object in the <Navigation /> component and update one of the values (change the 'end' value to a different number) this will trigger a hot reload without refreshing the browser. If you scroll back to the top and scroll down, you will see the effect is now working. If you comment out the entire useEffect() hook in the main index.js component this will disable Smooth Scrollbar. If you save the file and refresh you will see the effect working as intended, without having to trigger a hot reload, but now there is no Smooth Scrollbar. If you reset the sandbox to the original state, uncomment the HTML in the main index.js components return, comment out the <Navigation /> component right above it, save and then refresh, you will see that the effect works on load, with Smooth Scrollbar, without having to trigger a hot reload, but now I've lost the ability to nest components. CodeSandbox link Direct link to results in browser (A little easier for refreshing) Any help would be greatly appreciated. I'm out of ideas at this point.
  17. Hi, im tying to use scrollProxy with locomotive like in the docs with Nextjs. Locomotive works fine but scrollTrigger its always at the initial position. It seams that the scrollTop inside the scrollerPoxy its not returning any value. I made a codesandbox to show the case: Scroll component: https://codesandbox.io/s/nextjs-gsap-locomotive-qq11t?file=/src/components/scroll.jsx Live demo: https://qq11t.sse.codesandbox.io/ I'm looking for a good way to implement a smooth-scroll in nextjs so if any have an alternative to locomotive and works with gsap it would help any way. Thx, for the support
  18. Hi there! We just completed a major site redesign featuring ScrollTrigger in several modules. It's been awesome, thanks to all involved in making this happen! One issue that came up is illustrated in the Codepen. We have a few modules that "expand" with interaction, like the green Module 2. The horizontal scroll module works great, but if the module above is expanded, it throws off the start/end positions of the trigger. My initial solution was to add a resizeObserver on the height of the body, that would trigger a window.resize event. This was effective in conjunction with MatchMedia, but it's global and is interrupting other animations that are happening on-screen while the body size is changing. What's the best (and more "componentized" ) way to recalculate the trigger when its position changes like that? Note: I don't think it's necessarily relevant, but we are using React in a sort of unique set up... we are working within a modular CMS, so each module is its own React app, similar to how the Codepen is set up. Much thanks!
  19. I've seen various threads and blog posts about using lagSmoothing() to prevent animations pausing when switching screens. Every time I try to implement solutions, I get either ___ is not a function or not a method, or cannot read property ___ of undefined or something to that effect. Here's the base animation: useEffect(() => { const timeline = new TimelineLite(); timeline.from(itemRef.current, { autoAlpha: 0, stagger: 0.5, }); }); I've tried adding a TweenLite instance, like recommended here and elsewhere useEffect(() => { const timeline = new TimelineLite(); TweenLite.ticker.useRAF(false); TweenLite.lagSmoothing(0); timeline.from(itemRef.current, { autoAlpha: 0, stagger: 0.5, }); }); I've also tried adding the fn directly to my timeline instance: useEffect(() => { const timeline = new TimelineLite(); timeline.lagSmoothing(0); timeline.from(itemRef.current, { autoAlpha: 0, stagger: 0.5, }); }); And have also tried with the updated gsap.timeline() syntax from this Stack OVerflow post. Does anyone have an example of how lagsmoothing should be used with a timeline instance?
  20. Congratulations on the release of GSAP 3! The work you lot do both with your framework and with the support for it is just incredible, and I personally can't express how thankful I am to have this resource! https://codesandbox.io/s/friendly-ellis-yi33q?fontsize=14 So what I'm trying to do is to run an animation across a few components. In this example specifically, I'm trying to have a button that animates and opens a modal, which then animates the content of the modal. I got it all to open relatively well, but what I'm not able to figure out is how best to go about reversing the animation, and more specifically the `ModalContent.js` animation. What I want to have happen is for it to reverse the content, then transition the modal itself. I'm certain that the issues all lie in the way in which I set this up, so any suggestions on structuring this better (while still preserving the capabilities of animating across multiple components), I'm all ears. Also, am I correct in using refs for targeting the elements? Is there any type of drawback to having a fair amount of refs for animating? Thanks!
  21. I am using Gatsby JS and initializing smooth-scrollbar within a layout component and I've also configured the scroller proxy. Within a child component, I'm trying to setup an animation using scroll trigger but I'm not sure how to configure this. Also, the config does work after HMR fires but not after page refresh. I removed most of the markup from the page as I know the animation works - I think the issue is with syncing the scrollers. layout.js: import React, { useRef, useEffect } from "react" import Navbar from "./navbar" import { gsap } from "gsap"; import { ScrollTrigger } from "gsap/ScrollTrigger"; import Scrollbar from 'smooth-scrollbar' // import "../styles/locomotive-scroll.css" const Layout = ({ children, location }) => { const scrollerRef = useRef() useEffect(() => { gsap.registerPlugin(ScrollTrigger) const scroller = scrollerRef.current const bodyScrollBar = Scrollbar.init(scrollerRef.current, { damping: 0.1, delegateTo: document, alwaysShowTracks: true }) window.bodyScrollBar = bodyScrollBar ScrollTrigger.scrollerProxy(scrollerRef.current, { scrollTop(value) { if (arguments.length) { bodyScrollBar.scrollTop = value } return bodyScrollBar.scrollTop } }) window.scroller = scrollerRef.current bodyScrollBar.addListener(ScrollTrigger.update) ScrollTrigger.defaults({ scroller: scroller }) // ScrollTrigger.refresh() return () => { if (bodyScrollBar) bodyScrollBar.destroy() bodyScrollBar.removeListener(ScrollTrigger.update) } }, [location]) return ( <div className="global-wrapper scroller h-screen overflow-hidden" ref={scrollerRef}> <header className="global-header animate__animated animate__fadeIn"> <Navbar /> </header> <main>{children}</main> <footer> {/* © {new Date().getFullYear()}, Built with */} {` `} {/* <a href="https://www.gatsbyjs.com">Gatsby</a> */} </footer> </div> ) } export default Layout index.js: import React, { useEffect, useRef } from "react" import { gsap } from "gsap"; import { ScrollTrigger } from "gsap/ScrollTrigger"; function Index() { const outroRef = useRef() const outroContentRef = useRef() useEffect(() => { gsap.registerPlugin(ScrollTrigger) gsap.fromTo(outroContentRef.current, { yPercent: "-50" }, { yPercent: "0", scrollTrigger: { scroller: window.scroller, trigger: outroRef.current, end: "bottom bottom", scrub: true, markers: true }, ease: "none" }) }, []); return ( <> {/* Page content */} </> ) } export default Index
  22. Hi there, I'm new to Gsap and looking for a way to animate a slider that I've created using react. Now, my Slider.js component is sourcing data from contentful via Graphql so I've took a different approach to build the slider as you can see below. Slider.js import React from "react" import { graphql, useStaticQuery } from "gatsby" const query = graphql` { allContentfulExperience(filter: {node_locale: {eq: "en-US"}} ) { nodes { id slug title excerpt { excerpt } image { fluid { ...GatsbyContentfulFluid_withWebp } } } } } ` const Slider = () => { const data = useStaticQuery(query) const { allContentfulExperience: { nodes: products }, } = data const [index, setIndex] = React.useState(0); React.useEffect(() => { const lastIndex = products.length - 1; if (index < 0) { setIndex(lastIndex) } if (index > lastIndex) { setIndex(0) } }, [index, products]) return <Wrapper> <div className="section-center"> {products.map((product, productIndex) => { let position = "nextSlide" if (productIndex === index) { position = "activeSlide" } if (productIndex === index - 1 || (index === 0 && productIndex === product.length - 1)) { position = "lastSlide" } return ( <article className={position} key={productIndex}> <Image fluid={product.image.fluid} alt={product.title} className="img"></Image> <div className="info"> <h3>{product.title}</h3> <p>{product.excerpt.excerpt}</p> <StyledLinks message="View more »" linkes={product.slug} /> </div> </article> ) })} <FormPrevious className="prev" onClick={() => setIndex(index - 1)} color="white" size="large" /> <FormNext className="next" onClick={() => setIndex(index + 1)} color="white" size="large" /> </div> </Wrapper> } export default Slider I was wondering now, if I can use Gsap to animate my slider. For example, If I click the ''next button'' I want the content to animate in (I hope you get my idea.. ). Is something like that possible with the Slider that I've built? What would be the Gsap plugins that I need? I know react pretty well but not really Gsap.. Thanks in advance!
  23. My goal is update a Three JS scene (colors, camera position, material properties, etc.) based on state properties set by the timeline. My scene is rendered inside of the BlobScene component which accepts an intensity prop. In the sample below, I'd like to update the intensity as the timeline is scrubbed which then updates the light intensity of my scene. In the pinned section, there are two columns: the left column contains text that will be translated into view and the right column contains the BlobScene. My ultimate goal is to change the color of the blob for each corresponding text section. So when the user scrolls from the red section to the green section, for example, the blob mesh would smoothly transition it's material color from red to green. Am I going about this the wrong way? I can create a minimal demo if needed. import React, { useState, useEffect } from "react" import { gsap } from "gsap"; import { ScrollTrigger } from "gsap/ScrollTrigger"; import BlobScene from '../components/blob-scene' function Index() { let [intensity, setIntensity] = useState({value: 1.5}) useEffect(() => { gsap.registerPlugin(ScrollTrigger) // Verticals Pinned Section Story const tl = gsap.timeline() tl.from(".red", {y: "100%"}) .from(".green", {y: "100%"}) .from(".blue", {y: "100%"}); // PROBLEM LINE tl.to(intensity, { value: setIntensity(0.5) }) ScrollTrigger.create({ animation: tl, trigger: "#verticals", scroller: "#___gatsby", start: "top top", end: "+=4000", scrub: true, pin: true, anticipatePin: 1 }); // each time the window updates, we should refresh ScrollTrigger and then update LocomotiveScroll. ScrollTrigger.addEventListener('refresh', () => window.scroll.update()) ScrollTrigger.refresh() }, [intensity]); return ( <React.Fragment> <div className="page"> <section id="verticals" className="h-screen flex items-center relative w-full overflow-hidden"> <div className="grid grid-cols-2 gap-6 h-full w-full"> {/* Grid Column 1 */} <div className="flex items-center"> <div className="relative w-full overflow-hidden h-80"> <div className="panel absolute h-full w-full red bg-red-500 text-black"> <h2>Panel 1</h2> </div> <div className="panel absolute h-full w-full green bg-green-500"> <h2>Panel 2</h2> </div> <div className="panel absolute h-full w-full blue bg-blue-500"> <h2>Panel 3</h2> </div> </div> </div> {/* Grid Column 2 */} <div className="flex items-center"> <BlobScene intensity={intensity.value}></BlobScene> </div> </div> </section> </div> </React.Fragment> ) } export default Index
  24. Hi all- I'm brand new to the GSAP space. I have an existing React project and want to implement a GSAP animation. The person that made it for me sent it in an HTML file. How would I go about implementing this into my React site? I know there's a react-gsap NPM package but figured there might be an easy way to just plug the HTML doc in and get it working that way? I've attached the file to this post; if you click when it loads, it should play the animation. Thanks! index-v3-3 (1).html
  25. Hey folks, I found this Codpen and would love to achieve this in React using Hooks, but having a little problem. Does anyone have this sample in React Hooks?
×