Leaderboard
Popular Content
Showing content with the highest reputation since 02/22/2023 in all areas
-
Whew... 2023's been quite a ride so far. On november 24th, 2021, I bought an HTML and CSS course on Udemy. Coming from a career that I hated, it was a relief to find something that I actually enjoyed doing. HTML/CSS naturally led me to JS, then the whole thing flipped upside down with React and I was learning so many interesting things. Then came Node and I finally could make more ambitious things. I am not a designer and my creations never turned out the way I wanted, so I ended up moving away from the front end. But one day I found this website called Awwwards and I saw things that defied everything I've ever learned. Obviously it took me like 3 minutes to find a green library that looked like magic. And the craziest thing is that it appeared to be... Easy to use? 🤨 I spent a month trying out different things. Made a website that has 0 design cohesion (but cool and poorly performant animations) and learned a lot. Got invited to an interview this week. It went great and a couple of hours afterwards they called to tell me I was the one. A part of the interview was specifically about the website with the "interesting" animations that I created and they liked what they saw. I'll start next Monday. So yeah, thank you all for being awesome! P.S.: in due time I will explain to them how getting me into the Club GreenSock is in the company's best interest.8 points
-
Thanks so much for reporting this everyone - I've reached out to Prasanna from PrivJS, they manage our private repo. More information incoming. Thanks so much for you patience.7 points
-
Congrats @Lamonier - that's great news. 🥳 I believe the current exchange rate for GSAP assisted job hunting is a Club purchase and 100 forum answers. Congrats again. We hope to see you around the forum all the time now.6 points
-
You can log this in the function to get all the values or the draggable instance. See the Draggable docs for more info, onDrag: Function - A function that should be called every time the mouse (or touch) moves during the drag. Inside that function, this refers to the Draggable instance (unless you specifically set the scope using callbackScope), making it easy to access the target element (this.target) or the boundary coordinates (this.maxX, this.minX, this.maxY, and this.minY). By default, the pointerEvent (last mouse or touch event related to the Draggable) will be passed as the only parameter to the callback so that you can, for example, access its pageX, pageY, target, currentTarget, etc. This is only called once per requestAnimationFrame. https://codepen.io/mvaneijgen/pen/OJoBNMa?editors=11115 points
-
Welcome to the world of GSAP, @edhillis! Yep, it sounds like you're on the right track. We typically don't do this in these forums, but I wanted to get you going in the right direction so I took the time to whip together a quick demo that shows the concept. Obviously you'd replace the simple boxes with whatever shapes you want, but here's the general idea: https://codepen.io/GreenSock/pen/xxaPojz?editors=0010 Is that sorta what you were looking for?5 points
-
You mentioned a "jump" occurring (which you wanted to avoid), so that sounded like debugging. And it's a little unclear (at least to me) if you're trying to change the "end" value on the ScrollTrigger only after it already reached its end (maybe you're applying the .no-effect class from within the onLeave of the very same ScrollTrigger?) or if you're saying you want to create the ScrollTrigger initially with the end value conditionally dependent on whether or not .no-effect is on the <body>. That's why is incredibly helpful when you can provide a minimal demo that illustrates the problem you're trying to solve. In order to do pinning (and not have things overlap), pinSpacing is added in order to push the elements below the pinned element further down to compensate for the pinning distance. So let's say you pin the element for 500px. That means the pin-spacer adds a padding-bottom of 500px (pushing things down). If you get to the end of that ScrollTrigger and then kill() it (which is basically what once: true does), then of course that pinSpacing gets removed (as it should). Removing that 500px of pinSpacing would obviously shift things up. That's why Cassie is doing that work in the onLeave to adjust the scroll position to compensate for that shift. Can you help us understand the "why" behind your request? It's confusing for me because it sounds like you're asking how to change the end value of a ScrollTrigger that has already finished and you want to kill (so why alter the end value of a ScrollTrigger that's no longer in effect?). I glanced at your code and it sure looks like that's what you're attempting but I'm trying to understand your ultimate goal because that approach doesn't make logical sense in my head (but I'm sure it will once you explain your ultimate goal). When you provide a minimal demo that clearly illustrates the issue, it saves everyone a lot of time, back-and-forth, etc. because we can see things in context and can very quickly diagnose problems and offer solutions, often by just forking your demo and applying the fix and posting it here.5 points
-
Hi @JV10 welcome to the forum! The thing with ScrollTrigger is that it is just animating something on scroll, so the best thing to do is to remove it! This seems counter intuitive, but ScrollTrigger is just animating something on scroll, so just focus on the animation at first and only when you're happy with the animation add ScrollTrigger back in. So in my example I've just commented out ScrollTrigger to focus on the animation. What I've done is after the item is done animating from opacity: 0.25, animate it to opacity:0 and at the same time move the element .stats-list up by -50 pixels (this is just some random value, you should probably calculate how high the item is and use that value), but do that for each .stat-item element and with that do some fancy calculations for the position parameter to have that work in time with your staggers. The idea for using this logic came from an awesome tutorial our own @Carl did. All his videos are great even if you think you're never going to use it there is a lot of general knowledge to gain from them that you can use in your own projects! Sidenote: if you're using the same values over and over again for most of your tweens, you can set them up in your timeline as defaults (see comments in the pen). Also I don't know what the yoyo: true and repeat was doing in your staggers, so I've removed them. https://codepen.io/mvaneijgen/pen/PodJBpw?editors=00115 points
-
@Lamonier Congrats on the J.O.B! That's awesome! I opened that link expecting a disaster with random animations and all kinds of craziness but what you have built is epic! I'm currently re-building my portfolio and messing with some GSAP animations.. I can only hope my results look as cool as yours Good luck on the new chapter! 🥳5 points
-
I'm not sure I 100% understand what you want to happen, but as far as I understood things, I'd just create one ScrollTrigger that handles the pinning - as Mitchel already said, since your lottie svg is inside that section, and you are going to pin that section, there is no need to pin the lottie element, too. After that, create your LottieScrollTrigger with pin set to false, and the same trigger and start as the pinning ST, then in the ST you set up for the images to move up, just use the end of the .previous() ScrollTrigger as the start - that way you won't have to make the calculations mentioned by Mitchel, as you can let ScrollTrigger handle that for you. As for using yPercent vs. top - you could probably just keep your calculations as they are and swap out top with y for the tweened property and should be set for better performance already, which all in all would result in something like this. Is taht what you were going for? https://codepen.io/akapowl/pen/KKxajmY5 points
-
For these sorts of things, I usually just use: overflow: visible!important; on my SVG elements. It'd be a tough ask to try to figure out how to constrain/contain shapes in the viewBox automatically (does the height change, with the width, etc). Also, animating the viewbox can have some performance implications too.5 points
-
This looks amazing dude. This low level API looks pretty powerful, thanks for taking your time of showing this, the more I use GSAP the more I realize I need to learn how to use it properly... Marked as definitely solved. UPDATE: Implemented it and it's noticeably smoother than the proxy version plus now rotations are pretty precise.5 points
-
Hi @Lucacv Welcome to the forum Check ScrollTrigger free plugin and SplitText premium plugin4 points
-
As the Helper already mentioned, this forum isn't really for tutorial requests of any sort @NikzA - but in this case I remember Tom Miller crafting something quite similar together with Alex Trost on the Frontend Horse / TrostCodes Twitch-Stream. https://www.twitch.tv/trostcodes You can re-watch that episode over on Youtube - maybe consider giving them a thumbs up for their work or leaving a nice comment on the video. It's not infinite and doesn't have the exact same effect as the website you referenced (the logic for that is for you to implement then, heads up; it will likely not be the easiest thing to do) - on that website they are probably not even relying on native browser scrolling or native browser elements but rather listenening to relevant events like the wheel or touch in combination with WegGL rendering - but at the very least this should be a good starting point. Hope it will help, good luck! https://codepen.io/creativeocean/pen/gOvYEgq4 points
-
Hello, @bntratox, containerAnimation was made to make what you are trying to do a breeze. Have a look at it in the ScrollTrigger docs. https://greensock.com/docs/v3/Plugins/ScrollTrigger containerAnimation Tween | Timeline - A popular effect is to create horizontally-moving sections that are tied to vertical scrolling but since that horizontal movement isn't a native scroll, a regular ScrollTrigger can't know when, for example, an element comes into view horizontally, so you must tell ScrollTrigger to monitor the container's [horizontal] animation to know when to trigger, like containerAnimation: yourTween. See a demo here and more information here. Caveats: the container's animation must use a linear ease ( ease: "none"). Also, pinning and snapping aren't available on containerAnimation-based ScrollTriggers. You should avoid animating the trigger element horizontally or if you do, just offset the start/end values according to how far you're animating the trigger. For changing classes on elements, you could either implement logic of your own in the ScrollTrigger's callbacks, like you do with your console.log, or you could see if ScrollTrigger's toggleClass could be helpful for your usecase, too. toggleClass String | Object - Adds/removes a class to an element (or multiple elements) when the ScrollTrigger toggles active/inactive. It can be either of the following: String - The name of the class to add to the trigger element, like toggleClass: "active" Object - To toggle a class for elements other than just the trigger, use the object syntax like toggleClass: {targets: ".my-selector", className: "active"}. The "targets" can be selector text, a direct reference to an element, or an Array of elements. Note that toggleActions don't apply to toggleClass. To have toggle class names in a different way, use the callback functions (onEnter, onLeave, onLeaveBack, and onEnterBack).4 points
-
GSAP is being blocked from downloading for me because you're referring to a GSAP file on an ad network and I have adblocker enabled.4 points
-
@b1mind You're a saint for sticking with me. Thank you! To summarize for any others that happen upon this thread. I'm using Astro.js framework. Hosting on Netlify. Astro uses Vite. My issue was two-fold. 1. I needed to follow @b1mind's advice and create a .npmrc file in my project root dir, not my system's home dir, as I had been doing. Secondly, I needed to add the GSAP auth code as an ENV in Netlify. Finally, I needed to edit my .npmrc file exactly as @b1mind described it (of course using whatever your Netlify ENV name is e.g. GSAP_TOKEN, NPM_TOKEN etc) 2. Now onto the Vite issue. @b1mind is also correct that the files need to a) be import from dist and b) have the filetype appended. This means do: import { DrawSVGPlugin } from "gsap/dist/DrawSVGPlugin.js"; and NOT: import { DrawSVGPlugin } from "gsap/DrawSVGPlugin"; Alright, that's it! Hope this helps someone else.4 points
-
Another little trick is to create everything at 0,0 and then start moving things. That way you know exactly where everything is located. Not always feasible, but works in some cases. YMMV4 points
-
The proxy technique is for when you want to make something draggable but do non-standard stuff with it, like infinite looping or leveraging the values elsewhere. The proxy is just an invisible element that you make Draggable and then in an onDrag() (or whatever), you can leverage the resulting x/y values for something else. Like if you need something to infinitely loop, the proxy might keep getting dragged all the way in one direction, but your "real" element loops back around because you're applying custom logic in the onDrag.4 points
-
Hi @andrejb welcome to the forum! Have you seen gsap.delaydCall()? https://greensock.com/docs/v3/GSAP/gsap.delayedCall() We would love to see a minimal demo with an example what you're trying to do, to better understand what could be the appropriate solution, but without that .delayCall() could be it. Hope it helps and happy tweening!4 points
-
Thanks for your patience, @james12345! After a lot of digging, I'm pretty confident I figured out the issue and got it resolved in the next release. You can preview that here: https://assets.codepen.io/16327/ScrollTrigger.min.js (you'll probably need to clear your cache) Can you reproduce any trouble when you drop that file in? It seems to resolve things for me. I can give you an explanation if you're curious, but it's rather complicated so I'll spare you if it's not something you really want to dig into I'm hoping that resolves everything. This is the one thing I was waiting to resolve before pushing out 3.11.5, so I'd love to hear back from you about whether or not you can reproduce any issues at all with the beta file. The GSAP core beta is here: https://assets.codepen.io/16327/gsap-latest-beta.min.js4 points
-
Hi @Roman S. is you're issue that your elements are not obeying the width you've set? That is because flexbox is trying to 'help', flexbox will automatically distribute the elements over the width you've given it. You can overwrite that using the flex-grow, flex-shrink and flex-basis property or the short hand flex: 0 0 "your width". You're right that this is a CSS issue and we like to focus these forums to just GSAP related questions. Still hope it helps and happy tweening! https://codepen.io/mvaneijgen/pen/GRXOvgo?editors=01104 points
-
Thank you very much for your reply ! The codepen and the videos are very useful, now I understand the flip technique !4 points
-
Hey there! So even if you're not using the FLIP plugin you'll need to use the FLIP technique. Here's a video A superfast video An article https://aerotwist.com/blog/flip-your-animations/ Annnd here's a vanilla JS logic example, with a GSAP tween handling the animation. https://codepen.io/cassie-codes/pen/xxpeBMb/abe8f76b97ba8f50037de78b745cd8e5?editors=0010 And a demo using FLIP plugin with a fixed/relative element https://codepen.io/cassie-codes/pen/gOvybdP Honestly, if I were you I would just use the Flip plugin as it handles a bunch of edge cases, makes your code tidier and abstracts away the messy bits. I think it's great to understand what's happening behind the scenes but no need to reinvent the wheel! Hope this helps!4 points
-
Thank you so so much! This worked perfectly! I've been scratching my head for a while trying to get it working. Very happy camper, here!4 points
-
i really can't follow much of the code in your demo (I don't know a lot about pixi and native canvas stuff) and it's been many years since I've touched the old GSAP syntax and bezier plugin. However, I was able to find and modify an example from the depths of the archives that might help you a bit conceptually. https://codepen.io/snorkltv/pen/mdGMWmB?editors=1111 The basic idea is that you add a bunch of bezier tweens to the same timeline that are offset using the position parameter. The left and right buttons use tweenTo() to tween the progress of the timeline by small amounts in positive or negative directions. For what it's worth, even back then these were fairly advanced concepts so I'm guessing it could be a much to bite off if you are new to the platform. I believe the newer MotionPath plugin would make this quite a bit easier as @mvaneijgen suggested.4 points
-
Hi, The issue is that you're adding the click handler to the menu button every time the code runs inside the GSAP MatchMedia instance. So every time you pass that particular breakpoint, the click handler is added again so you end up with multiple calls on a single click event. This seems to resolve that: mm.add("(max-width: 860px)", () => { /* Activate menu */ const mobileTl = gsap.timeline().reverse(); mobileTl /*...*/ const menuClickHandler = () => { menuOpen ? mobileTl.timeScale(4).reversed(menuOpen) : mobileTl.timeScale(1).reversed(menuOpen); menuOpen = !menuOpen; document.body.classList.toggle("lock-scroll"); navBar.classList.toggle("unlock-scroll"); }; // Add click handler menuBtn.addEventListener("click", menuClickHandler); mobileTl.pause(0).reverse(); return () => { mobileTl.pause(0).reverse(); menuOpen = false; document.body.classList.remove("lock-scroll"); navBar.classList.remove("unlock-scroll"); // Remove click handler menuBtn.removeEventListener("click", menuClickHandler); }; }); Hopefully this helps. Happy Tweening!4 points
-
Are you trying to calculate the size of the circle so that is grows always go the full size of the browser? If so check this tutorial our own @Carl did4 points
-
Hi @Lamonier Congrats on the new job and thanks for sharing the site. I really enjoyed the variety of animations. I'm going to have to use this as "Exhibit A" for when people ask me why I don't give certificates for my courses. Just the other day I replied to a potential student by saying that "as a former employer I would MUCH rather someone proved competence and effort through a small gallery of examples than be shown a certificate". Your site and experience landing the job is the PERFECT example of this. While I'm here, I'll also add that playing a bunch of videos that you paid for (even mine) is not much of an accomplishment. It takes no skill and doesn't prove anything. I guess that's the end of my Ted Talk as the kids say. ha. Keep up the great work! Carl4 points
-
This should be resolved now - feedback to verify would be appreciated 🤞 Fingers crossed. Thanks so much for your patience and understanding everyone. Hugely appreciated.4 points
-
Hi @eche and welcome to the GreenSock forums! I played with your example (nice job by the way! 👍 it's always great to see what GSAP users come up with 🎉) and the only thing I would change is not animating the height of the elements. Animating height/width/margin/padding/top/left etc is not really performant. Perhaps keep the animation on the Y transform and give the container an overflow hidden property, it should create the same effect, and after changing the z-index value to a lower one, move the element to it's final y position. Beyond that I don't see anything that I would change. Normally you can micro-optimize stuff for hours and you'll get super fancy code, but it might not be the most readable code and easier to follow. Always think that in six months you or someone else will look at this and has to get a clear idea of what's going on instead of going: "What the hell is going on here?!". Like @mvaneijgen says: "If it works it works" Happy Tweening!4 points
-
It looks like you just had your toggleActions wrong: // BAD toggleActions: "play none reverse none" // GOOD toggleActions: "play none none reverse" And this is not a valid start value: // invalid start: "-=200top top" I'm not sure if you meant "top top" or "200 top" or something else. But actually, if I were building this I'd approach it in a very different way: Put the color/backgroundColor into a data-color attribute on any element that I want to have this effect (a space-delimited value like "blue white") Grab all the elements that have that data-color attribute, loop through them and create a ScrollTrigger for each. When it activates, create a new tween that animates to its colors. Here's a demo with my strategy implemented: https://codepen.io/GreenSock/pen/OJoROgY?editors=0010 Benefits: You can easily add as many sections as you want - just slap a data-color value on it in the markup and BOOM, it works. It works more smoothly if you scroll very quickly. With your previous strategy, you had one animation for each section, and you were calling play()/reverse() on them when necessary...but they were controlling the same properties of the same object. Imagine a scenario where the user scrolls quickly, so one animation starts and is midway through when the other animation gets triggered. Now you've got two animations fighting for control of the same properties. Plus you may see a jump because let's just say (to make it simpler) we're animating a number from 0 to 100 in the first one, and 100 to 200 in the second animation. If the first animation is at 50 when the second one starts playing, you'd see it jump from 50 to 100 instantly (and go to 200). With my strategy above, a new tween gets created each time so that it's just taking the value from whatever it currently is to the new value. So everything is perfectly smooth every time. No fighting for control (it has overwrite: "auto"), no overlapping. I hope that helps.4 points
-
No, it only affects the path data. Never ever apply CSS transitions to anything that JavaScript is animating because not only is it horrible for performance, but it'll prolong all the effects because whenever JavaScript updates a value (which GSAP typically does 60 times per second), the CSS transitions interrupt and say "NOPE! I won't allow that value to be changed right now...instead, I'm gonna slowly make it change over time". So it disrupts things, adds a bunch of load to the CPU because you've now got CSS and JS both fighting over the same properties, but let's say you've got a 1-second tween and the CSS transitions are set to take 1000ms...that means what you intended to take 1 second will actually take 2 seconds to complete. You could probably just apply an x or y value to your tween to compensate for any shift. That'd be the simplest. Actually, the best thing is to just make your SVG artwork correct to begin with, as @PointC's article says, artwork prep is key. If your goal is to just prevent the SVG element from being outside the <svg> container and you want to automate that (rather than calculating it yourself), so that if it bleeds 20px off the left side, it moves 20px to the right, here's a relatively simple helper function that'll just apply an "x" or "y" transform to correct for any bleed off the edge: function keepInViewBox(tween) { let progress = tween.progress(); // record progress so we can revert it later tween.progress(1, true); // jump to the end so we can measure the end state let targetBounds = tween.targets().map(target => target.getBoundingClientRect()), // get an Array of all the target bounds svgBounds = tween.targets().map(target => (target.ownerSVGElement || target.parentNode).getBoundingClientRect()); // get an Array of all the SVG bounds tween.vars.x = i => { let left = svgBounds[i].left - targetBounds[i].left, right = svgBounds[i].right - targetBounds[i].right; return left > 0 ? left : right < 0 ? right : "+=0"; }; tween.vars.y = i => { let top = svgBounds[i].top - targetBounds[i].top, bottom = svgBounds[i].bottom - targetBounds[i].bottom; return top > 0 ? top : bottom < 0 ? bottom : "+=0"; }; tween.invalidate().progress(progress, true); return tween; } And here it is in action: https://codepen.io/GreenSock/pen/LYJZBaq?editors=0010 You just feed it the tween and it'll handle the rest. It doesn't do advanced things like scaling or centering, nor does it actually change the viewBox. I think in most cases, those things are probably unnecessary. I looked at your demo and I would definitely not do that setInterval() stuff to animate the viewBox. It's terribly inefficient and it's not synchronized with repaints and you get almost no control over easing, etc. It's far better to leverage the power of GSAP to do all that. Way more efficient, synced, and simple. I hope this helps!4 points
-
I find it is always easier to start with just the animation and remove ScrollTrigger from your setup at the beginning! This seems counter intuitive, but ScrollTrigger is just animating something on scroll, so just focus on the animation at first and only when you're happy with the animation add ScrollTrigger back in. What I've done is instead of adding a ScrollTrigger forEach .numb, create a timeline and add the scrollTrigger:{} object to that timeline (disabled it for now, to test the animation). Then on the timeline add the animation to each .numb, now all your .numb elements get animated at the same time, but with the stagger: 0.5 property each .numb gets animated in a staggered fashion. When you're happy with the animation you can remove the comments from the scrollTrigger:{} object and have it animate on scroll. Hope it helps and happy tweening! https://codepen.io/mvaneijgen/pen/OJoXWgJ?editors=10104 points
-
Hi @swaggle welcome to the forum! You've disabled the forking of your pen, which makes it harder for anyone to start helping you. I know there is some CSS tricks to make text transparent and use a background image on the text, but that seems to only work when it's 'normal' text without extra elements sprinkled in https://usefulangle.com/post/372/css-text-background-image https://codepen.io/mvaneijgen/pen/Exeygvz You can also look in to SVG text with a mask, but there are also cavities with that. https://codepen.io/daiquiri/pen/GWpPKP?editors=0100 Or give each letter their own distinct gradient based on the position they are in, this will require some more setup, but is the most robust and has the least caveats with browser support. Last week I wanted to do something similar, but my gradient was a lot simpler (just two colors), so I used the GSAP util interpolate() with the two colors and based on the position of the letter given each letter a color that was interpolated between the two colors. It is kinda a fake gradient, because each letter has just a solid color, but to me the effect was good enough. Hope it helps and happy tweening! https://codepen.io/mvaneijgen/pen/vYzKXpX4 points
-
Here's a plugin I whipped together for you that lets you animate scaleZ in GSAP 3: gsap.registerPlugin({ name: "scaleZ", priority: -1, // make it run last so that the other transforms are already applied init(target, value) { target._gsap || gsap.set(target, {x: "+=0"}); // force creation let cache = this.cache = target._gsap; if (!("scaleZ" in cache)) { cache.scaleZ = 1; let oldRender = cache.renderTransform; cache.renderTransform = (ratio, data) => { oldRender(ratio, data); // takes care of everything except scaleZ() target.style.transform += " scaleZ(" + data.scaleZ + ")"; } } this.add(cache, "scaleZ", cache.scaleZ, value); this._props.push("scaleZ"); }, render(ratio, data) { let pt = data._pt; while (pt) { pt.r(ratio, pt.d); pt = pt._next; } data.cache.renderTransform(ratio, data.cache); } }); And here it is in action: https://codepen.io/GreenSock/pen/KKxzjJX?editors=0010 That way, you just load that plugin once and it works with all your tweens without needing to add any onUpdates or anything. Does that help?4 points
-
Read this last night and this morning - very thorough and well articulated thank you. Everything making sense ☺️4 points
-
Just FYI - A timeline is the right choice if you have a sequence of animations that are playing one after another. In that case you'd add the ScrollTrigger to the timeline itself, like so - gsap.timeline({ scrollTrigger: { trigger: '.image', start: 'top bottom-=10%' } }) .from('.image', { opacity: 0, y: 80, ease: 'power2.ease', duration: 1, }) .from('.image-2', { opacity: 0, y: 80, ease: 'power2.ease', duration: 1, }) Happy tweening!4 points
-
Timelines/Tweens have absolutely no clue about "scroll distance units" - they work purely in time/duration/seconds. When you add a ScrollTrigger to a tween/timeline, think of it like that ScrollTrigger controls the playhead of that animation. When you apply a scrub value to a ScrollTrigger, all that does is force it to squish/stretch that animation to play inbetween the start/end scroll positions but it never actually changes its duration. So you could have a timeline that's 10,000 seconds long, but if you apply a ScrollTrigger to it with scrub: true and it the start/end scroll positions are only 50px apart, that super long timeline would still get squished to fit inside that 50px area, but if you check timeline.duration(), you'd still see 10,000. All the ScrollTrigger does is map the "progress" of the scroll position to the progress of the animation (the playhead). It's like watching a 2-hour video, but having the video playhead mapped to something that can move it around fast or slow. It's still a 2-hour movie, you're simply messing with the playhead. In your demo, the orange timeline is 1.5 seconds, so as soon as you add() that to the empty master timeline, the master timeline's duration is 1.5 seconds. Then, when we add the green tween and use the master timeline's duration(), it'll be 1.5 (matching the orange one). See why? Pretty much, but be careful not to mistakenly think that while the timeline is playing (or after it's built), it's grabbing that duration dynamically. It has nothing to do with playback. We're just creating the tweens/timelines (nothing is happening yet). So think of it purely in terms of JavaScript (don't think animation yet)... // insert 1.5-second orange timeline masterTl.add(orangeTimeline()); // the duration at this point is 1.5 console.log(masterTl.duration()); masterTl.to(".box.green", { rotation: 360, duration: masterTl.duration() // this resolves to 1.5 because it's invoked NOW (before our green tween is inserted) }, "<"); It's just a shorter way of doing this with a variable: // insert 1.5-second orange timeline masterTl.add(orangeTimeline()); let duration = masterTl.duration(); masterTl.to(".box.green", { rotation: 360, duration: duration }, "<"); Does that clear things up?4 points
-
Yep, exactly right. Also, you don't HAVE to wait 1 tick. The whole point is that there's no way for GSAP to know when you're done populating your timeline, so it has to wait a tick but since YOU know when you're done populating your timeline, you can manually trigger the refresh() at that point: https://codepen.io/GreenSock/pen/MWqyeRB?editors=10104 points
-
Hi @Nelou and welcome to the GreenSock forums! You can use preventOverlaps in your ScrollTrigger configuration: for (let i = 0; i < sectionArray.length; i++) { let tl = gsap.timeline({ scrollTrigger: { trigger: sectionArray[i], toggleActions: "restart none reverse none", start: '125% top', end: '125% top', preventOverlaps: true, // <- HERE id: 'animate '+containerArray[i]+" & "+containerArray[i+1], markers: true, } }); } You can read more about it here: https://greensock.com/3-8#preventOverlapsAndFastScrollEnd Be sure to watch @Carl's great video, which explains in details how it works. On top of that, right now you have a very convoluted setup. Since every section has only one child element, why are you running two loops? This could be simplified quite a bit by running just one loop and selecting the child element of each parent section. Also I'd try to create just a single ScrollTrigger instance for each section and run the animation on that. As for the resize event issue, it seems that the order you're creating the ScrollTrigger instances is what's causing those problems. I couldn't tell you exactly why, but creating the parent containers ScrollTriggers after the other ones seems to resolve the issue. Is a bit odd since the parent element is being pinned but without pin spacing, so that shouldn't mess the calculations ScrollTrigger makes for the other instances, but apparently it does, so this seems to work: // Run child sections loop first for (let i = 0; i < sectionArray.length; i++) { } // Run parent sections loop after for (let i = 0; i < containerArray.length; i++) { } Hopefully this helps. Happy Tweening!3 points
-
Yes, a minimal demo is always a big help, but these 2 demos should get you a little closer Path https://codepen.io/snorkltv/pen/gOvWdGy viewBox https://codepen.io/snorkltv/pen/xxjqEvj They are from my SVG Animation with GreenSock course which has over 30 lessons to help you understand how to design svgs, hand-code them, and animate them.3 points
-
The .fromTo() tween are great, but you will rarely need it. If you know where your element is and where you want it to you can just use .to() and .from() and because these are just simpler in nature they will behave them selfs better. It is not to say that .fromTo() is bad, but you have to know what you are doing to prevent these kinds of bugs. Also the best thing to do with ScrollTrigger is to remove it! This seems counter intuitive, but ScrollTrigger is just animating something on scroll, so just focus on the animation at first and only when you're happy with the animation add ScrollTrigger back in. Hope it helps and happy tweening! https://codepen.io/mvaneijgen/pen/xxaadrp?editors=00103 points
-
Hi @sawacrow what have you tried already? Personally I would create a timeline and add your animations to that timeline instead of having a ScrollTrigger for each element like in the example. The best thing to do with ScrollTrigger is to remove it! This seems counter intuitive, but ScrollTrigger is just animating something on scroll, so just focus on the animation at first and only when you're happy with the animation add ScrollTrigger back in. Here is a great tutorial how to start working with ScrollTrigger by not starting with ScrollTrigger. Just post back here with the animation you want to have and any follow up questions you have. Hope it helps and happy tweening!3 points
-
I think you'll want to use GSAP's Draggable for this (observer may work for this too). This thread may be helpful: Draggable Docs here:3 points
-
@Bureau Blanc yep that is always tricky. This example uses staggers and advanced position parameter to put thing on the timeline at the right place. I've used CSS selectors in my example, but you can easily convert them back to the JS slice method if you want. This kind of reminds me of a tutorial our own @Carl did which has a great visual explanation on how to create seamless loops, be sure to give it a watch! Hope it helps and happy tweening! https://codepen.io/mvaneijgen/pen/JjaOWjV?editors=00103 points
-
@rfldesigner welcome to the forum! Never animate your trigger element! ScrollTrigger uses the trigger element to do all its calculations! Right now you're moving this trigger yPercent: 100, so your trigger will never line up where you think it is! That could cause your issue. If that doesn't fix your issue I would go in to your sites files and disable some CSS and JS until you've found the culprit. If you can't reproduce the issue on codepen it is probably some other code that is interfering. Hope it helps and happy tweening!3 points
-
Hi @equ welcome to the forum! Thanks for you're message, we're working on a fix. See post above, you're not the only one. Sorry for the inconvenience it is causing!3 points
-
Well I'm actually glad you did it because it helped expose something we should fix. So thanks for making that "mistake" Glad it's all worked out now. Good luck with your launch.3 points
-
Fantastic. As for what caused it..that would require quite a long explanation. Pretty complex. I'll grossly oversimplify it by calling it a timing issue regarding updates. Let me know if you run into anything else.3 points
-
There was a major browser bug in iOS Safari that made the page entirely non-scrollable when setting the scrollTop to 0 which is necessary for a refresh(). Your content was resizing which automatically triggered the smoother to refresh(). To work around that browser bug, we had to set pointer-events: none temporarily, just when we set scrollTop to 0, and then we revert it but apparently when re-enabling them, that was firing the mouseenter for you (again). In the next release, I've changed the workaround to set the position to absolute and then back to fixed instead, so the pointer events shouldn't fire again. By the way, if you don't want ScrollSmoother to automatically do a refresh() when the content resizes, you can set autoResize: false in the config object (undocumented thus far because I wasn't sure it was worth leaving in). Generally that's not desirable, though, because if your resizing pushes triggers further down on the page, you'd want the start/end values to get recalculated.3 points