Jump to content
GreenSock

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

Search the Community

Showing results for 'overwrite'.

  • Search By Tags

    Type tags separated by commas.
  • Search By Author

Content Type


Forums

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

Product Groups

  • Club GreenSock
  • TransformManager
  • Supercharge

Categories

There are no results to display.


Find results in...

Find results that contain...


Date Created

  • Start

    End


Last Updated

  • Start

    End


Filter by number of...

Joined

  • Start

    End


Group


Personal Website


Twitter


CodePen


Company Website


Location


Interests

  1. Hi! This is a strange one. I assume it's because the tweens in the third timeline are conflicting with the second timeline but I can't quite pin it down, and as you say - it only occurs when you scroll quickly. I tried overwrite true and some other things, but no luck. It's annoying to suggest a fix without a reason, so apologies. But if I were in your position I would handle the section pinning in one timeline and the dot animation in another - then there aren't conflicts (however they're arising and however strange...) https://codepen.io/cassie-codes/pen/89e49c2af76bc0dca02749e75ce54dee?editors=0110
  2. I tweaked the helper function so that toIndex() will always go in the shortest direction: /* This helper function makes a group of elements animate along the x-axis in a seamless, responsive loop. Features: - Uses xPercent so that even if the widths change (like if the window gets resized), it should still work in most cases. - When each item animates to the left or right enough, it will loop back to the other side - Optionally pass in a config object with values like "speed" (default: 1, which travels at roughly 100 pixels per second), paused (boolean), and repeat. - The returned timeline will have the following methods added to it: - next() - animates to the next element using a timeline.tweenTo() which it returns. You can pass in a vars object to control duration, easing, etc. - previous() - animates to the previous element using a timeline.tweenTo() which it returns. You can pass in a vars object to control duration, easing, etc. - toIndex() - pass in a zero-based index value of the element that it should animate to, and optionally pass in a vars object to control duration, easing, etc. Always goes in the shortest direction - current() - returns the current index (if an animation is in-progress, it reflects the final index) - times - an Array of the times on the timeline where each element hits the "starting" spot. There's also a label added accordingly, so "label1" is when the 2nd element reaches the start. */ function horizontalLoop(items, config) { items = gsap.utils.toArray(items); config = config || {}; let tl = gsap.timeline({repeat: config.repeat, paused: config.paused, defaults: {ease: "none"}}), length = items.length, startX = items[0].offsetLeft, times = [], widths = [], xPercents = [], curIndex = 0, pixelsPerSecond = (config.speed || 1) * 100, snap = config.snap === false ? v => v : gsap.utils.snap(config.snap || 1), // some browsers shift by a pixel to accommodate flex layouts, so for example if width is 20% the first element's width might be 242px, and the next 243px, alternating back and forth. So we snap to 5 percentage points to make things look more natural totalWidth, curX, distanceToStart, distanceToLoop, item, i; gsap.set(items, { // convert "x" to "xPercent" to make things responsive, and populate the widths/xPercents Arrays to make lookups faster. xPercent: (i, el) => { let w = widths[i] = parseFloat(gsap.getProperty(el, "width", "px")); xPercents[i] = snap(parseFloat(gsap.getProperty(el, "x", "px")) / w * 100 + gsap.getProperty(el, "xPercent")); return xPercents[i]; } }); gsap.set(items, {x: 0}); totalWidth = items[length-1].offsetLeft + xPercents[length-1] / 100 * widths[length-1] - startX + items[length-1].offsetWidth * gsap.getProperty(items[length-1], "scaleX") for (i = 0; i < length; i++) { item = items[i]; curX = xPercents[i] / 100 * widths[i]; distanceToStart = item.offsetLeft + curX - startX; distanceToLoop = distanceToStart + widths[i] * gsap.getProperty(item, "scaleX"); tl.to(item, {xPercent: snap((curX - distanceToLoop) / widths[i] * 100), duration: distanceToLoop / pixelsPerSecond}, 0) .fromTo(item, {xPercent: snap((curX - distanceToLoop + totalWidth) / widths[i] * 100)}, {xPercent: xPercents[i], duration: (curX - distanceToLoop + totalWidth - curX) / pixelsPerSecond, immediateRender: false}, distanceToLoop / pixelsPerSecond) .add("label" + i, distanceToStart / pixelsPerSecond); times[i] = distanceToStart / pixelsPerSecond; } function toIndex(index, vars) { vars = vars || {}; (Math.abs(index - curIndex) > length / 2) && (index += index > curIndex ? -length : length); // always go in the shortest direction let newIndex = gsap.utils.wrap(0, length, index), time = times[newIndex]; if (time > tl.time() !== index > curIndex) { // if we're wrapping the timeline's playhead, make the proper adjustments vars.modifiers = {time: gsap.utils.wrap(0, tl.duration())}; time += tl.duration() * (index > curIndex ? 1 : -1); } curIndex = newIndex; vars.overwrite = true; return tl.tweenTo(time, vars); } tl.next = vars => toIndex(curIndex+1, vars); tl.previous = vars => toIndex(curIndex-1, vars); tl.current = () => curIndex; tl.toIndex = (index, vars) => toIndex(index, vars); tl.times = times; return tl; } Here's a fork with a slightly different color scheme https://codepen.io/GreenSock/pen/ZELPxWW?editors=0010 Better?
  3. Hi, In the animation, overwrite: true is not working with mask animation, works fine on other normal tweens. Works fine once animations are finished. Thanks for your help, always appreciated.
  4. Another option is to set overwrite: true on your tween which will cause it to find all other tweens of the same target(s) and immediately kill them. Basically, you were creating conflicting tweens.
  5. when I use a hex it works great, I was just wondering is it could do gradients as well I tried this but it failed badly :) gsap.registerPlugin(ScrollTrigger); window.addEventListener("load", function () { const scrollColorElems = document.querySelectorAll("[data-scrollcolor]"); scrollColorElems.forEach((colorSection, i) => { const prevColor = i === 0 ? "linear-gradient(180deg, #FBD786 0%, #f7797d 85%)" : scrollColorElems[i - 1].dataset.scrollcolor; ScrollTrigger.create({ trigger: colorSection, start: "top bottom", onEnter: () => gsap.to("body", { backgroundColor: colorSection.dataset.scrollcolor, overwrite: "auto" }), onLeaveBack: () => gsap.to("body", { background-image: prevColor, overwrite: "auto" }) }); }); });
  6. I'm not sure that helps but good to know. ...I still can't tell if gradients with data-scrollcolor think I have here ? data-scrollcolor="linear-gradient(90deg, #46224e 0%, #30184f 85%)" ) gsap.registerPlugin(ScrollTrigger); window.addEventListener("load", function () { const scrollColorElems = document.querySelectorAll("[data-scrollcolor]"); scrollColorElems.forEach((colorSection, i) => { const prevColor = i === 0 ? "linear-gradient(90deg, #46224e 0%, #30184f 85%)"" : scrollColorElems[i - 1].dataset.scrollcolor; ScrollTrigger.create({ trigger: colorSection, start: "top bottom", onEnter: () => gsap.to("body", { backgroundColor: colorSection.dataset.scrollcolor, overwrite: "auto" }), onLeaveBack: () => gsap.to("body", { backgroundColor: prevColor, overwrite: "auto" }) }); }); });
  7. is it possible for scrollColorElems to use gradients? html <div id="section1" class="mymodeheader-newmods mytop"><img class="hero__image" data-scrollcolor="linear-gradient(90deg, #46224e 0%, #30184f 85%)" ) src="https://parkviewhomes.info/energy-star/banner-top-01.svg" alt="" /></div> window.addEventListener("load", function () { const scrollColorElems = document.querySelectorAll("[data-scrollcolor]"); scrollColorElems.forEach((colorSection, i) => { const prevColor = i === 0 ? "#1b1521" : scrollColorElems[i - 1].dataset.scrollcolor; ScrollTrigger.create({ trigger: colorSection, start: "top bottom", onEnter: () => gsap.to("body", { backgroundColor: colorSection.dataset.scrollcolor, overwrite: "auto" }), onLeaveBack: () => gsap.to("body", { backgroundColor: prevColor, overwrite: "auto" }) }); }); });
  8. Hi, In terms of performance, both are equal. None of the options is going to put a lot of stress in either the CPU or the GPU. In terms of cleanliness, I'd prefer the first one, because if your GSAP instance is not going to change at any point in time, there is no need to create it over and over again, probably generating some overwrite and initial position problems. I always prefer to store my GSAP instance and use them as needed and only rely on creating GSAP instance on every event handler call when I have no other choice. Happy Tweening!!!
  9. I'm not totally sure what effect you're after, but keep in mind: If you want to link the animation progress directly to the scrollbar so that it acts like a scrubber, use scrub: true (or a number for smooth scrubbing). The way you're doing it is with callbacks, but keep in mind that if the user scrolls very fast, those could fire in very quick succession (potentially on the same tick), so make sure you factor that into your logic. For example, you'd probably want to set overwrite: true or overwrite: "auto" to prevent conflicts where you're firing off multiple tweens attempting to control the same properties of the same objects. Are your callbacks being fired in the way/order you expected? As for your setup, there are tons of ways you could do it but in my brain this seems like a timeline that scrubs to me (or it should be) rather than callbacks that fire animations at a specific spot. But again, that's totally up to you regarding the effect you want. I'm just usually a fan of creating the animation(s) once and then just controlling them. Typically that's best for performance too. But if you want these dynamic so that they start using the property values wherever they're at currently (mid-tween), it's unavoidable.
  10. Welcome to the forums, @Nzhiti! Since this is a somewhat common effect (a group of elements that seamlessly loop on the horizontal axis), I whipped together a helper function that makes it significantly easier. Here's a fork with it in place: https://codepen.io/GreenSock/pen/wvgOmBO?editors=0110 Here's the helper function isolated: /* This helper function makes a group of elements animate along the x-axis in a seamless, responsive loop. Features: - Uses xPercent so that even if the widths change (like if the window gets resized), it should still work in most cases. - When each item animates to the left or right enough, it will loop back to the other side - Optionally pass in a config object with values like "speed" (default: 1, which travels at roughly 100 pixels per second), paused (boolean), and repeat. - The returned timeline will have the following methods added to it: - next() - animates to the next element using a timeline.tweenTo() which it returns. You can pass in a vars object to control duration, easing, etc. - previous() - animates to the previous element using a timeline.tweenTo() which it returns. You can pass in a vars object to control duration, easing, etc. - toIndex() - pass in a zero-based index value of the element that it should animate to, and optionally pass in a vars object to control duration, easing, etc. - current() - returns the current index (if an animation is in-progress, it reflects the final index) - times - an Array of the times on the timeline where each element hits the "starting" spot. There's also a label added accordingly, so "label1" is when the 2nd element reaches the start. */ function horizontalLoop(items, config) { items = gsap.utils.toArray(items); config = config || {}; let tl = gsap.timeline({repeat: config.repeat, paused: config.paused, defaults: {ease: "none"}}), length = items.length, startX = items[0].offsetLeft, times = [], widths = [], xPercents = [], curIndex = 0, pixelsPerSecond = (config.speed || 1) * 100, snap = config.snap === false ? v => v : gsap.utils.snap(config.snap || 1), // some browsers shift by a pixel to accommodate flex layouts, so for example if width is 20% the first element's width might be 242px, and the next 243px, alternating back and forth. So we snap to 5 percentage points to make things look more natural totalWidth, curX, distanceToStart, distanceToLoop, item, i; // convert "x" to "xPercent" to make things responsive, and populate the widths/xPercents Arrays to make lookups faster. gsap.set(items, {xPercent: (i, el) => { let w = widths[i] = parseFloat(gsap.getProperty(el, "width", "px")); xPercents[i] = snap(parseFloat(gsap.getProperty(el, "x", "px")) / w * 100 + gsap.getProperty(el, "xPercent")); return xPercents[i]; }}); gsap.set(items, {x: 0}); totalWidth = items[length-1].offsetLeft + xPercents[length-1] / 100 * widths[length-1] - startX + items[length-1].offsetWidth * gsap.getProperty(items[length-1], "scaleX") for (i = 0; i < length; i++) { item = items[i]; curX = xPercents[i] / 100 * widths[i]; distanceToStart = item.offsetLeft + curX - startX; distanceToLoop = distanceToStart + widths[i] * gsap.getProperty(item, "scaleX"); tl.to(item, {xPercent: snap((curX - distanceToLoop) / widths[i] * 100), duration: distanceToLoop / pixelsPerSecond}, 0) .fromTo(item, {xPercent: snap((curX - distanceToLoop + totalWidth) / widths[i] * 100)}, {xPercent: xPercents[i], duration: (curX - distanceToLoop + totalWidth - curX) / pixelsPerSecond, immediateRender: false}, distanceToLoop / pixelsPerSecond) .add("label" + i, distanceToStart / pixelsPerSecond); times[i] = distanceToStart / pixelsPerSecond; } function toIndex(index, vars) { vars = vars || {}; let newIndex = gsap.utils.wrap(0, length, index), time = times[newIndex]; if (time > tl.time() !== index > curIndex) { // if we're wrapping the timeline's playhead, make the proper adjustments vars.modifiers = {time: gsap.utils.wrap(0, tl.duration())}; time += tl.duration() * (index > curIndex ? 1 : -1); } curIndex = newIndex; vars.overwrite = true; return tl.tweenTo(time, vars); } tl.next = vars => toIndex(curIndex+1, vars); tl.previous = vars => toIndex(curIndex-1, vars); tl.current = () => curIndex; tl.toIndex = (index, vars) => toIndex(index, vars); tl.times = times; return tl; } Usage const loop = horizontalLoop(".magnet", {paused: true}); next.addEventListener("click", () => loop.next({duration: 1, ease: "power1"})); prev.addEventListener("click", () => loop.previous({duration: 1, ease: "power1"})); Kinda fun, huh? And since I made all the movement based on xPercent, it's pretty much responsive, meaning you can alter the width and it should still work. here's a responsive version that makes the magnets always 20% of the width of that container (fitting nicely): https://codepen.io/GreenSock/pen/ZELPxWW?editors=0010 Does that help?
  11. Thanks for the reduced test case, @emsee. It is a bit of a tricky scenario. Solution: add these lines after you enable() the ScrollTrigger: g.scrollTrigger.refresh(); // re-calculates start/end gsap.set(g, {progress: g.scrollTrigger.progress, overwrite: true}); // sets the progress immediately instead of scrubbing, and overwrites the scrubbing tween // or you could replace the line above with: gsap.getTweensOf(g)[0].progress(1); When you enable() a ScrollTrigger, it automatically refreshes its start/end values to ensure they're correct, but timelines have to wait one tick to do that (I'll spare you the lengthy explanation, but just ask if you want one). Anyway, when you disabled the ScrollTrigger, it was at a progress of around 0.1 and then when you re-enable it, it must scrub to the new value (0.5) which is what you were seeing. To get the behavior you want, we've gotta basically make the progress jump there instead of tweening there. So we refresh() to ensure the start/end are set correctly, then we directly set the progress and use an overwrite: true in order to overwrite the scrubbing tween. Does that resolve things for you? https://codepen.io/GreenSock/pen/e500c7ad56ff97f35149ba60d194e521
  12. Yikes! I totally missed this thread, @lucky111. Sorry! Yes, I would definitely recommend the helper function in the docs, no question. It will perform significantly better because it doesn't constantly create a new tween over and over again on each tick to accomplish the scrubbing technique. Plus the non-GreenSock one didn't set overwrite: true, so it was creating a lot of conflicting tweens. You'd probably never notice functionally, but it's bad for performance. It gives you a lot more flexibility, as mentioned above - you can add any ScrollTrigger-related value to the object you pass in. You're not limited to "fast", "medium" and "slow" speeds - you can set ANY end value, like end: "+=2000" or whatever. But it still works with speed if you prefer that syntax. So overall it's more concise, it performs much better, and offers way more flexibility. I don't mean that as a knock on Chris's version - he built that when he was very new to ScrollTrigger. We all know what a genius he is with animation in general - I just have an unfair advantage of knowing all the ins and outs of ScrollTrigger (having built it)
  13. Hm, it sounds like there may be some logic flaws in your code, but it's difficult to troubleshoot without a minimal demo. But I'll offer a few thoughts: It looks like you're reusing the same timeline instance over and over again and doing a clear() after it's done. That's not "wrong", but it's probably simpler to just create a new timeline each time (or if you're not sequencing things or need to control multiple as a whole, just skip the timeline altogether and use tweens). Remember that by default, when you add() something to the timeline, it gets added to the END of it. So let's say the user clicks multiple times quickly, that means your code is just sequencing the new ones after the current ones that haven't finished yet. See the problem? Or do you clear() it before running each time too? Again, it's tough to know by just looking at a small excerpt. You don't need to timeline.add(gsap.to(...)) - that's just a long way of using the convenience methods: // long timeline.add( gsap.to( ... )); // short timeline.to(...); Be careful about creating conflicting tweens where they're fighting to control the same property of the same object. You can set overwrite: true to have GSAP immediately kill all other tweens of those targets, or overwrite: "auto" to only kill the individual parts (properties) of the tweens that overlap. I'd rewrite your first block of code like this: // OLD let itemScrollTimeLine = gsap.timeline({ paused: true, onComplete: resetTimeline }); function resetTimeline() { console.log('Completed'); itemScrollTimeLine.clear(); } if (itemList.indexOf(elem.id) >= 0) { let elemPos = (window.innerHeight - document.getElementById(elem.id).offsetHeight) / 2; itemScrollTimeLine.add(gsap.to(window, { duration: 1.2, ease: "power1.inOut", scrollTo: { y: elem, offsetY: elemPos } })); } else { itemScrollTimeLine.add(gsap.to(window, { duration: 1.2, ease: "power1.inOut", scrollTo: { y: elem, offsetY: 100 } })); } itemScrollTimeLine.add(gsap.to(elem, { ease: "power3.inOut", duration: 0.8, '--line-color-top': '#a18248' }), '>'); // NEW let itemScrollTimeLine = gsap.timeline(); itemScrollTimeLine.to(window, { duration: 1.2, ease: "power1.inOut", overwrite: "auto", scrollTo: { y: elem, offsetY: itemList.indexOf(elem.id) >= 0 ? (window.innerHeight - document.getElementById(elem.id).offsetHeight) / 2 : 100 } }); itemScrollTimeLine.to(elem, { ease: "power3.inOut", overwrite: "auto", duration: 0.8, '--line-color-top': '#a18248' }); You really shouldn't need to getChildren() and remove() any of them. Ditch that altogether. I think that was more of a band-aid that was attempting to cover over the fundamental problem which had more to do with your reusing the same timeline and continuing to shove new tweens one-after-the-other or maybe overwriting (or lack thereof). If you're still having trouble, it'll greatly increase your chances of getting a solid answer if you can provide a minimal demo. Thanks for being a Club GreenSock member! 🙌 Happy tweening!
  14. The only thing I would add is to enable overwrite.... gsap.defaults({ overwrite: "auto" }); And modifiers need to return a unit. return (pressedTop ? -skew : skew) + "deg"; But it still a little weird. Probably just need to tweak some stuff.
  15. Thank you , You are super helpful and always get to the heart of the solution but I get how I made this slightly confusing. Yes I understand the overwrite issue. I was getting this issue with other solutions, For some reason it is not working on codepen (I will add it but hopefully it doesn't confuse you), but my html works, I could film it if needed. The reason I don't want to reverse is because I want to fade out immediately and quickly once the mouse leaves the box. The only issue is I don't know how to do this without waiting for the mouseenter timeline finishes. ... Is there a way to on mouseleave: kill/ pause the previous timeline and tween to an opacity 0. if this still doesn't make sense I can explain it differently, just let me know! Thanks! https://codepen.io/jaykobz/pen/YzNQBVm?editors=0010
  16. I don't have time to troubleshoot your whole project with various other frameworks that may be causing conflicts, but I can offer a few pointers: Make sure you're killing things off properly between pages. I bet you keep starting a requestAnimationFrame() loop over and over again on every page and it's building up and causing a very heavy load with conflicts and redundancies. This is very inefficient: // not good function mouseMove(e) { gsap.to(jsCursorCircle, { duration: 0.5, x: xPos, y: yPos }); gsap.to(jsCursorDot, { duration: 0.2, x: xPos, y: yPos }); requestAnimationFrame(mouseMove); } Roughly 60 times per second you're creating a new conflicting tween and you don't have overwrite: true set, so you'll end up having a ton of tweens running on the same thing simultaneously. At the very least, set overwrite: true on your tweens. I wouldn't use a requestAnimationFrame() either. You only need to run things when the pointer moves, right? And how are you killing that loop when you go to another page? (see #1) It's a bit more efficient to reuse the same tween instance in a case like this where you're doing a TON of them constantly: const cTween = gsap.to(jsCursorCircle, {duration: 0.5, x: xPos, y: yPos}); const dTween = gsap.to(jsCursorDot, {duration: 0.2, x: xPos, y: yPos}); function mouseMove(e) { cTween.invalidate(); dTween.invalidate(); cTween.vars.x = dTween.vars.x = xPos; cTween.vars.y = dTween.vars.y = yPos; cTween.restart(); dTween.restart(); } I hope that helps.
  17. Yep, it was a very intentional change because: The old "auto" default overwrite mode sometimes caught people off guard, so opting in ensures people are aware of what's going on. Overwriting costs some overhead processing-wise, but in the vast majority of cases you don't really need to do any overwriting so it's just a waste to always do by default. Even when you've got two competing tweens running, the "last one wins" rule applies so people almost never notice it anyway. The only time it becomes a stumbling block is when the new tween has a shorter duration than the old tween. That wouldn't work because the 5th parameter is the value that you want mapped. But like you said, it's quite easy to get the result you want: const convert = gsap.utils.pipe( gsap.utils.clamp(0, 1000), gsap.utils.mapRange(0, 1000, 2, 0.5) ); console.log(convert(-100)); // 2
  18. Hmm, it seemed to hold up well in my quick tests, but I don't doubt you. Have you tried removing all the custom velocity-based stuff? My suggestion would be to roll-back and get it working with more basic features and then see where it breaks. Unfortunately, I don't have the time to jump in there and do deep debugging. Glad you liked the overwrite video though! @GreenSock can speak better to the change in overwrite modes for GSAP 3. I'm pretty sure the default of true requires less overhead and better performance. Carl ps. good to know you are previously Kreativzirkel
  19. Cool! Very good video! Especially the real life example at the end is important in my opinion to drive the point home. I love it! So my assumption is right for gsap 2: there was an automatic overwrite. I'm wondering why you chose to set it to false by default? `auto` as the defaults feels like it makes more sense. Back to topic: unfortunately I can still provoke unwanted jumps/flashes in your pen when scrolling back and forth heavily
  20. Clever approach! Thanks for the demo. I set overwrite:true on the progress tween in the fork below. https://codepen.io/snorkltv/pen/QWdwyVg?editors=0010 Does that help? For more on overwriting check out: Handling Conflicting Tweens
  21. Hi @ZachSaucier, Thanks so much for that article, it really helped. I've looped it and it's much cleaner. How should I go about adjusting individual animations? eg let's say I wanted to change the duration of the second brush. I'll add an additional class to that element and then overwrite it? What's the cleanest way to do that? https://codepen.io/spearquit/pen/NWbVGJM
  22. Hey girro. Some notes: You're making one of the most common GSAP mistakes of using the old duration parameter instead of including the duration inside of the vars. Putting it in the vars also lets you use defaults. Your repeatRefresh isn't doing anything. If you're going ahead with some ES6 stuff (like arrow functions) you may as well use more features (like using const and let). If you're using a timeline we recommend using the position parameter instead of the delay property. We recommend using the latest GSAP version (3.6.0 at the moment). On CodePen, you should only include the stuff within the body in the HTML section. And you can load external resources via the cog icon next to JS (and CSS). I recommend adding a background rect to each button so that they're easier to click. As for your question, often times it's good to create animations ahead of time and use control methods (this is one of the tips to animate efficiently). However, if you need the animations to overwrite each other like this usually it's best to create the animation when you need it. I'd approach it like so: https://codepen.io/GreenSock/pen/GRNLwXE?editors=0010 With that being said, your timelines currently affect both state-specific elements and shared elements (like the background shape). The way I have it set up above, the animation is killed off completely when an item is clicked, which could be an issue if users click between them fast enough. To remedy that, I'd separate the state specific animations from the shared animations. Only kill off the animation(s) related to the shared elements and let the ones for each state continue.
  23. We don't really have the capacity to do code optimization for every person who posts in these forums. Glancing at the code I'd try to avoid creating functions inside of a loop if possible. I bet you could avoid having to get the BoundingClientRect every mouse move event. You should also make sure to overwrite the previous animations in GSAP 3. There's really not that many calculations going on - GSAP can't remove the need to avoid calculations in every case.
  24. That's TypeScript misinterpreting things - it doesn't understand what "this" is in that context. Totally unrelated to GSAP/Draggable. You could work around that like: let drag = Draggable.create('.scrollbar', { type: "x", bounds: ".scrollbarContainer", throwProps: true, onDrag() { gsap.to('.gallery', {x: -drag.x, overwrite: true}); } })[0];
×