Jump to content
Search Community

Momentum Based Range Input with Draggable

jh3y test
Moderator Tag

Go to solution Solved by GreenSock,

Recommended Posts

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

Link to comment
Share on other sites

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.

 

See the Pen QWaLGpM by GreenSock (@GreenSock) on CodePen

 

  • Like 3
Link to comment
Share on other sites

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?: 

See the Pen VwyZmEv?editors=0010 by GreenSock (@GreenSock) on CodePen

 

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

  • Like 1
Link to comment
Share on other sites

17 minutes ago, GreenSock said:

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 was trying to set the input value when you press and don't move, but then realized he was doing in that in completely different function, so I just left it.

 

image.png

 

Link to comment
Share on other sites

9 hours ago, OSUblake said:

What's this undocumented 2nd parameter for?

dragHandle.startDrag(e, true)

 

It's an "align" parameter. If the target element isn't on top of the pointer (according to the supplied event), setting align to true will move it there immediately.

  • Like 2
Link to comment
Share on other sites

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
Link to comment
Share on other sites

  • 2 weeks later...

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 ʕ •ᴥ•ʔ

Link to comment
Share on other sites

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 👍

See the Pen 4ca7a99334e6f132e34c0ff07cb177aa?editors=0010 by GreenSock (@GreenSock) on CodePen

 

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? 

  • Like 1
Link to comment
Share on other sites

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 ʕ •ᴥ•ʔ

Link to comment
Share on other sites

2 hours ago, jh3y said:

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"?

 

Just going off of Jack's demo, I would check and keep track of the velocity value on every update, and if the current velocity has a different sign than the previous velocity, that means it bounced. For example, if the velocity was -10 on the last tick and now it's 15, it has changed direction, i.e. it most likely bounced.

 

  • Like 2
Link to comment
Share on other sites

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
Link to comment
Share on other sites

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
Link to comment
Share on other sites

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. 

  • Like 1
Link to comment
Share on other sites

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
Link to comment
Share on other sites

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!

Link to comment
Share on other sites

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
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...