Jump to content
Search Community

GreenSock last won the day on April 21

GreenSock had the most liked content!

GreenSock

Administrators
  • Posts

    23,137
  • Joined

  • Last visited

  • Days Won

    817

Everything posted by GreenSock

  1. There's a demo on the docs page I linked you to already. You can try the plugin for FREE on CodePen, Stackblitz, etc. See https://gsap.com/trial I'm not sure what you mean by "...where we can make it more smooth??" 🤷‍♂️
  2. That looks like exactly what ScrambleTextPlugin does: https://gsap.com/docs/v3/Plugins/ScrambleTextPlugin It's a membership benefit of Club GSAP, just so you know. 👍
  3. Yeah, definitely a browser rendering issue. I did poke around a bit and it looks like the fundamental problem has to do with the mask, so the key is to make some kind of change that forces the browser to kinda wake up and realize it should re-render. So here's a fork where I set x: 0.01 on the mask element (I added a "test" class to it) AFTER the y position of the logo is updated: https://codepen.io/GreenSock/pen/QWPmRxz?editors=0010 If you remove line 29, you'll see that the logo doesn't render properly at the y: 200 value. I hope that helps.
  4. Also, you can easily force a render of a tween or timeline, and even improve runtime performance slightly by forcing all the tweens inside a timeline to initialize and grab their start/end values like this: // jump to the end and immediately back to the start animation.progress(1).progress(0);
  5. Sure, that's one way you could do it. A few suggestions: Don't use "new": // BAD let childTl = new gsap.timeline({}); // GOOD let childTl = gsap.timeline(); This can be simplified: // OLD elements.forEach((element, index, array) => { element.style.display = "none"; }); // NEW gsap.set(elements, {display: "none"}); Since you're not using params anyway, just use .add() instead of .call(): // OLD childTl.call(() => { //... }, [], 1); // NEW childTl.add(() => { //... }, 1); I think you could greatly simplify the logic too: https://codepen.io/GreenSock/pen/YzMaBME?editors=0010 Like I said, there are many, many ways to tackle this. Hopefully this helps get you on your way to something that works well for you.
  6. Oh, that's a logic issue in the way you coded it - you're depending on the onToggle to always fire, but if you scroll very quickly it might not because the active state may not toggle. For example, let's say you've got a ScrollTrigger that starts at a scroll position of 100 and ends at 150, and the user scroll really fast such that the scroll position goes from 98 to 161 (skipping over that ScrollTrigger) - it'd never toggle. You could just create a simple timeline with callbacks positioned on it like this: https://stackblitz.com/edit/stackblitz-starters-hgyqdb?file=components%2FValue.tsx Is that better?
  7. I had a very difficult seeing any issue even in your video. 🤷‍♂️ iOS is absolutely TERRIBLE with scroll-related things. There are a bunch of bugs in the browser itself, some of which were reported years ago and still haven't been fixed. It's unbelievable to me. If I remember correctly, iOS runs the requestAnimationFrame() updates at only 30fps instead of 60fps in certain scenarios, like in an iframe, until the user interacts with it (taps or drags or does something like that). I assume they're trying to "optimize" things to reduce battery drain. But of course that has very annoying side effects. What's very odd to me when I glanced at your code was that you're attaching a ScrollTrigger to a timeline, thus it by default will use toggleActions, but you're controlling that very same timeline inside of the onEnter/onLeave/onEnterBack/onLeaveBack. Why are you even attaching the ScrollTrigger to that timeline itself? It seems counter-intuitive, since you're potentially fighting with toggleActions. Why not just do a normal ScrollTrigger.create()? ScrollTrigger.create({ trigger: middle, start: "top 40%", end: "+=" + (middle.offsetHeight / 1.5), markers: true, onEnter: () => { tl.timeScale(1).play(); }, onLeave: () => { tl.timeScale(3).reverse(); }, onEnterBack: () => { tl.timeScale(1).play(); }, onLeaveBack: () => { tl.timeScale(3).reverse(); }, }); (Do that instead of putting it in the gsap.timeline({...})) In any case, the overall issue here really doesn't seem GSAP-related. It sounds like challenges related to iOS itself.
  8. It's not really a bug. It's just a fundamental logic problem in the way you're setting things up. Let me explain... In order for a callback to fire, the playhead must cross that spot on its parent timeline, or land directly on top of it. So it's based on the playhead moving (its new position). The timeline doesn't render for the first time until the next tick (it'd be silly to render right away by default because the playhead hasn't moved anywhere yet, so it'd be a waste of CPU cycles). That's why the very first one didn't fire right away. The timeline's playhead updates on each "tick" which is typically about every 16.67ms but that really depends on the browser and how busy the CPU is, etc. Your timeline is 2 seconds long and has repeat: -1. So let's say it renders almost at the end, at like 1.9857 seconds, and then on the next tick, the totalTime renders at 2.013 which means that it went past the end and wrapped around to the beginning, and 0.013 seconds into the timeline (from the start). In that ONE tick, it'd fire that callback that's at the very end of the timeline AND since it looped back to the beginning and went a little bit past, it ALSO triggers the callback that's sitting at the very start. Great. BUT What if the playhead happens to land EXACTLY at the end of the timeline (2 seconds precisely)? What do you think should happen? Obviously the callback at the end should fire, but should the callback that's sitting at the very START of the timeline also fire? I mean the end of the timeline and the start of the timeline are not the same technically, so it'd be weird if both fired. The playhead can't be at 2 seconds AND at 0 seconds. It wouldn't make a lot of sense to fire the callbacks from BOTH places on that ONE tick. See the problem? There are many ways to accomplish what I think you're trying to do there (alter visibility of things in a synchronized way), but I'd need to see what other requirements you have in order to offer the best recommendation. Thanks for the excellent minimal demo, by the way. 👍
  9. I noticed a few problems: You weren't doing proper cleanup - React calls hooks TWICE in strict mode (annoying, I know). Since you didn't return the sphere to the original parent, the 2nd time the hook ran, the sphere was already reparented to the target, thus the Flip animation wasn't really doing anything (the state was identical) You had overflow: hidden on the second element, thus you couldn't see the sphere. Is this more like what you were looking for? https://stackblitz.com/edit/react-11fqur?file=src%2FApp.js
  10. Looks to me like putting all those in a container <div> and then animating the rotationX/rotationY/x/y of that, while having a perspective applied. Give it a shot and if you get stuck and have a GSAP-specific problem, feel free to post back here with a minimal demo (like a CodePen) 👍 Good luck!
  11. There's also a helper function that may help: https://codepen.io/GreenSock/pen/vYMdMZN?editors=0010
  12. Not sure if this is helpful to you or not, but: ignoreMobileResize is true by default in recent versions, but it sounds like maybe you actually want it to be false(?) so that ScrollTrigger.refresh() gets called when the window resizes on mobile. Is that correct? ScrollTrigger.config({ ignoreMobileResize: false }); Or of course you can call ScrollTrigger.refresh() manually anytime, so wire it up however you please.
  13. You don't have to use CSS variables. Here's another way you can just use a normal object as a proxy: https://codepen.io/GreenSock/pen/PogELWo?editors=0010
  14. Yes, exactly - you'll need a Premium or Business Club GSAP membership to get access to the bonus plugins like MotionBlurPlugin. 👍
  15. This is the pretty much the same issue (and answer) as your other posts. Let me try to explain the process... When you do a normal tween, like a .to() or .from(), it must get the CURRENT value from the browser using window.getComputedStyle(element). You're using calc() values which get calculated and the result gets returned by the browser. So, for example: // you set: `inset(0% 0% 0% calc(0% - 0px) round 200px)` // browser returns via window.getComputedStyle(): `inset(0% round 200px)` So now GSAP has to figure out how to interpolate between those strings which have different amounts of numbers (thus it isn't really feasible). That is what's biting you. If, however, you use a .fromTo() tween where you're feeding in BOTH the start and the end, GSAP can discern what you're asking it to discern - it doesn't have to pull the current style from window.getComputedStyle() which is why it works: https://codepen.io/GreenSock/pen/ExJorrv?editors=0010 Since you've got the same amount of numbers in the start/end strings, they'll interpolate cleanly. We've already given you several solutions in the past - use the fromTo() or you can use CSS variables instead, and animate those with GSAP.
  16. Great catch, @aviolin. Sorry about any confusion there - it should be resolved in the next release which you can preview at: https://assets.codepen.io/16327/gsap-latest-beta.min.js As a workaround, you can just add this: tween.scrollTrigger && tween.scrollTrigger.kill(); https://codepen.io/GreenSock/pen/zYXpmBB?editors=0010
  17. If it's actually DUPLICATING the graphics, that's a browser rendering bug. I doubt it'll help at all, but you could try: ScrollTrigger.config({ignoreMobileResize: true}); ScrollTrigger.normalizeScroll(true); (one or both of those) And of course make sure you're using the latest version of the GSAP files (your CodePen looks fine).
  18. I cannot replicate it anywhere either. That sounds more like a browser rendering issue than a GSAP issue anyway 🤷‍♂️
  19. It looks like largely logic problems. I wonder if you were trying to do something like this: https://codepen.io/GreenSock/pen/wvZraYz?editors=0010 Here's another demo from our CodePen Collection that does something similar: https://codepen.io/GreenSock/pen/OJoROgY?editors=1010 I hope that helps!
  20. You're making one of the common ScrollTrigger mistakes - nesting ScrollTriggers inside of a timeline. That can't logically work because you can't have the parent timeline AND the scroll position both control the same playhead(s), as they could be going in different directions. So either separate out each tween into its own individual tween that has a ScrollTrigger on it, OR put your tweens into a timeline that has a ScrollTrigger applied to that timeline. As for once: true, you could do something like this instead: onLeave: self => { let max = ScrollTrigger.maxScroll(window), // record maximum scroll amount, and the current scroll position scroll = self.scroll(); self.kill(true, true); self.scroll(scroll - (max - ScrollTrigger.maxScroll(window))); // adjust the scroll based on how much was lost when self.kill() was called. } https://codepen.io/GreenSock/pen/jORLdmy?editors=0010 (I didn't fix all your nested ScrollTriggers - you'll need to work through those).
  21. No, sorry. But hopefully my description gets you going in the right direction. I'm not sure if it'd be any easier or work the way you want, but another idea is to use DrawSVGPlugin to basically draw a super fat orange stroke, and see if you can do a blur effect on the end of it (you'd likely need to clip/mask the other end to keep that sharp). If you need more help, we do offer paid consulting services which you can contact us about. 👍 Good luck with the project, and thanks for being a Club GSAP member! 💚
  22. That's exactly why - you put it outside the wrapper. Why would you do that? If your goal is to have a position: fixed element (not pinned with ScrollTrigger), that's fine to have it outside the wrapper. Otherwise, it should be inside. https://codepen.io/GreenSock/pen/KKYvbRw
  23. Is that orange "fill" supposed to be gradated like that (feathered/blurred)? That makes things more difficult, but not impossible. I mean in its most simple form, you could place pie-cut segments of orange along the circle, and just animate the opacity in a staggered fashion. If you need it to be gradated in a totally smooth way, you might need to make a .png that's basically HALF of the circle. Make a mask of the opposite half, and animate the rotation of that .png so that it comes into that mask on the other half. Then when that reaches 180 degrees (half the circle), pop that out of the mask and keep going to 360 degrees while you do exactly the same thing with another .png that's half the circle but fully opaque orange. Rinse and repeat until that first one gets to 540 degrees and the other two half-circle .png files are filling the circle. Good luck!
  24. Yeah, that's because you're listening to the window's "scroll" event, but ScrollSmoother gradually applies that. So you're using stale values. Just set up an onUpdate event handler on the ScrollSmoother, and for maximum performance you can leverage a cache boolean to only trigger updates when absolutely necessary: https://codepen.io/GreenSock/pen/RwOZBjp?editors=0010 Is that what you're looking for?
×
×
  • Create New...