Jump to content

Search the Community

Showing results for tags 'gsap'.

  • Search By Tags

    Type tags separated by commas.
  • Search By Author

Content Type


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

Product Groups

  • Club GreenSock
  • TransformManager
  • Supercharge


There are no results to display.

Find results in...

Find results that contain...

Date Created

  • Start


Last Updated

  • Start


Filter by number of...


  • Start



Personal Website



Company Website



  1. Hi! I create some demo with image source. I'm trying to make pinned image sequence on GSAP 3. Reference: https://store.google.com/us/product/nest_doorbell_battery?hl=en-US#gq-overview-chap-intro-3 I've found some example but they are implemented on canvas and they user sprite. I dont need to use sprite. Example №1: https://codepen.io/osublake/pen/KKzgqKr Example №2: https://codepen.io/osublake/pen/2152a28cffe2c2c0cca8a3e47f7b21c6 I need other way. I know that's not GSAP product but I want to implement same thing Like ScrollMagic + gsap image sequence https://scrollmagic.io/examples/expert/image_sequence.htmlhttps://scrollmagic.io/examples/expert/image_sequence.html I cant figute it out, how to make it on gsap 3 only. Additional question: what's difference between canvas method or simply div with img source? Is canvas better for page speed optimization?
  2. I've been struggling with the issue for 3 days, rewriting, refactoring code few times. Please help me if possible, guys. I use ReactJS and GSAP to create different computed animations ( overlays over a video ). What happens is that when I seek to specific percentage completed, for example 0.19 out of 49s timeline total length, it does seek to the first 1s part of the animation timeline cycle, and doesn't show the animation at the stage expected based on the progress percentage. I couldn't upload project to codesandbox as 1) it is nda signed and 2) it says that it has exceeded the 500-module items limit; I'm really sorry for that. Could someone please help me? I can share the source code or give access to my github repository. Thanks in advance everyone! import gsap from 'gsap'; import RightTitleStyles from '../../../../styles/right-title.module.css'; import React from 'react'; interface RightTitleProps { range: Object; name: string; currentTime: number; isPreview: boolean; type: 'smaller' | 'bigger'; isVisible: boolean; style: any; subtitle: string; title: string; } const RightTitle = React.memo( ({ videoRef, setStyle, range, name, currentTime, isPreview, type, isVisible, style, title, subtitle, }: RightTitleProps) => { const titleRef = React.useRef(); const { current: tl } = React.useRef(gsap.timeline({ paused: true })); const [ rangeIntervals, setRangeIntervals ] = React.useState< Array< number > >( range.timeIntervals ); const connectTitleRef = ( el : HTMLElement ) => { if (titleRef.current || !el || !videoRef || isPreview ) { if ( isPreview || !el || rangeIntervals === range.timeIntervals ) { return; } else { tl.killAll(); // just clearing out some tweens for repeated recreation } } tl.progress(1 - (range.timeIntervals[1] - currentTime) / (range.timeIntervals[1] - range.timeIntervals[0])); titleRef.current = el; console.log( titleRef.current.id, videoRef, ); console.log('configuring...'); tl.fromTo(videoRef, { width: '100%' }, { duration: 1, width: '63%' }).to(videoRef, { duration: range.timeIntervals[1] - range.timeIntervals[0] - 1 - 1, width: '63%' }).to(videoRef, { duration: 1, width: '100%' }); console.log( 'video configured', ); tl.fromTo( el, { x: name === 'Right Title' ? 150 : -150 }, { duration: 1, x: 0 }, ) .to(el, { x: 0, duration: range.timeIntervals[1] - range.timeIntervals[0] - 1 - 1, }) .to(`#${ el.id }`, { duration: 1, x: name === 'Right Title' ? 150 : -150, }); console.log(range.timeIntervals[1] - range.timeIntervals[0] - 1 - 1); }; // console.log( style, ); React.useEffect(() => { if (!titleRef.current || isPreview) return; console.log( 'styles applied to titleRef', titleRef.current._gsTransform ); console.log( 'these are tweens', tl.getChildren().map( child => child.vars.x || child.vars.width ) ); console.log( 'these are tweens', tl.getChildren().map( child => child.vars ) ); if (!(range.timeIntervals[0] <= currentTime && currentTime <= range.timeIntervals[1])) { console.log( 'current timing doesn`t fit the intervals' ); setStyle({}); tl.progress(0); return; } setStyle({ marginLeft: name === 'Left Title' ? 'auto' : 'unset', marginRight: name === 'Right Title' ? 'auto' : 'unset', }); tl.progress(1 - (range.timeIntervals[1] - currentTime) / (range.timeIntervals[1] - range.timeIntervals[0])); console.log(range.timeIntervals[1] - range.timeIntervals[0] - 1 - 1) console.log( currentTime, range.timeIntervals, 1 - (range.timeIntervals[1] - currentTime) / (range.timeIntervals[1] - range.timeIntervals[0]), ); }, [range.timeIntervals, currentTime]); const show = isVisible; if ( isPreview ) { return <div style={{ top: type === 'smaller' && 0, height: type === 'smaller' && '100%', ...style }} className={RightTitleStyles.aligningWrapper} > <div style={{ transform: isPreview && 'scale(0.55)' }}> <h1> {title} </h1> <p> {subtitle} </p>{' '} </div> </div> } return ( <div ref={ connectTitleRef } id={`${isPreview ? 'previewMode' : 'notPreviewMode'}3${range.color.slice(1)}`} style={{ visibility : !( currentTime + 1 >= range.timeIntervals[0] && currentTime - 1 <= range.timeIntervals[1] ) ? 'hidden' : 'visible', top: type === 'smaller' && 0, height: type === 'smaller' && '100%', ...style }} className={RightTitleStyles.aligningWrapper} > <div style={{ transform: isPreview && 'scale(0.55)' }}> <h1> {title} </h1> <p> {subtitle} </p>{' '} </div> </div> ); } ); export default RightTitle; Title.tsx animation.tsx
  3. Hello! I'm wondering how to replicate this effect on https://monopo.london/ where the mouseover interacts with the colours. I thought it would be something like this https://tympanus.net/Development/FlowmapDeformation/index.html, but it does not seem like it as in this case, it's a still image. In Monopo London, the mouse was interacting with the colours. Really am dying to find out: 1) What this effect is called. Is it repulsion effect? 2) How do I make this? (e.g., use Pixi.JS or WebGL... or?) 3) Are there any tutorials to watch/learn about this mouseover effect together with the glowy background? Any help would be greatly appreciated!
  4. Hi and hello . Working on a custom code for a online platform but I can not at all figure out > how to implement a simple GIF preloader via my internal scr link? Is it possible to implement some kind of simple code to my existing code. The GIF preloader needs to load and fade in / out when the page transition start and ends? internal SRC link: <img src="/s/galleri-studio-artwork.gif"></img> Kind regards from Denmark. Thanks to @OSUblake for helping out with my other issue.
  5. When transforming 3D using perspective for svgs chrome there is always a bug that I face. For example on this web https://threeedy.surge.sh/ when all the elements are translated in 3D, there is a bug in the where it sticks on awkward positions. And this happens only in chrome , on firefox it stays in 3D transformation. Also in my portfolio web: https://obaidnadeem.surge.sh/ in the User Interface section I have done the same thing but getting the same bug in chrome while the firefox runs everything smoothly. Is their solution to this or am I doing something wrong? or the only solution is to wait until chrome team fixes bug from their end and extend support for Scalar Vector Graphics ?
  6. As i was using the angular 12 to build the webapp i'm trying to use the scroll trigger animation in my project, the thing is the initial animation is working fine. But when it comes to ScrollTrigger in doesn't works in angular. I dont know why... I want to target a particular class in HTML to animate it using the scrolltrigger plugin
  7. I am using gsap scrolltrigger to toggle a class that makes the container sticky. After the end of the animation when the class is removed it behaves weirdly. Here is the video link : https://drive.google.com/file/d/12WOmmK43dFBxvVLKuDPoAGnOAT4-b76L/view?usp=sharing As you can see in the video after the end of animation it moves it..... Is there any fix to this ? Javascript code : const canvas = document.getElementById('hero-lightpass'); const context = canvas.getContext('2d'); const frameCount = 45; const currentFrame = index => `./assets/medium_${(index + 1).toString().padStart(4, '0')}.jpg`; canvas.width = 600; const images = []; const airpods = { frame: 0, }; for (let i = 0; i < frameCount; i++) { const img = new Image(); img.src = currentFrame(i); images.push(img); } console.log(canvas.width); gsap.to(airpods, { frame: frameCount - 1, snap: 'frame', scrollTrigger: { trigger: '.sticky-wrapper', scrub: 0.5, markers: true, start: 'top 48px', end: 'center 49px', toggleClass: 'sticky', }, onUpdate: render, }); images[0].onload = render; const getheight = () => {}; function render() { console.log(images[0].naturalHeight); const h = images[0].naturalHeight; const w = images[0].naturalWidth; const aspect = h / w; console.log(aspect); canvas.height = aspect * canvas.width; context.drawImage(images[airpods.frame], 0, 0, canvas.width, canvas.height); }
  8. Hello every one , i found so trouble in use scroll trigger with react three fiber , so can any one help me ? or i have to learn some thing befor ?? i saw an example but never see what i need to do thank's
  9. Hi everyone, just started with GSAP two days ago! I'm having a problem with my horizontal scroll layout where sections are overlapping each other to an extent that you can't even view the content in them. Also, a lot of margins are being added. If I put real data in these sections, the last section disappears from the viewport (due to margin/padding being added) What I want to achieve is horizontal scroll but sections should respect the width and margins instead of overlapping. Please suggest me what should I do...
  10. Sorry for such a basic question. But it is a real problem. I am a GreenSock member and trying to load SlowMo for the first time and it just won't go. I've written basic code and loaded the proper CDNs <style> .box{width: 100px; height: 100px; border: solid 1px black;}</style> <body> <div class="box"></div> <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.7.1/gsap.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.7.1/EasePack.min.js"></script> <script src="EasePack.min.js"></script> <script> gsap.to('.box', 3,{x: 1000, ease: "slow(.1, .7"}); </script> </body> I've loaded the EasePack.min.js file into my project: By all appearances (to me) this should do it. The code runs but does not include the SlowMo. A simple task for many, but I've spent a lot of time spinning my wheels on this and getting nowhere. Would someone please direct me as to where I am going wrong?
  11. In the given like you can see that the scroll animation gets stuck for ios. Working fine of android. Below is my code. Here is the site link : https://animationstest.vercel.app/work I am also attaching the video for your reference : useEffect(() => { gsap.registerPlugin(ScrollTrigger); const boxes = gsap.utils.toArray('.Work-info'); boxes.forEach(box => { gsap.from(box, { scrollTrigger: box, y: 100, duration: 1.2, opacity: 0, }); }); }, []);
  12. Hi, I've been using gsap and multiple plugins over the last couple weeks. For the most part I've found my way around but been stuck on this for the past day. The pen is a minimized version of what I'm trying to achieve, which is a pinned panel that rolls through 7 sections (all full-screen), and some of the panels have a lottie animation tied to it via ScrollTrigger. I got the panel to work, but tweaking timings is getting complicated, which I'm trying to solve via using a master timeline so the timings are contained. The timeline bit seems to work but I can't seem to get lottie to fire up correctly. Also any pointers on the rest of the code is appreciated as I'm sure there's a lot to be improved. Thanks in advance
  13. Hi there, I'm trying to build a seamless animation where my van image is centered in the middle with its light, and it is following the line. The white dot at the left is from another codepen: https://codepen.io/lisaschumann/pen/KKqMMoe which I used as a starting point. That point follows the path perfectly, but whenever I try to make my van follow the path in the middle it still takes the path position (-33%) Also, when I try to add my own path, it destroys the animation even more. I would like to use this as my own path <path id="line" d="M0,251.32C59.82,242,144.24,236.61,205,247c24,4.11,51.08,16.94,93.28,17.46,51.08.62,85.58-13.89,110.91-21.77,46.78-14.55,71.87-9.48,225,4.42,154.49,14,231.73,21,263.29,20.4,157.16-3.08,161.25-38.87,254.58-20.52,107,21,131.36,73.93,210.74,60.61,63.78-10.7,66.12-50.36,142.89-69.32,65.67-16.23,129.58.42,193.86,8.47,92.31,11.57,189.23,6,280.72-8.29,292.41-45.49,315.9,15,621.94-1.28,293.86-15.63,282.72-46.8,401-24.71,151.74,28.35,230.58,91,380.35,64,56.7-10.22,79.74-21.4,168-37.43,69.39-12.61,149.91-9.39,220.58-9.3,52.95.07,131.22-1.76,227.87-4.31" style="fill:none;stroke:blue;stroke-linecap:round;stroke-width:10px" /> Any help is massivly appriciated!
  14. I want to use this parametric waves animation with gsap.min library, currently this is working with TwinMAX. After removing the gsap.min It's working fine but I cant remove gsap.min because other animations or not working without gsap.min I have getting the following error in console. Uncaught TypeError: Sine.easeInOut.getRatio is not a function https://codepen.io/ThiemelJiri/pen/pWyezW Anyone can help please? Thanks
  15. Hello! I am trying to accomplish this effect https://greensock.com/docs/v2/Plugins/DirectionalRotationPlugin on a 3d scene. So basically i want the camera to transition to the point selected and not jump to it as it is right now. How can i do that?
  16. Hello Guys, I am having trouble creating an circular animation with motion path. Basically what I am trying to achieve is that all the 4 images rotate in even manner. for example First image should start from 0%, second from 25%, third from 50% and fourth from 75%. Here is the code sandbox for my code https://codesandbox.io/s/orbit-motion-path-mb6ns?file=/src/App.js:1339-1344 Please see attached screenshot for what I am trying to achieve Please help me. Thanks
  17. Hello , i hope some one can help me , how i can do like this effect in gsap , not latterly same , but i want to open laptop win scroll , is there a way with gsap ? thank you in advance reference :https://www.apple.com/macbook-pro-13/ https://postimg.cc/ftRJTW5M
  18. Hello guys, Im trying to build a simple carousel slider in vue 2. First problem is that my timeline is initially paused (i heard that is a good practice) and if i click next or prev button my animation doesn't play. I found solution, but I don't know if it is right one. I mean, it works but when using prevSlide function animation back to first initial slide. (probably thats another not related problem) So my question is how to handle timelines in vue. Should I put timeline in mounted, and prevSlide and nextSlide functions should only play or reverse the timeline? temporary solution: nextSlide() { this.tl.to(gsap.utils.toArray(this.$refs.slider.children), { xPercent: "-=" + 100, onComplete: this.tl.pause() }); this.tl.play() } the main code: <template> <div id="app"> <div class="slider" ref="slider"> <div class="slide"></div> <div class="slide"></div> <div class="slide"></div> </div> <button @click="prevSlide">prev</button> <button @click="nextSlide">next</button> </div> </template> <script> import gsap from "gsap"; export default { name: "App", data() { return { currSlide: 1, tl: gsap.timeline({ paused: true }), }; }, methods: { nextSlide() { this.tl.to(gsap.utils.toArray(this.$refs.slider.children), { xPercent: "-=" + 100, }); }, prevSlide() { this.tl.to(gsap.utils.toArray(this.$refs.slider.children), { xPercent: "-=" - 100, }); }, }, mounted() {}, }; </script> <style> #app { font-family: "Avenir", Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 60px; } .slider { width: 600px; height: 350px; display: flex; flex-direction: row; overflow: hidden; } .slide { width: 100%; height: 100%; flex-shrink: 0; } .slide:nth-child(1) { background: crimson; } .slide:nth-child(2) { background: green; } .slide:nth-child(3) { background: darkcyan; } </style> Code playground down below https://codesandbox.io/s/gracious-fog-y4b8d?file=/src/App.vue
  19. 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 Playgrounds Get started quickly by forking one of these starter templates: CodePen CodeSandbox CodeSandbox + Bonus Plugins Component 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 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 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 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 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 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.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 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 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: useSelector 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> ); } useArrayRef 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> ); } useStateRef 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) }); }, []); useIsomorphicLayoutEffect 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!
  20. Struggling with .from() tweens in React 18? This is due to React's new 'Strict mode' - this forum thread will help you get back on track. React is a hugely popular library 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 Playgrounds Get started quickly by forking one of these starter templates: CodePen CodeSandbox CodeSandbox + Bonus Plugins Create 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 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 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 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 timelines 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. 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 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 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) 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 runs before 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 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.
  21. Hello Dear members of this forum can i please ask for a little guidance. I want to combine three.js and gsap/scrollTrigger. My page structure is simple: two sections both with height: 100vh First section is simple hero baner that is not containing any animation (not relevant for the issue) and the second section is a place when magic should happen. I animating three.js model as soon as the second section hits the viewport and I did it by absolute postioning inside relative parent, creating a timeline with all the changes to model (rotation, scale, lighting, camera movment) and connect it all with scroll trigger (using "pin" propery) and this works like charm. Since I cant use codepen i will paste code here (so sorry) js: const timeline = gsap.timeline({ scrollTrigger: { trigger: ".scene-wrapper", start: "top", scrub: 1, pin: true } }); timeline .to(ambientLight, {intensity: 4, duration: 0.4}, 'scene1') .to(mainLight, {intensity: 5, duration: 0.4}, 'scene1') .to(model.rotation, {y: -Math.PI / 2, duration: 4}, 'scene2') .to(model.position, {x: 0, duration: 3}, 'scene2') .to(camera.position, {y: 0, duration: 3}, 'scene2') .to(camera.position, {z: 4, duration: 2.5}, 'scene3') .to(model.rotation, {z: -0.8, duration: 3}, 'scene3') .to(model.rotation, {y: -2.6, duration: 3}, 'scene3') .to(model.rotation, {z: 0, duration: 3}, 'scene4') .to(model.rotation, {y: -3.1, duration: 3}, 'scene4') .to(camera.position, {y: 0.4, duration: 3}, 'scene4') .to(camera.position, {z: 6, duration: 3}, 'scene4') html: <div class="wrapper"> <div class="section one"> <h1>Funny stuff</h1> </div> <div class="section two scene-wrapper"> <div class="background-container"> <div class="animation-container"></div> </div> <div class="scroll-container"> <section class="scene-1 scene"> <div class="copy-wrapper"> <h2 class="headline">This is 3D Model approach</h2> <p class="subtitle">Lorem ipsum dolor sit amet consectetur adipisicing elit</p> </div> </section> <section class="scene-2 scene"> <div class="content-column"> <div class="copy-wrapper"> <h2 class="headline">Scene 2</h2> <p class="subtitle">Lorem ipsum dolor sit amet consectetur adipisicing elit</p> </div> </div> <div class="content-column"> </div> </section> <section class="scene-3 scene"> <div class="copy-wrapper"> <div class="content-column"> </div> <div class="content-column"> <div class="copy-wrapper"> <h2 class="headline">Scene 3</h2> <p class="subtitle">Lorem ipsum dolor sit amet consectetur adipisicing elit</p> </div> </div> </div> </section> <section class="scene-4 scene"> <div class="copy-wrapper"> <h2 class="headline">Scene 4</h2> <p class="subtitle">Lorem ipsum dolor sit amet consectetur adipisicing elit</p> </div> </section> </div> </div> </div> As you can see inside ".scene-wrapper" i have container for aniamtion AND for 4 sections that i called scenes. My model is pined to a container and stays always on top of the screen (thats desired behaviour) but now i want to hide ans show this 4 texts from the "scens" in particular moments of my aniamtion and I dont know how to approach this.... So question to you dear forum: 1. Should I add animations of each text to the created timeline and create even longer timeline that i have right now or create separate timeline for text ? 2. Do I need second scrollTrigger instance just for texts ? To give you an idea what I want to create please see screenshots NOTE: this is free model downloaded from Sketchfab
  22. Hi, let me start by saying i love gsap and the hard work you guys put in it, since thats out of the way as you can see in my code pen i have two problems i need to solve. 1. When i drag and click on a card before inertia velocity go to zero the cards container animation position rest to x zero break. 2. When a crad onclick happens i want the animation drag to stop and when clicked again or a close button to reverse animation and rest where the card was. I feel am close but i need help 😅
  23. Hello to all. Maybe someone has time to review and correct the script - https://codepen.io/tester_info/pen/JjNJLNV Here is a small problem, it’s incorrect scrolling and changing the directions of the scrolling (from horizontal scrolling to vertical one and vice versa). There are two problems: 1. There is a bug in the scrolling (as I understand it’s because of the vertical block takes height, such as in horizontal). 2. The last section of the "contact block" cannot be scrolled to its full width Maybe we can work differently to perform this task with mix (horizontal/vertical) scrolling sections?
  24. Hey guys, I'm using SplitText plugin to show like a typing animation, the problem is that sometimes I have buttons inside the paragraph and the plugin is also typing those character when I don't need the text inside the buttons to have that animation. It's there a way to get avoid that text to be splited? I try several techniques, one was to remove the paragraph with the buttons outside of the SplitText animation, the problem there is that I need the movement to the next line