Jump to content
GreenSock

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

Leaderboard

Popular Content

Showing content with the highest reputation since 11/30/2020 in all areas

  1. Welcome to the GreenSock forums! Glad you’re here. It’s a wonderful place to learn and get your questions answered. What topics can I post about here? We love answering questions that are directly related to GreenSock tools. API questions, bug reports, or if you’re wondering why GSAP behaves a certain way - those types of posts are welcome around here. What topics should be avoided? As much as we love solving problems, the following types of questions are beyond the scope of what we generally provide here for free: Logic issues. JavaScript and application logic, CSS setup, and generic troubleshooting that isn’t directly related to GreenSock tools. Third party tools. Frameworks (React, Angular), other JavaScript libraries (LocomotiveScroll, Barba), build tools, etc. We’re happy to help with the GSAP part of things if you strip out as much irrelevant code as possible and provide a minimal demo. “How do I do this cool effect I saw on a trendy website?” Someone here may point you in the right direction but please don't expect a full tutorial on how to create and effect you saw on a slick web site. Where else can I go for help? If your question is primarily about another tool, try looking for a forum or GitHub repository about that tool. If it’s a general programming-related question, try StackOverflow. Want feedback about your working code? We’d be glad to take a peek at GSAP-specific code but for more general topics (like performance or application logic) we’d suggest something like CodeReview. Read first Please read Getting Started with GSAP, common GSAP mistakes (maybe also common ScrollTrigger mistakes), as well as the GSAP docs before asking your question. Often you’ll get your question answered just by doing that! Make a minimal demo This helps provide context and gives us a rough idea of what you’re trying to accomplish. It's WAY better than trying to dig into a live website with lots of other things going on, or looking at a small excerpt of code without much context. Pro tip: It's often easier to create a minimal demo from scratch rather than stripping out irrelevant things from your original project. You will GREATLY increase your chances of getting a prompt answer if you create a minimal demo. After you've posted a demo to our forums, please click the "Fork" button on CodePen before making future changes so that context is not lost for future readers of the forum. Be courteous We try to treat people the way we’d want to be treated around here. Please do the same. Also keep in mind that the people answering your post are doing so for free! Most of our regular contributors gain nothing from helping you except the satisfaction of doing so. Please give them your gratitude and respect. Ask away! We’re eager to help, so make a minimal demo and ask your question! We’ll do our best to answer it promptly. Pay it forward - help someone else The best way to learn is to teach someone. You’d be surprised how much you grow when you try answering some questions here! We are so grateful for the group of volunteers dedicated to helping others in these forums. It’s quite satisfying to come alongside a fellow developer who is struggling and deliver a clever solution to their issue. Become a contributor! You do NOT need to be an expert. Anyone...and we do mean anyone...is welcome here.
    17 points
  2. A GSAP tale: One goofy guy’s odyssey from knowing nothing to knowing just enough to confuse himself. (This is crazy long so feel free to jump to the epic conclusion). Greetings fellow GreenSockers. The end of this week marks the one-year anniversary of my first post on the forum so I thought I’d take the opportunity to share my 12-month story and hopefully encourage others to jump into the conversations around here. Maybe you’ll recognize yourself in some of the things I’ve experienced. My quick history in a nutshell Web design and coding is a second career for me. After 15 years of owning and operating a photography studio and processing lab (back in the film days - yup - I’m old), the digital camera came along and changed that industry, which necessitated a new career for me. I shifted to video production, which led to motion graphics and finally to web design. Our little agency now offers all those services. The web design clients never needed anything fancy so JavaScript took a back seat to HTML & CSS only sites for a number of years. JavaScript & GSAP: false starts and other obligations I first discovered GSAP a few years ago, but only tried it briefly. It looked cool, but with the time obligations of field video work and motion graphics jobs, it wasn’t something I could work into the schedule. Besides that, it was JavaScript – too complicated I thought. I knew JavaScript was the third piece of a good web designer’s skillset along with HTML and CSS, but I always convinced myself that I didn’t have the time and the sites we built didn’t need it. JavaScript Books + Classes = Fail I did make a few attempts at reading some JavaScript books and working through some online tutorials, but it just never ‘stuck’. Maybe the examples were too theoretical and dry or they were the wrong books and classes. I really don’t know, but I abandoned the learning process a number of times. Cut and Paste mentality Why did I really need to learn anyway? You can just Google what you need, cut and paste some code and presto – you’ve got some working JavaScript or jQuery. I only understood a small portion of what I was cutting and pasting, but hey… it worked so the problem was solved. That’s how I operated for quite some time. What’s a loop? What’s an array? What’s an object? Who cares? Wait a minute. This is ridiculous. Last spring, I was remodeling our company website and I had all these grand visions about making things move and behave in certain ways. Googling for code just wasn’t cutting it. I suddenly felt stupid. “This is ridiculous!” I thought. I should be able to learn how to write my own code. Oh yeah, I remembered that GreenSock thing I had looked at a few times and abandoned. That might work. Maybe I could actually learn how to use it this time. I become a forum lurker I started lurking in the shadows of the forum. After reading a lot of posts, I saw people asking many types of questions from simple to crazy complicated (at least to me). Two things I noticed were that every effort was made to find an answer (no matter the difficulty level of the question) and not one post was condescending or snarky. That’s quite rare on the ol’ interwebs, isn’t it? Hmmmm…maybe I’m in the right place. Oh boy… time to ask a question of my own One of the great things about learning GSAP is you’ll also pick up a lot of other JavaScript and/or jQuery along the way. I kept reading and practicing with some simple tweens, but now I had a question. Dare I post? I suppose, like many others, I feared looking like an idiot even though the forum members and moderators seemed quite nice and helpful. I do several dumb things every day so you’d think I’d be used to it by now. Oh well, here goes. My first question had to do with the indexOf() a Draggable snap array. Within 30 minutes, Diaco and Rodrigo had posted great answers and neither one called me stupid! Yay – how cool. I get hooked on GSAP and the forum About that same time, I decided our company should discontinue on-site video production and switch to studio only filming. I got tired of lugging loads of video gear in and out of buildings – it’s quite tiring and as I mentioned earlier – I’m old. This freed up some time and I decided to dedicate that time to learning GSAP and maybe, one day, even helping others. It wasn’t too long and I actually knew the answer to a forum question. I posted some information and wow – a little red indicator lit up on my control panel. Someone liked something I wrote. How fun – I’m hooked. Carl makes direct contact I continued to learn and experiment. I posted a few additional questions of my own, but I tried to answer more than I asked. If someone posted a question for which I had no answer, I tried to look it up in the docs and figure it out. Most of the time I was far too slow and Jack, Carl or one of the mods would already have the answer posted before I was done reading the question, but it was an interesting way to learn. I did sneak in a few good answers, which led to a private message from Carl. He thanked me for participating and helping in the forums. I thought it was pretty cool that a super smart guy like Professor Schooff would take the time to do that for little ol’ me. My decision to dedicate time to the platform and forum was reinforced. http://i.imgur.com/hdaB73Y.jpg Blake and I have a conversation I don’t recall if it was a back and forth in a forum post or a private message conversation, but Blake told me something that, of course is obvious, but it stuck with me and is important for all of us to remember. He mentioned that we all enter this learning process knowing nothing. If someone of Blake’s considerable skill level can be humble enough to remember first starting out in code, there may be hope for me after all. I guess if you think about it, there was a time when the simple concept of a variable was brand new to all of us. We’re not born with these abilities. They’re learned and we’re all at different points on the educational path. Never feel stupid for not knowing something. Moderator Promotion Throughout the last year, I’ve continued to learn and study both GSAP and JavaScript. Some of those books I abandoned in the past even make sense now. I’ve tried to be active in the GS community and answer as many forum questions as possible. If I’ve answered a question of yours, I hope you found it somewhat helpful. I’ve cranked out some fun CodePens and finally started a Twitter account to tweet them out. I am nowhere near an expert with GSAP or JavaScript, but I know so much more than I knew a year ago. Apparently I know enough to be entrusted with a forum promotion to Moderator status. I’m honored to be included on such an amazing team. 12 months down – what’s next? My agency duties are still numerous so I can’t dedicate full time to coding, but it remains something to which I’m committed and thoroughly enjoy. I started this 12-month GSAP journey just wanting the ability to write my own code rather than cutting and pasting the work of others. I’m confident I have achieved that, but I still have days when a simple piece of code just won’t coalesce in my brain and that can be frustrating. I guess we all have those days, right? I make several mistakes every day, but that’s o.k. too. I learn a lot more from my screw-ups than I ever do when it all goes right on the first try. I plan to keep learning and getting better and when I get stuck, I’ll be able to get an answer from this amazing community. I’ll continue to give back to the GS community by answering any questions that are within my abilities to do so. The super mods: Jonathan, Blake, Diaco and Rodrigo Thank you to my fellow moderators. You guys rock and have taught me so much. @Jonathan – if there is a browser bug, quirk or special fix that you are not aware of, I’ve yet to read about it. Your knowledge has helped me fix many pieces of code before they even became a problem. Plus, if I ever have a question of top/left vs. x/y, I know who I’ll ask. @Blake – if I could be half as good at coding as you, I’d be a very happy guy. Your work always teaches and inspires me. I don’t think you’re allowed to ever stop posting on the forum or we may all show up on your doorstep and ask questions. @Diaco – your code is always so concise. I deconstruct some of your pens and am astounded by how much you squeeze out of a few lines. If I made some of your pens from scratch, I’d have 20 variables, 5 loops, 12 tweens and 80 lines of code. You do the same with two variables and 4 lines of code. Amazing stuff. @Rodrigo – when searching the forum, I often land on one of your past posts and learn a lot. Your knowledge is vast and I wish you had more time to post around here. Your ninja skills are incredibly strong. Our superhero leaders @Carl – I’ve participated in several online forums ranging from graphic design to 3D to video production, but the GreenSock forum is the best and a big part of that is you. You not only provide great answers, but you do it in clever ways with just the right amount of humor thrown in here and there. The collection of videos you’ve made is invaluable and should be mandatory viewing for anyone interested in GSAP. I’ve seen you monitoring the forums at all hours of the day and even on weekends. When you get any sleep I’ll never know, but I thank you for your dedication and sharing your knowledge. @Jack – how you had the vision to start GreenSock and write the first version of the animation platform I can only imagine. I’m glad you did because GSAP is such an amazing collection of tools. The friendliness of the community is definitely following your lead. I don’t understand a lot of what you talk about sometimes, but I know enough to be amazed by your brilliance and talent. You call yourself just a guy who geeks out about code, but you’re more than that. You’re a smart and generous innovator who’s created a special brand and place on the web. I think I can safely speak for the community when I say we all appreciate the time and effort you put into helping us make beautiful and high-performance animations. Thank you sir. The epic conclusion. Well… maybe just a regular conclusion. If you didn’t read the whole post, I don’t blame you. It’s ridiculously long and I’m just some guy you don’t know so I’ll wrap it up with this bit of advice. Whether you’re a genius or feel like an idiot, it doesn’t matter. Try to learn one new thing each day and before you know it, a year will have passed and all those little bits will add up to new skills and abilities. If you’ve never posted on the forum, please jump in and participate. The more voices we have around here, the more we all benefit. If you need an answer, please don’t be afraid to ask a question. Believe me, I’m just some goofy guy in front of a computer. If I can learn this stuff, so can you. As I begin my second year in GreenSockLand, I’m looking forward to learning more, seeing everyone’s work and answering as many of your questions as I can. This is an amazing community and I encourage anyone reading this to set up an account and get involved. My best to all of my fellow GreenSockers. See you around the forums. Edit and Update (July 2020): I just made it to five years of hanging around the forum and you can read the continuation of my journey here. motiontricks.com Finally, without further ado, I introduce you to motiontricks.com - Craig (PointC) PS I made a little CodePen to commemorate my one-year forum anniversary. It’s how I felt before and after discovering the power of GSAP. Enjoy.
    12 points
  3. I know this thread is now over a month old, but I wanted to share a fork of Zach's pen. I made a 6-sided die from the cube. To simplify/clarify, the 3d rotations are moved out of the CSS. Hopefully this demo is useful to someone... https://codepen.io/creativeocean/pen/qBRbNwa
    9 points
  4. Hey, I created a simple example based on this CodePen from the GreenSock collection (I assume that this is what you're after). Now for some reason in Codesandbox this didn't worked so I had to create a repo and publish the sample in the corresponding GitHub page. This is the repo url: https://github.com/rhernandog/gsap-flip-react Here is the meat of the code: https://github.com/rhernandog/gsap-flip-react/blob/master/src/App.js Here is the live sample: https://rhernandog.github.io/gsap-flip-react/ Based on your initial post in the thread, I take that you want to remove the elements from the DOM when they're animated out and mount them back, once they're animated in. Unfortunately, for work reasons I don't have enough time to add such feature to this sample, but this should be a good starting point for it and hopefully it will help you. When I have more time in the future I hope to be able to improve this sample. Happy Tweening!!!
    9 points
  5. 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!
    8 points
  6. 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 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 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.
    8 points
  7. Looping back with a codepen. ✨ https://codepen.io/cassie-codes/pen/zYdxopE
    8 points
  8. Hi Sandy! You're animating <g> elements, so you're scaling a container. You need to get more granular and target the shapes. And I would set the transform origin ahead of time. For example, your circles could be like this. gsap.set("#circles circle", { transformOrigin: "50% 50%" }); ... .from("#circles circle",{opacity:0,scale:0,duration:2}) But it would probably better to just give your circles the same class name to make target easier. <g> <circle class="circles st6" cx="775.79" cy="197.35" r="5.81"/> <circle class="circles st6" cx="370.82" cy="55.65" r="5.81"/> <circle class="circles st7" cx="595.77" cy="70.42" r="5.81"/> </g> The zigzag could be done with the DrawSVGPlugin, or maybe by animating a clipPath. For beginners, I would recommend checking out @PointC's MotionTricks and @Carl's CreativeCodingClub.
    8 points
  9. Ooh, this was a fun experiment. I just pasted the SVG <path> data string into the CustomEase editor to convert it, then copied the results into a CustomEase that's used in an animation of the dot's "y" value to -140 because that's the tallest peak and BOOM: https://codepen.io/GreenSock/pen/abWLgwY?editors=0010 🎉 That'll be much more performant than that StackOverflow post thing that constantly loops to try to find the closest x value.
    8 points
  10. oh, ok, probably best to continue in that thread. It was very nice for @Ali Manuel to help you but we don't typically have the capacity to rebuild other people's projects on demand with new specifications. While I'm here though I believe making this full-screen will require some significant changes to the CSS and it's not really something that you would do with GSAP. If you have a question related to GreenSock animation please don't hesitate to ask.
    8 points
  11. 🤔 Well, I'm also not sure what the desired outcome is here. But since you are moving things in a 3-dimensional space and using the PerspectiveCamera here, there is no need to 'apply' a parallax effect in any special way between different 3D elements - it is already there. If you'd want it to feel parallax(ish) in relation to the background, I guess you'd just have to make sure that your elements move faster than the background does. In the end what exactly you want to achieve comes down to camera positioning and movement of either the elements or the camera ( one might be better suited than the other, depending on what you want to do, I guess). Here is an example with ScrollTrigger's smoothScroll() helper-function included for the smooth-scrolling effect (first example pen on the .scrollerProxy() documentation page). Maybe that helps get in the right direction https://codepen.io/akapowl/pen/d48ab3a40f7f8614db9e6f460c331704
    8 points
  12. Okay, I think I get it. I made a couple demos for you. I used a wider path just so it's easier to see what's happening. You could use gradientTransform like in your 2nd demo, but I'm not sure that gets the desired result and I have no idea about browser compatability on that one. https://codepen.io/PointC/pen/a774175618d8c17aa07fcd59e28119ed My preference would be to animate the x1/x2/y1/y2 attributes. I'd also use objectBoundingBox (default) for the gradientUnits. In this case I find it a bit easier to use values from 0 → 1 rather than keep track of absolute coordinates when using userSpaceOnUse. https://codepen.io/PointC/pen/jOBwLWw Here's a fork of your pen with option 2. https://codepen.io/PointC/pen/cae50d4609e6e0e8a1b44811aa3d278d Hopefully that helps.
    8 points
  13. I remember these two canvas based examples not GSAP specific. But each impressive none the less yielding similar result. https://codepen.io/waaark/pen/VbgwEM https://codepen.io/Zaku/pen/JNxKKY
    8 points
  14. In case anyone else comes across this like I did, having trouble with GSAP animating from one clip-path to another: make sure both clip-paths are using the same units, including any zero values. For example, trying to animate from this: clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%); to this: clip-path: polygon(-100% 0, -100% 0, -100% 100%, -100% 100%) wasn't working, but when I changed the zero values to percentages, it worked perfectly. // From clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%) // To clip-path: polygon(-100% 0%, -100% 0%, -100% 100%, -100% 100%)
    8 points
  15. lol. then that's probably a good place to stop. Unfortunately, the many folks like @PointC that have volunteered countless hours of their time over the years don't often have the luxury of making sure that every line of code is understandable to everyone of every skill level that passes by. The original poster was already satisfied so there's really no need for a committee to come in to do a code review. If you have an alternative, cleaner solution that would certainly be a welcome addition that would add value to the thread. If something isn't clear you can simply ask for an explanation. That's much better than calling it "over-engineered" and yet somehow at the same time criticizing it for not being forward-thinking enough to handle an ad-server injecting an ad into the middle of it 🙄 It's great that you started in this thread by trying to help. That's exactly what makes this place so great. There's loads of opportunity to help people, offer solutions, be a part of the community, make it better, and have your own voice around here. Stay positive, offer solutions, and you'll be amazed at how much more fun it is around here and how much you discover along the way!
    8 points
  16. There actually is a wonderful codepen demo out there, that shows how that second example could be done - the creator made use of GSAP 3.5.1 for it. https://codepen.io/jakewhiteleydev/pen/VwaXZZV Edit: And I just found this one recreating the works-section of that first video-example of yours. https://codepen.io/eleanxrg/pen/abZmdqe
    7 points
  17. Hi and welcome to the GreenSock forums, thanks for providing the demo. it seems your animation is set up to start with the menu open and then close it. I forced the animation to it's end (closed state) using progress(1) at the end of the timeline. it also seems your css is set up so that the page loads with the menu items visible. I set the opacity to 0 in the css and toggle it back to 1 once the page is loaded using gsap.set(".menu-overlay", {opacity:1}) https://codepen.io/snorkltv/pen/PojVQLv?editors=0110
    7 points
  18. Hi @Sibteali Baqar Welcome to the forum. Looks like the problem is that your orbit path (#ellipse_1) is a filled path so the circle wouldn't animate along it as you expect. I've created a little demo using your SVG. The path you were using has no fill and a red stroke. See how the circle would orbit along the outside of that path? Probably not what you want here. I added a simple ellipse and converted it to a path to show you that everything is working correctly with the MotionPathPlugin. I'd think an actual ellipse would be the easiest method here. https://codepen.io/PointC/pen/576a99f87ecf0a9aea713b8beb894f00 Hopefully that helps. If you have additional questions, you'll need to create a demo. Just strip it down to the minimum like I did above and we'll be able to provide the most accurate and helpful answers. Happy tweening and welcome aboard.
    7 points
  19. Hey DoPhuongAnh, Welcome to the GreenSock Forum. It's a lot of fun: a color 'kaleidoscope'. https://codepen.io/mikeK/pen/powWwMm Happy scrolling ... Mikel
    7 points
  20. Hey DoPhuongAnh and welcome to the GreenSock forums. Here's how I'd break down this effect: Position all of the text, one after another, around a circle. You've already done most of this! The text will overlap at this point. Select all of the parts of the text for each segment (in the case of the site you linked that means each person's name). Store this for reference. Keep a reference of which part is currently active. Whenever the active segment changes, rotate the circle so that the active one is where you want it. Also animate the transparency of all of the segments that are off screen to 0. Animate the transparency of the segments next to the active one to a value like 0.7 or so. Animate the transparency of the active segment to 1. Hopefully that will help you get a bit further than where you are now
    7 points
  21. *** UPDATE 9/10/21 THIS PROMOTION HAS ENDED *** Hey All, I'm doing a test run of a special promotion for all Club GreenSock members (Green, Shockingly, and Business). If you've invested your money in the awesome bonus plugins and the ongoing support of the platform I'm happy to offer you 50% savings on Lifetime access to my comprehensive and extremely specialized GreenSock training available at www.CreativeCodingClub.com. For those that don't know, I've spent nearly a decade in these forums and 7 years working at GreenSock helping folks learn the ins-and-outs of the platform. The Creative Coding Club courses are in simplest terms a collection of all the tips and tricks I've learned throughout the years helping frontend developers excel at GreenSock animation and avoid common beginner pitfalls. Unlike typical "one and done" courses, the Creative Coding Club is a subscription-model that gives you access to 5 premium courses PLUS new lessons every week (over 120 now). If you've spent any time in the GreenSock docs or these forums you've probably encountered my videos and demos so I'm not going to bombard you with a hard sell... just a killer deal: 50% off a Lifetime Subscription to Creative Coding Club (exp 9/8/21) To get this deal you just have to send me a Personal Message (PM) through the GreenSock site requesting a coupon code. Once logged into the GreenSock site follow these instructions Once I receive your message I'll confirm your Club GreenSock membership status (by viewing your profile) and send you a coupon code. I'll do my best to get it to you quickly (within 2-12 hours), but understand I do sleep and work Please review the following before purchase: This promotion is being run by Creative Coding Club. GreenSock isn't involved with the selling, maintenance, or delivery of this training. I offer a 28-day 100% money back guarantee. Just send me a note requesting a refund and it's yours. This offer is only available to new Creative Coding Club students. This offer is only available to people with an active Club GreenSock membership. This offer is only available to 1 member of a Multi-Developer Club GreenSock account. Contact me for info on team plans. You must request your coupon prior to 9/8/21. Coupons must be used before 9/15/21. Let's keep this simple. If you're a Club GreenSock member and ready to commit to mastering the art of GreenSock animation through my Creative Coding Club courses and weekly lessons, send me a personal message (as outlined above) and I'll get you going. Carl *** UPDATE 9/10/21 THIS PROMOTION HAS ENDED *** Not a Club GreenSock member? Well, this is the best time to join Club GreenSock, gain access to the bonus plugins, AND grab this incredible deal on my training. Remember, to get 50% off my training you must be a Club GreenSock member prior to enrolling. If you're not a Club GreenSock member but still would like me to guide you on your training journey I have a little something for you too. Save 20% on 1 Year or Lifetime memberships to Creative Coding Club (exp 9/8/21) Visit creativeCodingClub.com Select 1 Year or Lifetime plan Use code: gsforums at checkout
    7 points
  22. 🐸 https://codepen.io/GreenSock/pen/NWgrqpG?editors=1111
    7 points
  23. It is an interesting challenge...I got sucked in and whipped together this solution: https://codepen.io/GreenSock/pen/powvxNx?editors=0010 It basically uses a "y" animation to fake the scroll on sections that are taller than the window height. Is that what you were looking for?
    7 points
  24. my advice would be to create 1 timeline that does the full circle animation. tween the progress of that timeline to any value you want below the progressTl shows the syntax for tweening the progress of the circle's timeline (tl). you can create tweens any way you want and pass them the progress from the data attributes or whatever. https://codepen.io/snorkltv/pen/mdmYVEL?editors=1010 hopefully this points you in a direction that works for you. also, if you just need to animate the stroke of a circle, it would be much easier using DrawSVG:
    7 points
  25. I took a slightly different approach. I created a timeline for each circle. (I removed the groups as they aren't really necessary here). This way you can copy/paste in more circles and the animation will adjust accordingly. The secret with the scale tween is just the same as @elegantseagulls did. Make the duration half of the orbit and use a yoyo tween. https://codepen.io/PointC/pen/xxqrqby Hopefully that helps. Obviously you can adjust durations and eases to your liking. Happy tweening.
    7 points
  26. Hi @rcneil, You can just add another tween to take care of scaleing. Something like: gsap.to("#graphic-1", { duration:5, repeat: -1, ease: "none", scale: .2, yoyo: true, }); would do the trick. Looking at your animation, though here's a few things that could help simplify: 1: Combine the graphics into a single tween and loop/offset them with stagger 2: Comnbine motionPath and scale tweens into a single timeline Check it here: https://codepen.io/ryan_labar/pen/rNywmLX?editors=0010
    7 points
  27. @Jim Nayzium, A tween is just that, a single tween of one or more properties on a matched element or elements. A single element https://codepen.io/sgorneau/pen/wvJKVwK Multiple matched elements https://codepen.io/sgorneau/pen/GRWpVKV A timeline contains one or more tweens that have timing controls to dictate when they begin and end on the timeline. The can begin relative to the timeline (at absolute positions .. like 0, 5, 20) , relative to each other ("+=3", "-=2",), or at named labels ("someLable"). You would use a timeline where sequencing is necessary. A simple timeline https://codepen.io/sgorneau/pen/eYvpqYG A more complex timeline https://codepen.io/sgorneau/pen/gOmaVpO?editors=0010 As far as variables go, with regard to both a tween and a timeline, a variable is used to reference that tween or timeline in the future. It's that simple. Neither require a variable, but it can make life easier to store them in variables at times, especially timelines. With the "more complex" timeline above, things are good ... until I need to dynamically add to the timeline or manipulate in some way later (pause, reverse, etc.). There is no reference to it. Once I'm stop chaining methods .. it's done. But, if I hold it in a variable to reference, I can do a lot with it. I can add tweens to it based on future logic, I can reverse it. I can jump to a specific timestamp or label. https://codepen.io/sgorneau/pen/jOBbgqB
    7 points
  28. Not exactly like the example button - a lot less liquidy, but fun nonetheless! https://codepen.io/cassie-codes/pen/15d1e3d339a64bbed746895dff4990b9?editors=0010
    7 points
  29. My recipe a little trial & error and remove the ****. https://codepen.io/mikeK/pen/VwwwGrd Happy morphing ... Mikel
    7 points
  30. Greensock is incomparable, and I just wanted to say thank you @GreenSock and all parties involved for making this unbelievable product.
    7 points
  31. We're looking for an independent front-end developer and creative technologist who loves animation and helping people. A great fit for this role would be someone who is: Independent – This role has a lot of independence. We’re looking for someone who is comfortable with being given an idea and running with it. When they see something that needs improvement, they take initiative and either tackle it themselves or delegate it to the appropriate person. A community builder – A major part of this role is investing into the GSAP and animation community both in the GSAP forums and beyond. Responsible – With so much flexibility, this role requires someone who is reliable and able to hold themselves accountable as well as keep track of and prioritize tasks. This is not a job for someone who needs to be told each day what to work on. Entrepreneurial – GreenSock is a small company and the person in this role can help shape the direction of the company and its products. We’re looking for a sharp go-getter. Responsibilities Answer questions and facilitate these forums alongside the current moderators. Basically the "Chief Moderator" assisting the community. Generate and implement ideas to help people use GSAP more effectively. Tutorials, demos, videos, etc. Maintain the GreenSock website (content and the front-end code). Manage GreenSock's social media presence (Twitter, CodePen, Facebook). Help spread the word. Generate and implement ideas that make GreenSock more profitable and useful to the developer community. Required skills A solid understanding of the fundamentals of HTML, CSS, JavaScript, and SVG Experience with GSAP Debugging skills, especially JavaScript Some experience working with git, modules, and front-end frameworks Bonuses Expert-level experience in animating various web technologies (WebGL, SVG, Canvas, CSS, and libraries/frameworks) A good sense of visual design A good sense of motion design Experience teaching others Experience building communities Experience creating videos React experience Back-end experience, especially InVision Power Systems (IPS) experience Perks Flexible hours Work 100% remotely Interested? Email us your resume along with links to the animations you're most proud of working on. Or you can send me a direct message via the forums.
    7 points
  32. Hey @sorciereus, You could also use gsap.utils.shuffle() and stagger methods - and a motionPath and ... https://codepen.io/mikeK/pen/PobOwjN Happy landing ... Mikel
    7 points
  33. Hey, A little experiment on how ScrollTrigger and snap could be used. Just for info. Happy tweening ... Mikel
    7 points
  34. Heya! Remember I said I was working on a Svelte project that would be needing some GSAP? Here are some of the barebones examples I made using GSAP and Svelte. onMount: https://svelte.dev/repl/94885eb0f90045da934ed5fd9f7fdb2a?version=3.29.0 Transition directive: https://svelte.dev/repl/1f70e16d637945fa8788fafafb481454?version=3.29.0 In/Out directives: https://svelte.dev/repl/000b2f192c204cd799dbb4f6d70a1c21?version=3.29.0 Action directive: https://svelte.dev/repl/eb2f99e9f3324e25af4eaada0389eed6?version=3.29.0 Animation directive: (TO-DO soon). Hope this helps.
    7 points
  35. If you find yourself writing multiple tweens to animate one target, it may be time to reach for keyframes. Keyframes are a great way to move a target through a series of steps while keeping your code concise. Take a repetitive timeline like the one below — It can be simplified down nicely to fit into one tween: // timeline let tl = gsap.timeline(); tl.to(".box", { x: 100 }) .to(".box", { y: 100 }) .to(".box", { x: 0 }) .to(".box", { y: 0 }); // Array-based keyframes gsap.to(".box", { keyframes: { x: [0, 100, 100, 0, 0], y: [0, 0, 100, 100, 0], ease: "power1.inOut" }, duration: 2 }); We like to think of keyframes as a sub-timeline nested inside a tween. There are a few different ways to write keyframes. If you're a visual learner, check out this video. Keyframe Options Object keyframes - v3.0 This keyframes syntax lets you pass in an Array of vars parameters to use for the given target(s). Think of them like a sequence of .to() tween vars. You can use a delay value to create gaps or overlaps. The default per-keyframe ease is linear which you can override in individual keyframes. You can also apply an ease to the entire keyframe sequence. gsap.to(".elem", { keyframes: [ {x: 100, duration: 1, ease: 'sine.out'}, // finetune with individual eases {y: 200, duration: 1, delay: 0.5}, // create a 0.5 second gap {rotation: 360, duration: 2, delay: -0.25} // overlap by 0.25 seconds ], ease: 'expo.inOut' // ease the entire keyframe block }); Percentage keyframes - v3.9 This familiar syntax makes porting animations over from CSS a breeze! Instead of using delays and duration in the keyframe object, you specify an overall duration on the tween itself, then define the position of each keyframe using percentages. To be consistent with CSS behaviour, the default per-keyframe ease is power1.inOut which generally looks quite nice but you can override this in individual keyframes or on all keyframes using easeEach. gsap.to(".elem", { keyframes: { "0%": { x: 100, y: 100}, "75%": { x: 0, y: 0, ease: 'sine.out'}, // finetune with individual eases "100%": { x: 50, y: 50 }, easeEach: 'expo.inOut' // ease between keyframes }, ease: 'none' // ease the entire keyframe block duration: 2, }) Simple Array-based keyframes - v3.9 Just define an Array of values and they'll get equally distributed over the time specified in the tween. The default per-keyframe ease is power1.inOut, but you can override this by using easeEach. The Arrays do not need to have the same number of elements. gsap.to(".elem", { keyframes: { x: [100, 0, 50], y: [100, 0, 50] easeEach: 'sine.inOut' // ease between keyframes ease: 'expo.out' // ease the entire keyframe block }, duration: 2, }) Easing keyframes Easing is integral to animation and keyframes give you a huge amount of flexibility. Percentage keyframes and Simple keyframes allow you to control the ease between each of the keyframes with easeEach. See the Pen Keyframe easing by GreenSock (@GreenSock) on CodePen. With Object keyframes and Percentage keyframes you can drill down and add different eases into individual keyframes. See the Pen Bounce Party with GSAP keyframes, by GreenSock (@GreenSock) on CodePen. You can even combine multiple easing properties, keyframes and normal tween values. 🤯 gsap.to(".box", { keyframes: { y: [0, 80, -10, 30, 0], ease: "none", // <- ease across the entire set of keyframes (defaults to the one defined in the tween, or "none" if one isn't defined there) easeEach: "power2.inOut" // <- ease between each keyframe (defaults to "power1.inOut") }, rotate: 180, ease: "elastic", // <- the "normal" part of the tween. In this case, it affects "rotate" because it's outside the keyframes duration: 5, stagger: 0.2 }); See the Pen keyframe easing by GreenSock (@GreenSock) on CodePen. Keyframe tips Both the Object keyframes and the Percentage keyframes behave similarly to tweens, so you can leverage callbacks like onStart and onComplete. gsap.to(".elem", { keyframes: [ {x: 100, duration: 1}, {y: 200, duration: 1, onComplete: () => { console.log('complete')}}, {rotation: 360, duration: 2, delay: -0.25, ease: 'sine.out'} ] }); gsap.to(".elem", { keyframes: { "0%": { x: 100, y: 100}, "75%": { x: 0, y: 0, ease: 'power3.inOut'}, "100%": { x: 50, y: 50, ease: 'none', onStart: () => { console.log('start')} } }, duration: 2, }) We hope this has helped you get your head around keyframes - if you have any questions pop over to our forums. Happy tweening!
    6 points
  36. Hey @tolka you can do something like this 🙂 https://codepen.io/nicofonseca/pen/badf67592921f8951b6366ce06367181
    6 points
  37. Yep. Here's how you can leverage the MorphSVGPlugin to animate along a motion path. Animate Motion Path (codepen.io)
    6 points
  38. Hey @ThePixelPixie you can do something like this. Send the color you want to the function in order to animate https://codepen.io/nicofonseca/pen/ecfe39fecf06caa658105d4536da694a
    6 points
  39. Hey @whizzbbig I created a very small demo for you. https://codepen.io/nicofonseca/pen/fa5f335f743d719744d4f34243a2250f To create this type of animation you'll need a loading animation and an entrance animation. So, when all the images are loaded or the loading animation finished you'll start the entrance animation. And you don't need the setTimeout() 🙂 For the shape effect you shared, you can use:
    6 points
  40. I'm not sure, I entirely understand how you want things to behave, but you could also just get the offsetHeight of your footer and work with that. Something like this maybe https://codepen.io/akapowl/pen/68a5c9b3cdc68350bc027f5886774b4f
    6 points
  41. Hey there Louis, Welcome to the forums! You can use functional values to calculate units that are dependant on viewport width or other variables. https://codepen.io/GreenSock/pen/poPXrdX?editors=0011 end: `+=${elem.offsetHeight}` // won't be updated on refresh end: () => `+=${elem.offsetHeight}` // will be updated Also - if you want the animation values to update, make sure the ones you want to update are function-based values and set invalidateOnRefresh: true in the ScrollTrigger.
    6 points
  42. Hi @Neg you have a syntax error in "line 86", try: window.addEventListener("load", function(event) { init(); });
    6 points
  43. Hello there, as a very basic starting point, maybe this codepen demo here by @PointC can help. I remembered it from an older thread (unfortunatelly I don't remember which thread it was, though). https://codepen.io/PointC/pen/YYxvgQ Note, that it still uses old TweenMax, so updating it to GSAP3 would probably be a good idea - and not too hard to do. If you want the individual elements to remain horizontally leveled throughout the animation (as it looks on your picture), you would probably have to add some countering rotation to the individual elements themselves. I'd suggest first getting the animation itself up and running in a way that it works for you, to get a good feeling for what is important for it to work in the first place. Depending on what interaction you want to enable, you can then try hooking it up to Draggable and/or ScrollTrigger. If you'd want both (dragging and scrolling) to work on it, it would probably be best to utilize a proxy element - similar to what was discussed in these threads... ...and is being used in this demo (note: I'm talking about the concept - not saying it is the exact solution you should use). https://codepen.io/GreenSock/pen/ZELQqeJ If you'd then also want to trigger things on click of the arrows, maybe it would be best to use the ScrollToPlugin to scroll the page to a certain point that you'd have to calculate. That would obviously not be neccessary/doable, if you don't want to hook things up to ScrollTrigger - then you could just tween things to a certain point and update your draggable along the way (if you want it to be draggable at all). ..best don't try implementing everything at once - go piece by piece here. As you can see, the rabbit hole is veeery deep Happy digging into it.
    6 points
  44. Hey @codenub You can not have multiple elements in your markup with the same ID - so you might want to consider giving the elements you want to skew on scroll a specific class instead and target that class in your JS instead of an ID ( as it also is the case in the demo where you likely got the code for this ). Then you will still have the problem that there is a pinning section in between that can not be accommodated for since in your JS you are creating the horizontal-fake-scrolling ScrollTrigger after your skewing-ScrollTriggers. So you would either have to change the order of creation - or you could just add a refreshPriority: -1 to the skewing-ScrollTrigger to make sure it will get considered later than the other ScrollTrigger and thus can accomodate for the pin-spacing. Happy scrolling ...oh, and welcome to the Forums!
    6 points
  45. The opposite direction maybe? Coming from random values and ended up at predefined values? This is how I'd do it - https://codepen.io/GreenSock/pen/YzZrEPV note, I've updated to new GSAP3 syntax and changed the CDN link too.
    6 points
  46. Alternately - you could use masking and SVG paths for the fluid part. Here's a liquid morphing demo - (a lot of it is in the setup) @PointC has a great article here if you want to dig in. https://codepen.io/osublake/pen/BYwgBg And a much simpler liquid mask without morphing https://codepen.io/cassie-codes/pen/RwRORqB?default-tab=result&theme-id=18940
    6 points
  47. Like this... const obj = { x: 0 }; gsap.to(obj, { x: 1, duration: 1.65, onUpdate() { console.log(obj.x); } }); Or this... const obj = { x: 0 }; gsap.to(obj, { x: 1, duration: 1.65, onUpdate() { console.log(this.targets()[0].x); } }); Note that's assuming the obj is not an element.
    6 points
  48. Here's an pen using an older version of gsap, but the logic is still relevant. https://codepen.io/osublake/pen/rZOGZy
    6 points
  49. Hi, Yeah, responsive vertical accordions are not the simplest thing, but they're not the hardest neither. It's just about wrapping your head around handling the content height changes and how GSAP works. This sample is not very elegant but it's a solid approach to this. I don't know if it's the best approach, but it works: https://codepen.io/rhernando/pen/ZbGeJd The code is quite commented so all the explanation and thought process is there. Hopefully is enough to get you started. Happy Tweening!!!
    6 points
×