Jump to content
Search Community

Search the Community

Showing results for tags 'best practice'.

  • Search By Tags

    Type tags separated by commas.
  • Search By Author

Content Type


Forums

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

Product Groups

  • Club GreenSock
  • TransformManager
  • Supercharge

Categories

There are no results to display.


Find results in...

Find results that contain...


Date Created

  • Start

    End


Last Updated

  • Start

    End


Filter by number of...

Joined

  • Start

    End


Group


Personal Website


Twitter


CodePen


Company Website


Location


Interests

Found 6 results

  1. Hi! First-time poster, please let me know if I can do anything to improve future questions. In the attached pen, I'm tweening the card title to follow the cursor on mouseover. In isolation, it works as I would expect. However, the behaviour changes dramatically when any arbitrary tween is applied to the card containing the title. I often encounter similar difficulty to avoid undesired effects, as well as issues with things like stacking context. I think I'm missing something critical about where, when and how to create and use multiple tweens correctly. Any guidance would be much appreciated.
  2. Are you guilty of any of the most common mistakes people make in their ScrollTrigger code? Nesting ScrollTriggers inside multiple timeline tweens Creating to() logic issues Using one ScrollTrigger or animation for multiple "sections" Forgetting to use function-based start/end values for things that are dependent on viewport sizing Start animation mid-viewport, but reset it offscreen Creating ScrollTriggers out of order Loading new content but not refreshing Why does my "scrub" animation jump on initial load? Or my non-scrub animation start playing? Tip: How to make scrub animations take longer Navigating back to a page causes ScrollTrigger to break Note: There's also a separate article that covers the most common GSAP mistakes. Debugging tip: In many cases, the issue isn't directly related to ScrollTrigger, so it's helpful to get things working without ScrollTrigger/any scroll effects and then, once everything else is working, hook things up to ScrollTrigger. Nesting ScrollTriggers inside multiple timeline tweens A very common mistake is applying ScrollTrigger to multiple tweens that are nested inside a timeline. Logic-wise, that can't work. When you nest an animation in a timeline, that means the playhead of the parent timeline is what controls the playhead of the child animations (they all must be synchronized otherwise it wouldn't make any sense). When you add a ScrollTrigger with scrub, you're basically saying "I want the playhead of this animation to be controlled by the scrollbar position"...you can't have both. For example, what if the parent timeline is playing forward but the user also is scrolling backwards? See the problem? It can't go forward and backward at the same time, and you wouldn't want the playhead to get out of sync with the parent timeline's. Or what if the parent timeline is paused but the user is scrolling? So definitely avoid putting ScrollTriggers on nested animations. Instead, either keep those tweens independent (don't nest them in a timeline) -OR- just apply a single ScrollTrigger to the parent timeline itself to hook the entire animation as a whole to the scroll position. Creating to() logic issues If you want to animate the same properties of the same element in multiple ScrollTriggers, it’s common to create logic issues like this: gsap.to('h1', { x: 100, scrollTrigger: { trigger: 'h1', start: 'top bottom', end: 'center center', scrub: true } }); gsap.to('h1', { x: 200, scrollTrigger: { trigger: 'h1', start: 'center center', end: 'bottom top', scrub: true } }); Did you catch the mistake? You might think that it will animate the x value to 100 and then directly to 200 when the second ScrollTrigger starts. However if you scroll through the page you’ll see that it animates to 100 then jumps back to 0 (the starting x value) then animates to 200. This is because the starting values of ScrollTriggers are cached when the ScrollTrigger is created. See the Pen ScrollTrigger to() logic issue by GreenSock (@GreenSock) on CodePen. To work around this either use set immediateRender: false (like this demo shows) or use .fromTo()s for the later tweens (like this demo shows) or set a ScrollTrigger on a timeline and put the tweens in that timelines instead (like this demo shows). Using one ScrollTrigger or animation for multiple "sections" If you want to apply the same effect to multiple sections/elements so that they animate when they come into view, for example, it's common for people to try to use a single tween which targets all the elements but that ends up animating them all at once. For example: See the Pen ScrollTrigger generic target issue by GreenSock (@GreenSock) on CodePen. Since each of the elements would get triggered at a different scroll position, and of course their animations would be distinct, just do a simple loop instead, like this: See the Pen ScrollTrigger generic target issue - fixed with scoping by GreenSock (@GreenSock) on CodePen. Forgetting to use function-based start/end values for things that are dependent on viewport sizing For example, let's say you've got a start or end value that references the height of an element which may change if/when the viewport resizes. ScrollTrigger will refresh() automatically when the viewport resizes, but if you hard-coded your value when the ScrollTrigger was created that won't get updated...unless you use a function-based value. end: `+=${elem.offsetHeight}` // won't be updated on refresh end: () => `+=${elem.offsetHeight}` // will be updated Additionally, 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. Start animation mid-viewport, but reset it offscreen For example try scrolling down then back up in this demo: See the Pen ScrollTrigger reset issue by GreenSock (@GreenSock) on CodePen. Notice that we want the animation to start mid-screen, but when scrolling backwards we want it to reset at a completely different place (when the element goes offscreen). The solution is to use two ScrollTriggers - one for the playing and one for the resetting once the element is off screen. See the Pen ScrollTrigger reset issue - fixed with two ScrollTriggers by GreenSock (@GreenSock) on CodePen. Creating ScrollTriggers out of order If you have any ScrollTriggers that pin elements (with the default pinSpacing: true) then the order in which the ScrollTriggers are created is important. This is because any ScrollTriggers after the ScrollTrigger with pinning need to compensate for the extra distance that the pinning adds. You can see an example of how this sort of thing might happen in the pen below. Notice that the third box's animation runs before it's actually in the viewport. See the Pen ScrollTrigger creation order issue by GreenSock (@GreenSock) on CodePen. To fix this you can either create the ScrollTriggers in the order in which they are reached when scrolling or use ScrollTrigger's refreshPriority property to tell certain ScrollTriggers to calculate their positions sooner (the higher the refreshPriority the sooner the positions will be calculated). The demo below creates the ScrollTriggers in their proper order. See the Pen ScrollTrigger creation order issue - fixed by GreenSock (@GreenSock) on CodePen. Loading new content but not refreshing All ScrollTriggers get setup as soon as it's reasonably safe to do so, usually once all content is loaded. However if you're loading images that don't have a width or height attribute correctly set or you are loading content dynamically (via AJAX/fetch/etc.) and that content affects the layout of the page you usually need to refresh ScrollTrigger so it updates the positions of the ScrollTriggers. You can do that easily by calling ScrollTrigger.refresh() in the callback for your method that is loading the image or new content. Why does my "scrub" animation jump on initial load? Or my non-scrub animation start playing? Most likely the ScrollTrigger’s start value is before the starting scroll position. This usually happens when the start is something like "top bottom" (the default start value) and the element is at the very top of the page. If you don’t want this to happen simply adjust the start value to one that’s after a scroll position of 0. Tip: How to make "scrub" animations take longer The duration of a "scrub" animation will always be forced to fit exactly between the start and end of the ScrollTrigger position, so increasing the duration value won't do anything if the start and end of the ScrollTrigger stay the same. To make the animation longer, just push the end value down further. For example, instead of end: "+=300", make it "+=600" and the animation will take twice as long. If you want to add blank space between parts of a scrubbed animation, just use empty tweens as the docs cover. Navigating back to a page causes ScrollTrigger to break If you have a single-page application (SPA; i.e. a framework such as React or Vue, a page-transition library like Highway.js, Swup, or Barba.js, or something similar) and you use ScrollTrigger you might run into some issues when you navigate back to a page that you've visited already. Usually this is because SPAs don't automatically destroy and re-create your ScrollTriggers so you need to do that yourself when navigating between pages or components. To do that, you should kill off any relevant ScrollTriggers in whatever tool you're using's unmount or equivalent callback. Then make sure to re-create any necessary ScrollTriggers in the new component/page's mount or equivalent callback. In some cases when the targets and such still exist but the measurements are incorrect you might just need to call ScrollTrigger.refresh(). If you need help in your particular situation, please make a minimal demo and then create a new thread in our forums along with the demo and an explanation of what's going wrong. Still need some help? The GreenSock forums are the best place to get your questions answered. We love helping people develop their animation superpowers.
  3. With over 120,000 posts in the popular GreenSock forums, we've noticed some common mistakes that you'd be wise to avoid. We threw in a few tips as well. Here is a summary of the mistakes: Creating from() logic issues Using fromTo() when from() or to() would work Not setting ALL transforms with GSAP Not using xPercent and yPercent Recreating animations over and over Adding tweens to completed timelines Not using loops Importing things incorrectly Using CSS transitions and GSAP on the same properties Using the old/verbose syntax Creating from() logic issues It's usually smart to use .to() and .from() tweens instead of .fromTo() because they're more dynamic - they pull either the starting or ending values from whatever they happen to CURRENTLY be at the time that tween renders for the first time. It’s one of the tips in the article on animating efficiently. But be careful because that dynamic nature can bite you in a few scenarios. First, keep in mind that .from() tweens go from the provided value to the current value. Take a look at this example: See the Pen Illustrating .from() effects - Part 1 by GreenSock (@GreenSock) on CodePen. Try clicking it one time and letting it play. It works, fading in the element. Now try clicking it multiple times right after each other. The box stops showing up because it uses the current opacity as the end point which, if the animation has not completed, is some value less than 1. The fix for this is simple: use a .fromTo(). Alternatively you could create the animation beforehand and use a control method (we'll talk more about this approach later in this article). See the Pen Illustrating .from() effects - Part 1 by GreenSock (@GreenSock) on CodePen. Second, keep in mind that by default immediateRender is true by default for .from() and .fromTo() tweens because that's typically the most intuitive behavior (if you're animating from a certain value, it should start there right away). But if you create a .from() tween after a .to() tween affecting the same properties of the same object, try to figure out what will happen: const tl = gsap.timeline() tl.to(".box", {x: 100}); tl.from(".box", {x: 100}); You might expect the box to animate x from 0 to 100 and then back to 0. Or maybe you'd expect it to animate from 0 to 100 and then stay at 100. Let’s see what happens: See the Pen Illustrating .from() effects - Part 1 by GreenSock (@GreenSock) on CodePen. The box animates x from 100 to 100 and then back to 0. Why is that? By default .to() tweens wait to render until their playhead actually moves (it's a waste of CPU cycles to render at a time of 0 because nothing will have changed). But since from() has immediateRender: true, x jumps to 100 immediately on the current tick! Then it runs the .to() tween on the next tick (since it’s first in the timeline) and records the current starting value which is 100! So it animates 100 to 100 over 0.5 seconds. Then it runs the .from() tween which has the cached value of 0 as the end value. If you have several timelines affecting the same element, situations like this can be a little tricky to catch. So just be mindful of how things work when using .to() and .from() tweens. They’re very powerful but with power comes responsibility. A simple solution here is to set immediateRender: true on the .to() tween, or immediateRender: false on the .from() tween. The third situation is similar but involves repeatRefresh and repeats. Let’s say you have a situation where you want a looped animation that fades in some text and fades it out. You could create a timeline, use a .from() to fade in the text, then use a .to() to fade it out: const tl = gsap.timeline({repeat:-1}); tl.set(".text", { color: "random([green, gray, orange, pink])" }, 2); tl.from(chars, { opacity: 0 }); tl.to(chars, { opacity: 0 }); This will work just fine! Here’s the same thing but staggered using SplitText to make it look a little nicer: See the Pen Fade in and out text by GreenSock (@GreenSock) on CodePen. But this only randomizes the colors at the start. What if we want new random values each repeat? That’s where repeatRefresh comes in. Let’s add repeatRefresh: true to see what happens: See the Pen Random on Reset (wrong way) by GreenSock (@GreenSock) on CodePen. The animation plays correctly the first time but after that the elements don’t fade in a second time! Why is that? repeatRefresh uses the end values of the animation as the starting values of the next iteration. In this case, the opacity of our text elements are all 0 at the end. So when the animation gets to the .from() the second time around, the opacity animates from a value of 0 to a value of 0 since the tween is relative. What we want to do instead is always animate from a value of 0 to a value of 1 so here the easiest fix is to use a .fromTo(): See the Pen Random on Reset by GreenSock (@GreenSock) on CodePen. Now it does what we want. There are other solutions like using a .set() before the .from() but most often it’s easiest to just use a .fromTo() in cases like this. Using fromTo() when from() or to() would work If you can, it's better for performance, maintainability, and ease to use relative tweens like .from() or .to(). So don't use .fromTo() unless you need to. .fromTo() tweens aren't bad, but should only be used when needed. Not setting ALL transforms with GSAP If you are going to animate an element with GSAP, even the initial transform values (including on SVG elements) should be set with GSAP because it delivers better: Accuracy - The browser always reports computed values in pixels, thus it's impossible for GSAP to discern when you use another unit like % or vw in your CSS rule. Also, computed values are in matrix() or matrix3d() which are inherently ambiguous when it comes to rotation and scale. The matrix for 0, 360, and 720 degrees are identical. A scaleX of -1 results in the same matrix as something with rotation of 180 degrees and scaleY of -1. There are infinite combinations that are identical, but when you set transform-related values with GSAP, everything is saved in a perfectly accurate way. Performance - GSAP caches transform-related values to make things super fast. Parsing all of the components from a computed value is more expensive. If you are worried about a flash of unstyled content, you can handle that by using a technique that hides the element initially and then shows it via JavaScript as this post covers. Or you can set the initial styles with CSS rules and ALSO set them in GSAP. Not using xPercent and yPercent Did you know that you can combine percentage-based translation and other units? This is super useful if, for example, you'd like to align the center of an element with a particular offset, like {xPercent: -50, yPercent: -50, x: 100, y: 300}. We often see people use percent values in the x and y properties which is technically possible but can cause confusion at times. For example, if you set x and y to "-50%" and then later you set xPercent: -50, you'd see it move as if it's at xPercent: -100 because the x and xPercent both have -50%. Whenever you're setting a percentage-based translation, it's typically best to use the xPercent and yPercent properties. // Not recommended x: "50%", y: "50%", // Recommended xPercent: 50, yPercent: 50 Recreating animations over and over Creating your tweens and timelines beforehand has several advantages: Performance - Instead of having to create them right as they’re needed, you can do it ahead of time. Additionally, you need fewer instances of animations. Most of the time you’d never notice, but it’s good practice. Simplified logic - This is especially true when related to user interaction events. Freedom - Want to pause an animation when an event happens? Do it. Want to reverse an animation when the user does something? No problem. This sort of thing is much more difficult to handle when you create animations inside of event callbacks. Most of the time when you create animations beforehand, you will want to keep them paused until they’re needed. Then you can use control methods like .play(), .pause(), .reverse(), .progress(), .seek(), .restart(), and .timeScale() to affect their play state. Here’s a simple example: See the Pen Playing and reversing an animation on hover by GreenSock (@GreenSock) on CodePen. For more information related to creating animations beforehand, you can see the animating efficiently article. One exception to this rule is when you need things to be dynamic, like if the initial values may vary. For example, if you’re animating the height of the bars in a chart between various states and the user may click different buttons quickly, it’d make sense to create the animation each time to ensure they flow from whatever the current state is (even if it's mid-tween) like the demo below. See the Pen Playing and reversing an animation on hover by GreenSock (@GreenSock) on CodePen. If you're animating dynamically to a new position that's updated very frequently, you might want to consider the gsap.quickTo() method. Adding tweens to completed timelines A common pattern of mistakes that I’ve seen goes like this: const tl = gsap.timeline() tl.to(myElem, { x: 100 }); myElem.addEventListener("click", () => tl.to(myElem, { x: 300 }) ); Did you catch the mistake? If you add new tweens to a timeline that is already completed, they won’t be called unless you re-run the timeline. Almost always in these situations you should just use control methods for a previously created animation or create a new animation instead (not using an existing timeline) following the guidelines that we covered in the previous section. Not using loops If you want to apply the same effect to multiple elements (sections, cards, buttons, etc.) when a certain event happens to each one, you should almost always use a loop. For example, don’t use a selector like "button" when you want it to affect just one button. For example, if you wanted to fire an effect when each button is clicked: // BAD: immediately animates ALL buttons at once! gsap.effects.explode("button", { direction: "up", duration: 3 }); // GOOD: animation is specific to each button, and only when clicked gsap.utils.toArray("button").forEach(btn => btn.addEventListener("click", () => gsap.effects.explode(btn, { direction: "up", duration: 3 })) }); Inside of this loop, you can use a selector that is scoped to the given element so that you're only getting things INSIDE that element. For example: gsap.utils.toArray(".container").forEach(container => { let info = container.querySelector(".information"), silhouette = container.querySelector(".silhouette .cover"), tl = gsap.timeline({ paused: true }); tl.to(info, { yPercent: 0 }) .to(silhouette, { opacity: 0 }, 0); container.addEventListener("mouseenter", () => tl.play() ); container.addEventListener("mouseleave", () => tl.reverse() ); }); See the Pen Who's That Pokémon? - forEach example demo by GreenSock (@GreenSock) on CodePen. Importing GSAP incorrectly A common issue people face when using GSAP in a module environment is importing GSAP or its plugins incorrectly. Most of the time import errors error can be avoided by thoroughly reading the relevant parts of the installation page. I won't copy all of the details into this post, but be sure to make use of that page if you're facing any sort of import error. It even has a very handy GSAP install helper tool that can generate the correct import code to use in most environments. Using CSS transitions and GSAP on the same properties You should definitely avoid having CSS transitions applied to elements that you're animating with GSAP. That's terrible for performance because the browser would constantly be interrupting things. For example, let's say you animate width to 500px from 100px. On every single tick (requestAnimationFrame), GSAP would set the interpolated value but the CSS transition would basically say "NOPE! I won't let you do that yet...I'm gonna transition to that new value over the course of ____ seconds..." and it'd start interpolating. But on the very next tick, GSAP would set a new value and CSS transitions would interrupt and start over again, going to that new value. Over and over and over. That would not only add a bunch of stress to the browser, but it'd slow things down regarding the overall timing of the animation. For example, if the GSAP tween has a duration of 1 second and the CSS transition is also set to 1 second, that means it'd stop moving after TWO seconds! Using the old/verbose syntax Drop the Lite/Max I regularly see people using the old syntax even though they are loading GSAP 3. Old habits die hard. Even though the old syntax still technically works, the new modern GSAP 3 syntax is sleeker and simpler. Plus the old syntax won't be supported in GSAP 4 (which is far off in the future, but it's still a good idea to write future-friendly code). For example instead of using something that has Lite/Max in it, just use gsap: // old TweenLite.to() TweenMax.from() new TimelineMax() // new gsap.to() gsap.from() gsap.timeline() Use the string form for eases The shorter string form of eases requires less typing and lets you avoid extra import statements in module environments. // old Power2.easeOut Sine.easeInOut // new "power2" // The default is .out "sine.inOut" Duration belongs in the vars parameter Putting the duration inside of the vars parameter does require a bit more typing, but it makes things more readable and intuitive. GSAP’s defaults and effects are very helpful but you can’t make use of them if you’re putting the duration as the second parameter. // old gsap.to(elem, 1, { x: 100 }); // new gsap.to(elem, { duration: 1, x: 100}); // using GSAP’s defaults: const tl = gsap.timeline({ defaults: { duration: 1 } }); tl.to(elem, { x: 100 }); // no duration necessary! tl.to(elem, { y: 100, duration: 3 }); // easily overwrite the default value For a more full listing of changes in GSAP 3, check out the GSAP 3 Migration Guide. Numerical values don’t usually need to be strings For example if you want to set the x transform to 100 pixels, you don’t need to say x: "100px", you can just say x: 100. Simple! The only time when you need to pass numerical values as strings are if you need to change the unit (like x: "10vw") or pass in a complex value (like transformOrigin: "0px 50px"). The target of a tween can be a selector string I often see people do something like this: gsap.to(document.querySelectorAll(".box"), { x: 100 }); Or even with jQuery: gsap.to($(".box"), { x: 100 }); Both of the above will work but could be simplified by passing a selector string in as the target; GSAP will automatically use .querySelectorAll() to get a list of all of the elements that match. So the above can be written simple as gsap.to(".box", { x: 100 }); You could also pass in a complex selector string like ".box, .card" and it will select all boxes and cards. Or use an Array of elements so long as they are of the same type (selector string, variable reference, generic object, etc.). Conclusion So how'd you do? Is your GSAP code clear of these common mistakes? Hopefully you learned a few things. As always, if you need any help, the GreenSock forums are a fantastic resource. We love to help people develop their animation superpowers. If you're looking for another great learning resource, read how to animate efficiently! Now go forth and tween responsibly!
  4. Hi guys, I was just looking for a bit of advice/a bit of a discussion about the pro's and con's, and the way you guys handle classes when it comes to animating essential page elements. For example, if you're using GSAP to have a modal window speed in from the left, a full-screen overlay grey's out the background. At this point the first animation ends, as the user needs to do something (input an e-mail address, for example). After which the modal window zooms off, and the full-screen overlay is removed, and the page is usable again, which is a separate animation. In this example there were three states; the Initial State, the Interactive State, and the End State (I just made those up), and at each of these points, different classes need to be applied to each of the elements involved (the background overlay, the modal box, etc.). Baring that in mind, is it best to animate the classes, using ClassName, or to use the standard inline-styles, and maybe apply the new classes/remove old ones at the end of the animation with a set/onComplete? Are there sometimes benefits to doing one over the other? This is more a question about best-practice I guess, rather than a specific use-case. I've just been learning about BEM recently, and it's got me thinking a lot about how to most efficiently and develop maintainable projects. I'd really appreciate any opinions/takes on this matter, as I really love both the BEM style of development as well as the GSAP suite. Thanks guys.
  5. Hello, I am very new to GSAP, and relatively new to front-end web development in general. I am always trying to optimize my workflows and organize my code such that it is readable, extensible, DRY and all the other fun acronyms associated with best practice. I recently worked on a vanity project where I coordinated multiple animations with a single event (a click), and I came across two problems that might possibly cost me a rewrite: 1) GSAP animates using element-level styling, overriding my CSS 2) Coordinating multiple animations in a single event is non-trivial Has anyone posted a best-practice of using GSAP responsibly? I'm looking for something similar to how Hugo described the 7-1 pattern for SCSS (https://github.com/HugoGiraudel/sass-boilerplate). I know that with trial and error, I can figure this out, but with all the experience in the forum, what do others do? For a solution to (1) that I am considering: Try to always use ClearProps at the end of an animation, and replace the destination of the animation with a CSS class. Is this a pattern that makes sense, assuming the animation is a transition? For (2): I am a bit stuck. I have two components that I have built to be plug and play for re-usability. However, I want these animation components to now coordinate with each other. Are there general patterns to follow on how to achieve this?
  6. Hi. With using sprite sheets I wanted to just set the width, height and background-position properties in CSS and set the rest in javascript with TimelineLite or TimelineMax. Is there a best practice for this? I want to do something like in the following example: I set all the images to hidden to start then animate with fromTo as needed. I made an example here with 3 different colored boxes, which works great on all browsers. http://codepen.io/anon/pen/jbKWJg The issue I am having is that on a DoubleClick banner example using the same technique I am having problems when I restart the timeline just in IE10. When I call tl.restart, any of the fromTo images with immediateRender:false are visible from the beginning instead of having autoAlpha:0. Thanks in advance, Kurt
×
×
  • Create New...