Jump to content
GreenSock

Leaderboard

Popular Content

Showing content with the highest reputation since 09/28/2021 in all areas

  1. 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.
    13 points
  2. Highlights: ScrollSmoother plugin for delicious, buttery-smooth scrolling that leverages native scroll. 💚 Observer plugin that greatly simplifies setup for reacting to various events across devices. gsap.quickTo() for frequent redirection to new values like mouse followers ScrollTrigger.normalizeScroll() solves a bunch of scroll-related annoyances across devices and browsers. "*=" and "/=" relative prefixes - multiply or divide the current value. Introducing ScrollSmoother.🥳 A shiny new plugin, exclusively for Club GreenSock members! ScrollSmoother makes it simple to add a buttery smooth vertical scrolling effect to your ScrollTrigger pages. Under the hood, ScrollSmoother leverages native scrolling which allows it to sidestep many of the accessibility annoyances that plague smooth-scrolling sites. No fake scrollbars, and no messing with pointer or touch functionality. See the Pen ScrollSmoother by GreenSock (@GreenSock) on CodePen. ScrollSmoother.create({ content: "#smooth-content", wrapper: "#smooth-wrapper", smooth: 1, // how long (in seconds) it takes to "catch up" to the native scroll position effects: true, // looks for data-speed and data-lag attributes on elements normalizeScroll: true, // prevents address bar from showing/hiding on most devices, solves various other browser inconsistencies ignoreMobileResize: true // skips ScrollTrigger.refresh() on mobile resizes from address bar showing/hiding }); Effects... ScrollSmoother will integrate seamlessly with all your scroll-triggered animations. but we've also added some bonus ScrollSmoother effects. ✨ speed - Great for parallax effects! It adjusts the speed at which an element moves vertically while scrolling through the viewport. A speed of 1 is normal speed, 2 is double speed, etc. lag - Add some lag* to gently flow elements behind the scroll before they ease back to their natural scroll position. * no seriously, trust us. It's the good kind of lag. <div data-speed="0.5"></div> <!-- half-speed of scroll --> <div data-speed="2"></div> <!-- double-speed of scroll --> <div data-speed="1"></div> <!-- normal speed of scroll --> <div data-lag="0.5"></div> <!-- takes 0.5 seconds to "catch up" --> <div data-lag="0.8"></div> <!-- takes 0.8 seconds to "catch up" --> Read the docs for all the juicy details, or pull up a seat and watch this short explainer video. Observer The brand new 3.5kb Observer plugin offers a super-flexible, unified way to sense meaningful events across all (touch/mouse/pointer) devices without wrestling with all the implementation details. Perhaps you want to respond to "scroll-like" user behavior which could be a mouse wheel spin, finger swipe on a touch device, a scrollbar drag, or a pointer press & drag...and of course you need directional data and velocity. No problem! Tell Observer which event types to watch (wheel, touch, pointer, and/or scroll) and it will collect delta values over the course of each requestAnimationFrame tick (debounced for performance by default) and automatically determine the biggest delta and then trigger the appropriate callback(s) like onUp, onDown, onDrag, etc. Look how easy it is to trigger next()/previous() functions based on when the user swipes up/down or uses their mouse wheel: Observer.create({ target: window, // can be any element (selector text is fine) type: "wheel,touch", // comma-delimited list of what to listen for ("wheel,touch,scroll,pointer") onUp: () => previous(), onDown: () => next(), }); Demo Notice there's no actual scrolling in the demo below but you can use your mouse wheel (or swipe on touch devices) to initiate movement so it "feels" like a scroll: Since ScrollTrigger leverages Observer internally for normalizeScroll(), we exposed it via ScrollTrigger.observe() so you don't have to load an extra file if you're already using ScrollTrigger. Excited? Why don't you observe this video or check out the docs (see what we did there?). gsap.quickTo() If you find yourself calling gsap.to() many times on the same numeric property of the same target, like in a "mousemove" event, you can boost performance by creating a quickTo() function instead. Think of a quickTo() like an optimized function tied to one particular numeric property, where it directly pipes a new number to it. Example let xTo = gsap.quickTo("#id", "x", {duration: 0.4, ease: "power3"}), yTo = gsap.quickTo("#id", "y", {duration: 0.4, ease: "power3"}); document.querySelector("#container").addEventListener("mousemove", e => { xTo(e.pageX); yTo(e.pageY); }); Mouse Follower Demo See the Pen gsap.quickTo() mouse follower by GreenSock ( @GreenSock) on CodePen. ScrollTrigger.normalizeScroll() and ignoreMobileResize Have you ever run into these problems?: Address bar on mobile browsers shows/hides and resizes the viewport, causing jumps When scrolling fast, a pinned element seems to shoot past for a brief moment and then jump to the correct pinned position (multi-thread synchronization issues) iOS jitter Overscroll behavior that seems impossible to prevent on iOS Inconsistent momentum scrolling across devices The powerful new normalizeScroll() functionality intercepts native browser scroll behavior and handles it on the JavaScript thread instead which solves the problems mentioned above on most devices (iOS Phones in portrait orientation still show/hide the address bar). To enable it, simply: ScrollTrigger.normalizeScroll(true); To prevent ScrollTrigger.refresh() from running (and recalculating start/end positions) when a mobile browser shows/hides its address bar, you can now do: ScrollTrigger.config({ ignoreMobileResize: true }); So easy! Read more in the docs. New "*=" and "/=" relative prefixes You've always been able to add or subtract from the current value, like: gsap.to(".box", { x: "+=100", // 100 more than the current value y: "-=100", // 100 less than the current value }); ...and now you can multiply or divide accordingly: gsap.to(".box", { x: "*=2", // double the current value y: "/=2", // halve the current value }); And more... GSAP 3.10 also delivers various bug fixes, so we'd highly recommend installing the latest version today. There are many ways to get GSAP - see the Installation page for all the options (download, NPM, zip, Github, etc.). Resources Full release notes on Github Full documentation Getting started with GSAP Learning resources Community forums Happy tweening!
    10 points
  3. Just to throw my two cents out there - some CodePen accounts to bookmark and/or follow. Talented coders that feature a ton of GSAP: Cassie Evans: https://codepen.io/cassie-codes Blake Bowen: https://codepen.io/osublake Carl Schooff: https://codepen.io/snorkltv Pete Barr: https://codepen.io/petebarr Steve Gardner: https://codepen.io/ste-vg Ryan Mulligan: https://codepen.io/hexagoncircle Tom Miller: https://codepen.io/creativeocean Chris Gannon: https://codepen.io/chrisgannon Darin Senneff: https://codepen.io/dsenneff Craig Roblewsky: https://codepen.io/PointC/ (this guy is awesome 🤣) It may not be exactly what you need, but there should some good inspiration in those accounts. Happy tweening.
    10 points
  4. OMG! I was about to get fired, ScrollSmoother saved my life
    10 points
  5. See the Pen box by GreenSock (@GreenSock) on CodePen. Highlights: Flip Plugin is no longer for members-only! - consider it an early Christmas present from us to you. 💚 CustomEase is now in the public downloads as well (and on the CDN)! 'Tis the season to be jolly. 🎁 Brand new Keyframe options that can drastically reduce the amount of code you must write. If you're used to CSS keyframes, you'll love this. Flip Plugin got a major overhaul and new features including batch() for complex scenarios. FLIP animations for everyone! 🥳 Flip plugin can give you some serious animation superpowers once you learn to think in terms of "FLIP" ("First", "Last", "Invert", "Play"). Here's a demo that explains the technique: See the Pen How GSAP&#39;s Flip Plugin Works by GreenSock ( @GreenSock) on CodePen. Sometimes you'll need to deal with state changes that you can't control, or reparenting of elements. Maybe a thumbnail image needs to transition to fill the viewport with position: fixed, or a grid of elements must get smoothly re-ordered within a flexbox container. This is where Flip Plugin shines! And now it's included in the public downloads and CDN! That's right, Flip Plugin isn't just for Club GreenSock members anymore (but seriously, if you haven't joined yet, what are you waiting for?). And for those who are members, don't worry - we've got something fun coming just for you in the future. Here's an example where a video that's in the flow of text seamlessly transitions into position: fixed in the corner when you scroll far enough: See the Pen Flip Video by GreenSock (@GreenSock) on CodePen. Even when the original position of elements could change - like in this spinning container, FLIP will handle the transition with ease. See the Pen Spinny flipz by GreenSock (@GreenSock) on CodePen. And here's a fan-favorite showing a grid of tiles you can filter by color and Flip smooths everything out: See the Pen Tiles by GreenSock (@GreenSock) on CodePen. Wanna learn about all the nitty-gritty details? Watch this video: New additions to the keyframe syntaxNew additions to the keyframe syntax Keyframes are a great way to animate a target through multiple steps while keeping your code nice and concise. You can think of them as a sub-timeline nested inside a tween Here's a reminder of the existing syntax. gsap.to(".elem", { keyframes: [ {x: 100, duration: 1}, {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 ] }); New options Percent-based keyframes This familiar syntax will make porting animations over from CSS a breeze! Instead of using delays and duration in the keyframes themselves, you specify the styles you want at certain waypoints during the animation, and just like CSS, if you omit a property from one of the keyframes the value will interpolate across that gap. gsap.to(".elem", { keyframes: { "0%": { x: 100, y: 100}, "75%": { x: 0 }, "100%": { x: 50, y: 50 } }, duration: 2, }) Array-of-values Just define an Array of values and they'll get equally distributed. So simple! And you don't need to make sure the Arrays are equal in length. Plenty of flexibility. gsap.to(".elem", { keyframes: { x: [100, 0, 50], y: [100, 0, 50] }, duration: 2 }) Demos 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. And more... GSAP 3.9 also delivers various bug fixes, so we'd highly recommend installing the latest version today. There are many ways to get GSAP - see the Installation page for all the options (download, NPM, zip, Github, etc.). Resources Full release notes on Github Full documentation Getting started with GSAP Learning resources Community forums FLIP Plugin docs More information about keyframes Happy tweening!
    9 points
  6. Looping back with a codepen. ✨ https://codepen.io/cassie-codes/pen/zYdxopE
    8 points
  7. 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.
    7 points
  8. Hey everyone! I've been working on a fun demo project called TweenPages to show how I do complex page transitions with GSAP in Next.js. I haven't shared it yet with anyone publicly until now. Would love to get some early feedback. Especially on the docs where I go into detail on the code side of things. Am I doing it right? Am I doing it wrong? Are there things I can improve? Fun! - https://tweenpages.vercel.app/ Docs - https://tweenpages.vercel.app/docs Code - https://github.com/johnpolacek/TweenPages Hope the project helps anyone who want to do GSAP animations like these on Next.js.
    7 points
  9. Hey @hastalavistababy welcome to the forum! `scale` is part of the `transform` property of CSS and it does not interact with the flow of your content, so if you do want it so that elements are pushed around when things are scaled I would use the property `fontSize` https://codepen.io/mvaneijgen/pen/ExoBoaz?editors=0110 I had to change your code a bit and had to set a fixed height on the `.logo` block, otherwise the block height would get recalculated based on the font size and you want it to stay at the bottom. Also know you can use the plugin SplitText to dynamically split text on the fly no need to manually split any text https://codepen.io/mvaneijgen/pen/XWVLVLm
    7 points
  10. Can't wait to see what you make pals!
    7 points
  11. Hi @glenhodges Welcome to the forum. Yep - just like @Cassie mentioned, morphSVG would make this pretty easy. Here's a quick version to show you the basics. https://codepen.io/PointC/pen/PoEGNjm MorphSVG is a Club GreenSock perk so you can check that out too. Happy tweening and welcome aboard.
    7 points
  12. Hi Yunus, That animation looks pretty complex, but I'm curious, so I'm going to attempt to build in this thread. 😃 There's probably some game concepts that could apply here. For example, the movement is just like a camera in a game. If you move down 100px, then you would move the world up 100px. That's all this demo is doing. https://codepen.io/osublake/pen/LYYJNmQ To start, I'm going to build a mouse controller to behave kind of like an analog joystick. The only difference is that we want a deadzone in the middle where no movement will register. If the mouse is inside the red circle, I'm going to ignore it, but if its' inside the green circle, I want get a magnitude of how far away from the center it is. Later on I will use the magnitude and angle to determine the speed and direction to move the world. Think of the magnitude kind of like a multiplier. If it's 0, there will be no movement. If it's 1, then it's going to move at full speed. Mouse controller (codepen.io) I'll be back later with more updates.
    7 points
  13. Hi, currently you are animating everything at the same time and based on the same trigger. As you already guessed you'd have to loop through the different elements and create animations/scrolltriggers for each element. Look at this JS code, particularly at the forEach portion and how the ScrollTrigger uses the element reference as the trigger: let banners = document.querySelectorAll(".banner") banners.forEach( (element) => { let background = element.querySelector(".background") let headings = element.querySelectorAll("h1, h2") let tl = gsap.timeline() .from(background, {backgroundPosition:"60% 0%", filter:"brightness(0.1)", duration:1}) .from(headings, {y:200, stagger:0.1}, 0) ScrollTrigger.create({ trigger:element, start:"top 90%", toggleActions:"play none none reverse", animation:tl }) }) I can highly recommend using the https://www.creativecodingclub.com subscription for a month to learn about these basics. The js code above is from that. My best, T
    7 points
  14. 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
  15. Just Added Width Expansion to my codepen above, feel free to adjust the Easing and Duration to match your Needs (Tranform X Y -50% doesn't seem to be working in this miniviewer, open codepen in another tab for correct display) https://codepen.io/blitzve0/pen/vYJGPaa
    7 points
  16. Hi @blizve0 Here's a fork of your demo with some custom hooks, which should optimize it tad bit more. Cursor Jelly Blob (codepen.io)
    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. Hey @Syed Azam, welcome to the forum! 💚 You can do something like this: https://codepen.io/nicofonseca/pen/e81138427318c2826079e5c3f2ac4d41?editors=1010 The trick to achieve the mask effect is move the parent element to a direction and they child element to the opposite at the same time. gsap.to(parentElement, { xPercent: -100, duration:1 }); gsap.to(childElement, { xPercent: 100, duration:1, }); I hope this can help you. 🙂
    7 points
  19. Welcome! In this article we’re going to cover GSAP's core fundamentals and animate some HTML elements. If that’s not your end goal, don’t worry. Whatever you plan to animate, whichever framework you use - the techniques and principles covered here will serve you well. If you just want to jump in, here are some handy links. Full documentation GSAP cheatsheet CodePen starter template Installation guide. Still with us? Wonderful, let's get started! What is "GSAP"? The GreenSock Animation Platform (GSAP) is an industry-celebrated suite of tools used on over 11 million sites, including more than 50% of award‑winning ones! You can use GSAP to animate pretty much anything JavaScript can touch, in any framework. Whether you want to animate UI, SVG, Three.js or React components - GSAP has you covered. The core library contains everything you need to create blazing fast, cross-browser friendly animations. This is what we'll be stepping through in this article. In addition to the core, there are a variety of plugins. You don’t need to learn them in order to get started, but they can help with specific animation challenges like scroll based animation, draggable interactions, morphing, etc. See an overview of the available plugins. Creating an animation. Let's start by animating an HTML element with a class of "box". gsap.to(".box", { x: 200 }) A single animation like this is called a 'tween'. This tween is saying "hey GSAP, animate the element with a class of '.box' to an x of 200px (like transform: translateX(200px))".
    6 points
  20. 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
  21. I don't have a specific article to refer you to, no. But here's some quick general advice: Filters are TERRIBLE for performance, especially in Safari. Avoid them if you can. In 95%+ of cases, performance issues are related to graphics rendering in the browser (unrelated to GSAP). So focus your energy on making it easier for the browser to render stuff quickly. Keep the area of change on the screen as tight as possible. The more pixels that must change on each tick, the harder it is on the browser. SVGs can be expensive to animate, especially if they're big (pixel real estate, not kb). Rendering bitmaps/raster images is fast, but SVGs are typically fabricating pixels on-the-fly via math (expensive). Set pointer-events: none wherever you can so that the browser doesn't have to worry about pointer event handling, bubbling, etc. But obviously you NEED pointer functionality on many things like buttons, links, etc. so you can't set pointer-events: none on everything. Avoid animating properties that affect layout like width/height/top/left. Instead, animate transforms wherever you can because they don't affect layout. When animating transforms, leverage GSAP's shortcuts wherever possible (like x, y, scaleX, scaleY, rotation) rather than generic string-based "transform" stuff. In other words, x: 50 is much better than transform: "translateX(50px)". Don't have animations running when they're totally outside of the viewport (invisible). It's a waste of resources If you are pushing the renderer hard in the browser, consider switching to something like PixiJS that can leverage <canvas> and WebGL. It's a headache to build, but it can be WAY faster at rendering. Never have CSS transitions/animations applied to elements that are also being animated with GSAP. Try using will-change: transform on elements that are tough on the renderer (big/heavy). Good luck!
    6 points
  22. You'd create a timeline instead of a single tween. Something like this should work. https://codepen.io/PointC/pen/zYpQVxj Happy tweening.
    6 points
  23. Let me just throw this one into the mix... ... which was based on this one by @PointC ... ... and also has some good advice by @Cassie that in the end using a clip-path might be more performat than using a mask as in the demos there. Happy tinkering!
    6 points
  24. But to do it the way you currently have it setup (no sprite sheet), you'd just make one small change. gsap.set(Div,{attr:{class:`icon${(i%2) + 1}`},x:R(0,w),y:R(-100,-150),z:R(-200,200)}); https://codepen.io/PointC/pen/popBLja/146de3244e0f33f09731512c7b81c796 Happy tweening.
    6 points
  25. Hi! Here my ScrollSmoother / ScrollTrigger experiment: https://lr9qb2.csb.app/
    6 points
  26. Hey Reinoud Adding will-change to smooth-content seems to fix the problem! #smooth-content { will-change: transform; }
    6 points
  27. Tried to make something incorporating my Japanese nationality for fun and for learning(focus was on creativity with absolutely zero care for code quality). Shout out to gsap, ive used gsap since AS2 days. it is THE BEST ANIMATION LIB ON THE WEB!!! As a former Creative Technologist I cant tell you how many projects ive incorporated some kind of gsap tween within. Here is my project: http://thepearlharborexperience.com/ Thank you
    6 points
  28. Hello @Mantrone The awesome Tom Miller aka @creativeocean made one looking just like that. That pen should serve as a pretty good starting point for you, trying to figure things out. https://codepen.io/creativeocean/pen/mdROBXx Edit: Oh, look, it's even the first pen on his profile/showcase on codepen 😅 https://codepen.io/creativeocean
    6 points
  29. This was kind of fun to make and borrows from some technique's I've seen @GreenSock discuss. It will most likely be a video lesson in ScrollTrigger Express very soon https://codepen.io/snorkltv/pen/eYyBooE?editors=0110
    6 points
  30. You can increase the height in your CSS. https://codepen.io/cassie-codes/pen/LYeZRmo/b6a261a3bc0d1170ff1e96e518731a23?editors=1100
    6 points
  31. The start and end are the same, so basically it enters and leaves (as well as enters back and leaves back) at the same point. toggleClass will toggle the class on each of enter/leave/enterBack/leaveBack, so the way I see it, it would add and remove the class at exactly the same points, which would result in the class never really being on the element - when it would be added, it would be removed at exactly the same time.
    6 points
  32. Unless the exported renders are more complex than what is being shown in your sprite sheet, I would think the following examples of math based approaches would be a better overall approach given what you’ve shown. https://codepen.io/osublake/pen/vdzjyg https://codepen.io/creativeocean/pen/JjWMqdv Or a simpler approach as @mikel has suggested above that you could use morphSVG within a timeline and utilize as many shapes as desired (or an array or path data directly in js) at any duration you choose. You can even use morphSVG within GSAP keyframes also, there are many creative options available when using morphSVG.
    6 points
  33. Just a fun alternative: https://codepen.io/mikeK/pen/yLPEwmM Happy tweening ... Mikel
    6 points
  34. The year is 2032 - Mikel and Cassie are still engaged in an endless battle to post the ultimate neon-tube demo
    6 points
  35. Hey @samwatts1988, do you mean this? #start{ // add a stroke stroke:#f49969; stroke-width:3px; } // and then tween the fill to transparent tl.to( '#start', { fill:'transparent', duration: 1.5, morphSVG: '#end' } ); And here another prep of the path: https://codepen.io/mikeK/pen/xxPWoyQ Happy tweening ... Mikel
    6 points
  36. ... and an example https://codepen.io/mikeK/pen/zYPEaPm?editors=1010 Nobody is perfect ... Mikel
    6 points
  37. Hey @Trynix, Combining two stagger tweens could be an alternative. https://codepen.io/mikeK/pen/JjOJGXB?editors=0110 Happy tweening ... Mikel
    6 points
  38. @Sunny Shah There are different ways. Here is a variant for a color change. https://codepen.io/mikeK/pen/GROpYbO Happy tweening ... Mikel
    6 points
  39. Did you mean "Buy them a Tesla"? 🤔 Yeah, @PointC is a rock star around here, as are @akapowl, @Carl, @elegantseagulls lately and really all the moderators. The staff too ( @Cassie & @OSUblake ). It's a dream team. 🙌
    6 points
  40. Glad to hear you are enjoying GSAP 3 Express! Currently the Free Trial of the course and a handful of bonus lessons get released over the course of a week. Students who want to learn more faster have the option of unlocking all my courses (over 130 lessons) for less than $10 a month. So far it has provided a nice balance of allowing thousands of students to learn GSAP for free while I still make some money (necessary). One of my lessons covers how to loop through multiple elements and assign them an animation. When you interact with that element its animation will play or reverse. If you choose to "open" an element we use a variable to track which element is currently open so we can close it https://codepen.io/snorkltv/pen/BaLWxLW In your approach you are creating an animation BEFORE you are applying your classes that you are using to select the target of the animation. When you add and remove classes it has zero impact on tweens that you created prior. We typically don't build out full feature requests here, but since you invested the time in providing a demo that was "ready to go" and this could make for a good lesson I made some modifications to account for your recent requests. It may be a bit much for someone just starting out but it builds off the the the techniques in the demo above. In the "mouseover" event I'm looping through ALL the menu items and reversing the animations of all the elements that are not being rolled over. This is really only necessary to accommodate your request that on initial load all menu items are fully visible. https://codepen.io/snorkltv/pen/vYeybEG?editors=0011 Note, I'm not adding or removing any classes but you can add that in if for some reason it's necessary. Hope this helps!
    6 points
  41. Hey @Daniyarkz, I've simplified this down to one loop so it's a bit easier to read for now, popped some comments in there too, hope it helps. https://codepen.io/GreenSock/pen/rNGxEvq?editors=0111
    6 points
  42. This is officially my favourite forum post so far. Thanks for starting my morning off with a chuckle @khairunnisaas
    6 points
  43. Hi Shutt, Do you mean something like this? A Pen by GreenSock (codepen.io)
    6 points
  44. Hello @jenda In that case you could just set up an extra ScrollTrigger for the pinning of the "19" with an endTrigger set alongside of the end to easily tell where it is supposed to stop pinning, and let the ScrollTriggers within the loop handle the rest. The pen below shows how you you handle the ScrollTriggers in the loop it 'timingwise', dependent on the height of each .content You can certainly do that in the onLeave callback as to be seen in the pen below - but don't forget do fade them back in onEnterBack. Happy scrolling! https://codepen.io/akapowl/pen/c5c949193e94621afc717cbce169bc96
    6 points
  45. Hi there Jenda, Welcome. ☺️ I'm not quite certain what you were trying to achieve with your first loop? Looks like you're looping through multiple containers but pinning the main one? I've removed that until you can tell me what your goal was! Here's a simplified loop pinning the numbers, is that what you were looking for? Positioning the first number would be down to styling things differently and pinning it for the duration - but again, I wasn't really sure what you were after. Hopefully this helps! https://codepen.io/GreenSock/pen/yLoYbpL
    6 points
  46. Hi @Anael_, welcome to the forum ! 💚 There are many ways to solve this and I created one for you 🙂. The problem when using position absolute is that you lose the .container dimension (width, height) and if the elements were not positioned with position absolute animation can get weird. So I created this, only animating the transform x, y, calculating the container dimensions and the left position of the element. https://codepen.io/nicofonseca/pen/XWareXV?editors=0110 I hope this helps 🙌
    6 points
  47. Hey Creek, If you want to handle logic within functions and pass parameters it may be best to use the methods that were made for that, like onComplete() or call() I'd also advise that you stay away from fancy arrow functions entirely until you have a better understanding of JS. Just stick with normal functions for now. (Some people may disagree with me here) But from experience teaching, there's less chance of getting confused about scope so it avoids unnecessary confusion. You can also create functions this way that are called from your timeline but instantiated elsewhere. This keeps your code nice and easy to read. I've put a playground together for you here. https://codepen.io/GreenSock/pen/VwzZWQm Also a note - I would consider myself experienced with JS and GSAP. I still have to put together minimal demos like the one above in order to test and understand things. When people ask questions here, the people that answer aren't necessarily drawing on knowledge that's immediately available. People helping have to: Read the post Read the code Extrapolate what the question is Read/reference the docs or search the forums for helpful threads Make a demo if one isn't provided Work out a solution Explain the solution It's quite time-consuming and a lot of the people in here do this voluntarily in addition to other work. You can help by keeping your posts as short as possible. Just outline the specific question and provide a minimal demo. Thanks
    6 points
  48. Hi @tibo78, when you use $('.whatever') jQuery will target all those elements from your page, so it is the same as document.querySelectorAll('.whatever'). So when you use those elements to animate with GSAP all are affected. You need to target only the elements you want to animate, you can do something like this: https://codepen.io/nicofonseca/pen/0aa7834959a64963e469a2d64e5328ec
    6 points
  49. New version of CreateJS came out then, so you need to specify the updated CDN link in your head. OLD to replace: <script src="https://code.createjs.com/createjs-2015.11.26.min.js"></script> NEW: <script src="https://code.createjs.com/1.0.0/createjs.min.js"></script> Or via Google's CDN > <script src="https://s0.2mdn.net/ads/studio/cached_libs/createjs_2019.11.15_min.js"></script> Once updated, should work fine.
    6 points
  50. Enhancement: I added the ability to define values in an Array for a more simplistic way to just go to certain values, split equally across the whole animation, like: keys: { scale: [1, 0.5, 1.5, 1] } To be clear, that first value in the Array isn't a "from" value - it's the first destination. So, for example, if scale is 0 when that tween renders for the first time, it'd go 0 > 1 > 0.5 > 1.5 > 1 over the course of the whole tween. So with this syntax, it gives you the flexibility to use the percent-based values when you need them spaced non-evenly or to use a non-linear ease, -OR- use the shorter Array-based syntax for evenly-spaced linear values. 👍 I'm not set on the "keys" name, so if anyone has a better idea, I'm all ears. New plugin: gsap.registerPlugin({ name: "keys", init(target, vars, tween) { let tl = this.tl = gsap.timeline(), add = (prop, obj) => { let a = [], p; if (Array.isArray(obj)) { obj.forEach((value, i) => a.push({t: (i + 1) / obj.length * 100, v: value})); } else { for (p in obj) { p === "ease" || a.push({t: parseFloat(p), v: obj[p]}); } a.sort((a, b) => a.t - b.t); } a.forEach((kf, i) => { let v = {ease: obj.ease || "none", duration: kf.t - (i ? a[i - 1].t : 0)}; v[prop] = kf.v; tl.to(target, v); }); tl.duration() < 100 && tl.to({}, {duration: 100 - tl.duration()}); }, p this.tween = tween; this.ease = gsap.parseEase(vars.ease || "none"); for (p in vars) { p === "ease" || add(p, vars[p]); } }, render(ratio, {tl, tween, ease}) { tl.progress(ease(tween.progress())); } });
    6 points
×