Jump to content
Search Community

jh3y

Premium
  • Posts

    77
  • Joined

  • Last visited

Posts posted by jh3y

  1. Howdy y'all 🤙

     

    I thought this was doable and could've sworn I'd done it somewhere before.

     

    But, what's the best way to update a Draggable type on the fly? I had assumed updating the `type` on the fly might work. But, it doesn't. I can update the bounds as expected. Can't remember if I need to do this with a proxy or something instead.

     

    const Dragger = Draggable.create('.drag', {
      type: 'x',
      allowContextMenu: true,
      bounds: {
        left: 0,
        right: 1000,
      },
      onDrag: function (event) {
        // Update bounds and types based on drag distance
        if (this.x > 200) {
          this.type = 'x,y'
          this.vars.type = 'x,y'
          // Bounds update as expected
          this.applyBounds(document.body)
        }
      },
    })

     

    Thanks for your help in advance! 🖤

     

    – Jhey ʕ•ᴥ<ʔ

     

    __UPDATE::__ Looks like in previous demos I've used a proxy drag with a trigger and hooked into the `x` or `y` value until a threshold is met. Then I would open up to using other values. But, would be nice to be able to update the `type` on the fly. Happy to close this one though 🤙

  2. Thanks for the replies people! 🤙 Sorry if I wasn't clear @Rodrigo, I had an idea about what I was going to do for the Draggable part. It was animating the little ticker part so it syncs up with the inertia of the wheel I hadn't attempted in my head yet 😅

     

    @Cassiehas come in with an awesome starting point! 🙏 That's pretty much there. I guess it'll just be a case of fiddling with the keyframes/timing of the ticker so I can almost scrub the ticking/clicking. It won't have to be super accurate for the demo I'm presenting. But, I'm always curious how close we can get with these things or what crafty techniques come out of exploring.

     

    – Jhey \ʕ •ᴥ•ʔ/

    • Like 2
  3. Hey y'all! 👋

     

    I've tried digging around in the forums a bit for kinda what I'm looking for but some of the threads are pretty old and I'm wondering if there's maybe a newer/easier way to achieve this effect.

     

    What I'm looking to do is create a spinning wheel that will land on a section with maybe some bounce back. My first thoughts are GSAP Draggable with rotation type and inertia which will get me most of the way. But, it's that little ticker piece at the top. How would you think to approach that? And potentially have it push back when the acceleration is slowing down from the inertia?

     

    Is it purely a case of tapping into the callbacks and trying to work it out based on the state of the Draggable? And could you somehow sync a repeating animation of the ticker to sync with the acceleration of the Draggable?

     

    I'm just sort of putting it out there. I haven't sat down to properly put something together yet. Attached is a very rudimentary CodePen with Draggable and Inertia in place. But, these were the first ideas that came to mind for approaching it. And there may be a much cleverer way of achieving it.

     

    Thanks for all you're doing!

     

    – Jhey ʕ •ᴥ•ʔ

    See the Pen c1734f9bbdc58bdde1542aaee94b7fbf by jh3y (@jh3y) on CodePen

    • Like 1
  4. You know what? I think that's all it needed, an `immediateRender` in the `fromTo`. Then you can change things however you like. I had my `immediateRender` in the wrong place d'uh 🤦‍♂️

     

    Thanks Carl! 🤙 

     

    A little further up in the code I log the loop times in an Array to use by counting the `index` so should be good on that part 🤞 See if I can actually build the rest of the demo now 😬

     

     

    • Like 2
  5. Hey y'all! 👋

     

    Got an odd one. I could be missing something straightforward. But, in this pen, I'm animating a set of squares on a trail on a canvas. I want to create a seamless loop so I've constructed the timeline by looping over the squares three times and then tweening the `totalTime`. In the code, It currently tweens from `totalTime: 0` to `totalTime: 7` and the entire timeline is shown as expected.

     

    But, as soon as I tweak the `totalTime` values, mainly the starting value, it breaks the animation window that I'd expect to see 🤔 Not sure if I'm missing something clear and obvious. But, the technique should work in this scenario I imagine.

     

    The looping code is at the bottom, try changing the initial `totalTime` to something like `1` and it doesn't show the `TRAIL` timeline window of `1` to `7`.

     

    Are my timings off? Or, do I perhaps need to do something different when working with plain Objects like this?

     

    Thanks in advance! 

     

    – Jhey

    const TRAIL_LOOP = gsap.fromTo(
      TRAIL,
      { totalTime: 0 },
      {
        totalTime: 7,
        repeat: -1,
        ease: 'none',
        duration: 6,
      }
    )

    See the Pen 783443d6a6ad7c9813b75807e80a92b4 by jh3y (@jh3y) on CodePen

  6. Howdy y'all! 👋

     

    Had a comment on CSS Tricks today about the article for this technique: https://css-tricks.com/going-meta-gsap-the-quest-for-perfect-infinite-scrolling/

     

    Has something changed dramatically between versions to introduce these breaks? Or is there a property I need to update or add potentially?

     

    The other solution I could see is pinning the GSAP version in all the demos for that article to ~3.7.0 I think 🤔

  7. On 4/2/2022 at 10:19 PM, GreenSock said:

    I'm not entirely sure what you mean by "...it would update the value and inertia would be triggered too..." but you could definitely use Draggable on a custom handle and just update the input.value in the onDrag, for example. And yes, you could have it track the inertia/velocity of the custom handle and do almost exactly the same thing we're doing in the simplified example, but you'd be animating the handle instead and then in the modifier you could apply the new value to the input.value. 

     

    Does that answer your question? 

    Yeah, I _think_ we mean the same thing 😅

     

    My terminology may be slightly off but I'm pretty sure we are on the same frequency there

     

    Thanks Jack!

     

                 
    ʕ •ᴥ•ʔ/

  8. 19 hours ago, GreenSock said:

    Hey @jh3y. Just making sure everything was clear and helpful. Are you all set? There are many ways you could accomplish this - I was just providing the way that seemed clearest in my brain but that's just one option. If you need help with anything else, just let us know. 

     

    Lookin' forward to the article!

    Hey Jack 👋

    Sorry – I've taken a couple of days off before starting a new role this coming week. That solution is super clear and I love it! It's a great spin on the article too for showing learning process and how we've got to this solution. Kinda like the "Meta GSAP" article (Which could totally be redone with Observer now, right? Awesome!)

     

    I really like this solution because of it's simplicity. If I were to put another element such as a custom drag handle, and use Draggable for that bound to the input like I had originally, it would update the value and the Inertia would be triggered too, right? I think that's correct. I may have not worded it right but I'm pretty sure that works too, haha.

     

    Can't wait to finish off the article! \ʕ •ᴥ•ʔ/

     

    Thanks again for everything!

    • Like 1
  9. 59 minutes ago, Cassie said:

    It certainly won't be removed.

    It's actually part of the functionality of ScrollTrigger, we originally just planned to surface it because it's a bit too useful to be tucked away under the hood but folks asked if it could be it's own plugin. So now if people using ScrollTrigger want to grab some extra event/delta info while using without adding extra kb's they can - but if people aren't using ScrollTrigger they can load a separate plugin. 

    This makes perfect sense. I'll get that other demo fixed up to use the new API too

     

    Thanks! \ʕ •ᴥ•ʔ/

    • Like 1
  10. 3 minutes ago, Cassie said:

    There's still a stripped back observe method in ScrollTrigger. That's what you used! Thank you 💚

    Free to share that now too btw, but yeah we pulled Observer out into it's own plugin. Gives it more space to grow and evolve.

    https://greensock.com/docs/v3/Plugins/ScrollTrigger/static.observe()

    And that won't be removed? Also, "that" demo could be converted to use this API?

     

    Rad! I'll share today.

    • Like 2
  11. 12 hours ago, GreenSock said:

    This is actually a good use case for our brand new Observer tool (announcing it broadly tomorrow): 

     

     

     

     

    Nice and simple, right?

    This is great! And perfect timing 😅 And will be great for the article too!

     

    I could totally make this into an "Observe" demo. I'm assuming this is the tool that has been "renamed" from something else that I may have made a demo for?

     

    Thanks Jack! 🙏

     

    \ʕ •ᴥ•ʔ/

    • Like 2
  12. 9 minutes ago, GreenSock said:

    Hey @jh3y! Sorry about the delay - been swamped with launch stuff. Super excited about 3.10!

     

    Anyway, I think you could greatly simplify things, cutting probably 70%+ of the code 👍

     

     

     

    The main problem was that inertia tracking by its very nature is time-dependent, meaning it sorta keeps track of a certain amount of history so that it can do the calculations. You were creating a scenario where you inverted the velocity via a tween, but it took a little time for that to actually get reflected in the tracked velocity (as it should). So let's say it's moving super fast in one direction, so maybe 3000px/s and then you suddenly start moving it in the opposite direction at half the speed, but it's taking data points once every tick and must average them out - if it hits the other limit quickly enough, it'll still have some historical data from when it was going 3000px/s that offsets things. So in your case, that'd result in it still being a positive velocity and you were multiplying it by a negative, thus heading in the same direction as before.

     

    Sure, we could implement fancy workarounds to adjust for that but I thought it'd be much cleaner to simply do a single tween with a modifier that uses a wrapYoyo() utility function. No Draggable, no proxy, no checking for collisions on every tick. Faster, cleaner, and more succinct. Oh, and I changed the range step to 0.05 for more accuracy. You could easily just snap it in an onComplete or whatever if it's important to have a step of 1. 

     

    It's a similar approach to the one used in this old thread: 

     

    Does that clear things up? 

    Oh wow. That is much smaller. And I like that wrapYoyo! TIL.

     

    The only issues I see here are:

    - I want to plug into the "collision" so I can play a noise and bump the input itself from left to right based on the velocity too. Can you detect when a wrap happens inside "onUpdate"?

    - If I click the track, it applies the velocity which I wouldn't necessarily want as I want that to only happen after a "drag".

     

    With this original demo, it has the "correct" behavior. But, it has the drawback of me trying to keep that element in sync with the slider thumb. I was thinking with the "Draggable" way, I'd be able to easily keep a "faux" thumb in place by updating it's position based on the input value. But, that isn't an important part here. Just something I was thinking of doing.

     

    Thanks for teaching me something new! Again! 🙏 The best part being that I've kinda written an article using Draggable. But, it's kinda cool that I can steer it in a different direction. I love mentioning the forum powers!

     

    Jhey ʕ •ᴥ•ʔ

  13. Hey y'all! 👋

     

    I've been playing with this demo some more and attempting to create it without a "proxy" element in the DOM. It works for the "most" part but the velocity seems a little off with the Inertia compared to the one above.

     

    But, that's not the main issue I'm encountering. The issue is calculating the bounce back based on the value of the input. If I animate the value of the input or drag it so that it should bounce, it gets stuck on the end as if other tweens are hanging on if that makes sense? It's like it can't keep up with itself or the tracker velocity is hanging. For example, if you bounce it off one end where you expect it to bounce off the other, it hangs. Any ideas @OSUblake && @GreenSock

     

    See the Pen abEWPOo by jh3y (@jh3y) on CodePen

     

    Look forward to seeing what you think on this one. Could it be the way I'm using the `inertia`? Previously, this was set on `x` because I was moving an element. Would I instead need to map this to the input values perhaps?

     

    Jhey ʕ •ᴥ•ʔ

  14. On 3/11/2022 at 7:26 AM, OSUblake said:

    Hey Jhey,

     

    I would probably just put an event listener on the input and use startDrag.

     

    https://greensock.com/docs/v3/Plugins/Draggable/startDrag()

     

    You might be able to do the same thing with Draggable by using the input as the trigger, but I just quickly threw this together. You're probably going to have to tweak it to work your sync function and whatnot, but it's just to give you an idea.

     

     

     

     

     

    Hey y'all! 👋

     

    Looks like `startDrag` was the piece of magic I was looking for 🙌 Thanks for sharing that one. I think this solves the issue of trying to align the slider proxy handle with the slider thumb because wherever you click, it will align, which is perfect

     

    On 3/11/2022 at 7:49 AM, GreenSock said:

    Hey @jh3y! Nice seeing you around the neighborhood. 🙌

     

    I'm not sure it needs all that code in the "pointerdown", Blake, although I very well may be missing something (it's super late and I'm brain-dead) - couldn't it be this simple?: 

     

     

     

    I didn't have time to pour over all your other code, @jh3y - were you asking for input about how to structure things differently? 

     

    Happy to be here! Thank you for both of your inputs. Love learning new parts of the API to solve these little challenges 💚 TIL "startDrag" 💪

     

    Now I can start writing this one up 😅

     

    Thanks again! \ʕ •ᴥ•ʔ/

    • Like 5
  15. Hey y'all!

     

    I've been thinking to write something up about this demo. But, I feel like there's a more robust way for me to handle dragging on the input regardless of where the slider thumb is 🤔

     

    I've tried putting in place a "Proxy" element (.slider__proxy) and I'm updating based on that. However, if you were to click the track somewhere away from the thumb, would it be possible to somehow trigger "drag" mode and move the slider thumb and proxy into place? I feel like I'm overcomplicating it somewhat seeing as a range input already has a drag handle kinda built in.

     

    But yeah, I was keen to know if there was a better approach to what I've done with it.

     

    Jhey ʕ •ᴥ•ʔ

    See the Pen podVRxw by jh3y (@jh3y) on CodePen

  16. Hey y'all!

     

    On 1/28/2022 at 4:34 PM, Cassie said:

    Always nice to see you in the forums Jhey.  ☺️

    Thank you @Cassie 🙏

     

    One more query on this demo 😁 I've got it pretty much there where I want it. There's one thing I can't seem to make work nicely. It's setting `aria-hidden` on the numbers as they slide in and out 🤔

     

    I've been trying this but it is a little inconsistent for me. Sometimes it works, sometimes it leaves some with the wrong values. Would it make sense to maybe do this outside of the timeline and work out which digits need the attribute applied when doing the scrub instead? (Maybe I've answered my own question there 😅)

     

    const DIGI_TIMELINE = gsap
          .timeline()
          .set(DIGIT, {
            yPercent: 0,
            attr: {
              'aria-hidden': 'true',
            },
          })
          .to(DIGIT, {
            yPercent: 100,
            delay: i * COEFF,
            duration: 1,
            onStart: () => {
              gsap.set(DIGIT, {
                attr: {
                  'aria-hidden': 'false',
                },
              })
            },
          })
          .to(DIGIT, {
            delay: COEFF - 1,
            yPercent: 200,
            duration: 1,
            clearProps: 'all',
            onStart: () => {
              gsap.set(DIGIT, {
                attr: {
                  'aria-hidden': 'true',
                },
              })
            },
          })

    Anyways, hope you've all had an awesome weekend. Here's the demo! It's part of a series I'm doing where it's about debunking Instagram Reels/TikTok where someone shows how easy it is to make a design in Figma but it doesn't show the reality on the dev side of making said thing 😅

     

    See the Pen XWzmGad by jh3y (@jh3y) on CodePen

     

    ʕ´•ᴥ•`ʔ

     

  17. 19 hours ago, GreenSock said:

    I love that you don't mind asking questions here, @jh3y! It shows that advanced users don't need to be bashful. 🙌

     

    Just a few thoughts/comments for what it's worth (maybe not much)

    1. You never need to set paused: false or delay: 0 on a timeline/tween (those are the defaults)
    2. Instead of using an onComplete to clearProps, you could just put that in your final tween. No biggie - maybe it was easier for you workflow-wise to use the onComplete.
    3. I'm not sure what you were trying to accomplish by using '- 0.5' as the position parameter. As it stands now, that's merely acting as a label (a very oddly-named one): 
      // since it's  a string...
      TL.add(DIGI_TIMELINE, '- 0.5')
      
      // ...it's acting the same as something like:
      TL.add(DIGI_TIMELINE, 'someLabel')
      
      // if you want it -0.5 seconds from the end:
      TL.add(DIGI_TIMELINE, "-=0.5")

      You could simplify things:

      // OLD
      const DIGI_TIMELINE = gsap.timeline({
        paused: false,
        onComplete: () => gsap.set(DIGIT, { clearProps: true })
      })
      DIGI_TIMELINE
        .set(DIGIT, {
        yPercent: 0,
      })
        .to(DIGIT, {
        yPercent: 100,
        delay: i,
        duration: 1
      })
        .to(DIGIT, {
        yPercent: 200,
        duration: 1
      })
      TL.add(DIGI_TIMELINE, '- 0.5');
      
      // NEW
      const DIGI_TIMELINE = gsap.timeline()
        .set(DIGIT, {
          yPercent: 0,
        })
        .to(DIGIT, {
          yPercent: 200, // since it's linear, there's no need for the prior tween to 100
          duration: 2,
          clearProps: "all"
        }, i)
      TL.add(DIGI_TIMELINE, 0);

       

    4. FYI, that tween wasn't rendering at the initial state because you had set paused: true on the timeline thus that's an indicator to GSAP that you don't want it rendering right now. But like Blake said, you can just .progress(1).progress(0) to force the playhead/render. Plus that also forces everything to get initialized up-front, so it'll be able to skip those tasks when it runs.

    We love seeing all the cool stuff you've been putting out there and tweeting about. Keep up the good work inspiring people!

    Not at all! I love some humble pie ☺️ It's the best way to learn. I'll always take suggestions and discussions.

     

    1. Totally! 💯 Sorry, the code is a little rough and I'd left them in as I was playing with variations trying to get it working with things like `delay: 2` etc. Sorry for that!

    2. Ahh yes, that would make sense too. I guess in my head I see each timeline like its own little block/animation component 😅

    3. Yes! Love this. Now the position param threw me to start with because I was in a bit of a rush. Without a label it broke and of course I needed `0` 😅. The simplified version is much nicer and again this was kind of my bad, sorry. I'd left it rough as I was playing with different eases for the effect I want. Ahh, I see that `clearProps` usage now and this is much nicer too!

    4. This is a rad tip. Kinda shows the magnitude of what GSAP covers because although I've used `progress` in the past, I'd completely forgotten about it haha. Trying to spend more time documenting things I make this year for this reason 😅

     

    Appreciate the kind words massively! I'll keep creating for sure. Love sharing and challenging ha. If I don't hear from you, have a great weekend!

     

    \ʕ •ᴥ•ʔ/

    • Like 3
  18. Think I've worked out a way.

     

    Needed another timeline to scrub the playhead of `MANIPULATOR`. Then I'm using a `delayedCall` to get the ball rolling

     

    const SCRUB = gsap.to(MANIPULATOR, {
      totalTime: 0,
      duration: 0,
      paused: true,
      ease: 'none'
    })
    
    
    SCRUB.vars.totalTime = 4
    SCRUB.invalidate().restart()
    
    gsap.delayedCall(2, () => MANIPULATOR.play())

    There is likely a cleaner way to do this. The idea being that I'm going to be scrubbing a timeline composed of a few timelines and need to show the current state. I'll keep tinkering 🤓

  19. Hey y'all 👋

     

    Looping through a set of numbers infinitely by tweening `totalTime` on a timeline. But, how do I show the current state while it's `paused`?

     

    I'm setting `TL.totalTime(1)` here but nothing shows. Do I need to do something with `immediateRender`? I'm going to be scrubbing this timeline with another control so it will remain paused. I've tried the `invalidate().restart()` manoeuvre but I'm not having much luck...

     

    import gsap from 'https://cdn.skypack.dev/gsap'
    
    const TL = gsap.timeline({
      paused: true,
    })
    
    const DIGITS = gsap.utils.toArray('.digit')
    
    const PADDED_DIGITS = [...DIGITS, DIGITS[0]]
    
    for (let i = 0; i < PADDED_DIGITS.length; i++) {
      const DIGIT = PADDED_DIGITS[i]
      const DIGI_TIMELINE = gsap.timeline({
        paused: false,
        onComplete: () => gsap.set(DIGIT, { clearProps: true })
      })
      DIGI_TIMELINE
        .set(DIGIT, {
          yPercent: 0,
        })
        .to(DIGIT, {
          yPercent: 100,
          delay: i,
          duration: 1
        })
        .to(DIGIT, {
          yPercent: 200,
          duration: 1
        })
        TL.add(DIGI_TIMELINE, '- 0.5')
    }
    
    const MANIPULATOR = gsap.timeline({
      delay: 2,
      paused: true,
    })
      .fromTo(TL, {
        totalTime: 1,
      }, {
        totalTime: 11,
        duration: 4,
        ease: 'none',
        repeat: -1,
      })
    
    TL.totalTime(1)

    Thanks in advance! Sure I'm missing something obvious.

     

    ʕ ᵒ ᴥ ᵒʔ

     

    Setting `MANIPULATOR` to `paused: false` plays the timeline as expected. But, I'd like to see the playhead state when it's paused.

    See the Pen OJOPdQw by jh3y (@jh3y) on CodePen

×
×
  • Create New...