Neek Posted January 13 Posted January 13 Hello, I faced a problem which tried to resolve in a couple of ways, but all of them have some issues. Currently Im trying to implement the simple wheel spin animation. But tricky part it's in a steps what I want to achieve. This is the final version of what I'm doing. So we have 3 phases: 1. Start spin animation (going little bit back) and increase speed after that and start infinity spinning animation; 2. Waiting on response from API; 3. After receiving information from API calculate the final position and adjusting time for a stop animation; So the main issue here, that for some reasons I can see "speed up" for a wheel while receiving the data from API and can't figure out what is going. Initially I was trying to use 2 different methods for start and stop, but it also have pretty much the same issue and I was thinking that it can be an issue with timeline, but in the example below I have one timeline, Im event pausing before calculation and still have visual problem. Or maybe Im on a wrong way and some one have a better idea/vision how to implement that part. spin1: async ( dataPromise) => { return new Promise((resolve) => { if (!wheelRef.current) return const speed = 360 const tl = gsap.timeline() autoSpinTween.current = tl const accelDuration = 1.5 const accelDistance = (speed * accelDuration) / 2 tl.to(wheelRef.current, { rotation: '-=10', duration: 0.5, ease: 'sine.inOut', }).to(wheelRef.current, { rotation: `+=${accelDistance}`, duration: accelDuration, ease: 'power2.in', }) tl.to(wheelRef.current, { rotation: '+=360', duration: 360 / speed, ease: 'none', repeat: -1, }) dataPromise.then(({ winningSectionId, nextSections }) => { const currentRotation = gsap.getProperty( wheelRef.current, 'rotation', ) as number const timeInTimeline = tl.time() const currentVelocity = timeInTimeline < 2.0 ? (timeInTimeline / 2.0) * speed : speed const winningIndex = wheelSections.findIndex( (s) => s.id === winningSectionId, ) const sectionAngle = 360 / totalSections const normalizedRotation = currentRotation % 360 let distanceToTarget = (360 - normalizedRotation - winningIndex * sectionAngle) % 360 if (distanceToTarget < 0) distanceToTarget += 360 const minSpins = 2 const finalDistance = distanceToTarget + minSpins * 360 const adjustedDuration = (2 * finalDistance) / currentVelocity tl.kill() gsap.to(wheelRef.current, { rotation: currentRotation + finalDistance, duration: adjustedDuration, ease: 'power2.out', onComplete: () => resolve({ winningSectionId, nextSections }), }) }) }) } Add link to the project: https://stackblitz.com/edit/gsap-react-basic-f48716-ssrnhwjb?file=src%2FApp.js
GSAP Helper Posted January 13 Posted January 13 Without a minimal demo, it's very difficult to troubleshoot; the issue could be caused by CSS, markup, a third party library, a 3rd party script, etc. Would you please provide a very simple CodePen or Stackblitz that illustrates the issue? Please don't include your whole project. Just some colored <div> elements and the GSAP code is best. See if you can recreate the issue with as few dependencies as possible. Start minimal and then incrementally add code bit by bit until it breaks. Usually people solve their own issues during this process! If not, at least we have a reduced test case which greatly increases your chances of getting a relevant answer. See the Pen aYYOdN by GreenSock (@GreenSock) on CodePen. that loads all the plugins. Just click "fork" at the bottom right and make your minimal demo: Using a framework/library like React, Vue, Next, etc.? CodePen isn't always ideal for these tools, so here are some Stackblitz starter templates that you can fork and import GSAP as shown in the Install Helper in our Learning Center : React (please read this article!) Next Svelte Sveltekit Vue Nuxt Please share the StackBlitz link directly to the file in question (where you've put the GSAP code) so we don't need to hunt through all the files. Once we see an isolated demo, we'll do our best to jump in and help with your GSAP-specific questions. ✅ Finally you can have a look at the demos in these threads as they could provide some inspiration:
Neek Posted January 14 Author Posted January 14 (edited) I create an example on stackblitz https://gsap-react-basic-f48716-ssrnhwjb.stackblitz.io Maybe on the first spin is not visible, but on the next once would be more visible the wheel spin speed changes. I also checked the couple of similar topics on the forum and wasn't able to find a solution. I tried to use inertia props for stopping, but it's also have problem with the speed, its not slow down smoothly. Tried this one: gsap.to(wheelRef.current, { inertia: { rotation: { end: currentRotation + finalDistance, }, }, I think it's a right way, to use inertia, but in that case, animation to stop the spin animation take too much time and duration property is not working in that case as Im understand. Would be appreciate any advice/direction/help on that issue! Found that we can configure the duration for inertia, but I think right now, I have a problem in calculations or timings. This is what give me a better visual effect (changed stop logic), but still see the changes in the speed (got speed up in some cases) dataPromise.then(({ winningSectionId, nextSections }) => { tl.pause() const currentRotation = gsap.getProperty( wheelRef.current, 'rotation', ) as number const winningIndex = wheelSections.findIndex( (s) => s.id === winningSectionId, ) const sectionAngle = 360 / totalSections const normalizedRotation = currentRotation % 360 let distanceToTarget = (360 - normalizedRotation - winningIndex * sectionAngle) % 360 if (distanceToTarget < 0) distanceToTarget += 360 const minSpins = 2 const stopAt = currentRotation + distanceToTarget + minSpins * 360 gsap.to(wheelRef.current, { inertia: { rotation: { end: stopAt, }, duration: { min: 3, max: 4 }, }, onComplete: () => resolve({ winningSectionId, nextSections }), }) }) Edited January 14 by Neek checked documentation for inertia plugin
mvaneijgen Posted January 15 Posted January 15 Hi @Neek welcome to the forum! You are sharing the link to the preview of the stackblitz, we would like to help you debug by diving in to the code, can you share a link to your project within the editor which then also linking to the correct file in which your GSAP code resides. Then someone will probably be able to point you in the right direction!
Neek Posted January 15 Author Posted January 15 Hi @mvaneijgen, Yeah, I post a wrong link, sorry for that. This is the link to editor https://stackblitz.com/edit/gsap-react-basic-f48716-ssrnhwjb?file=src%2FApp.js
Rodrigo Posted January 15 Posted January 15 Yeah the issue here is that the speed in terms of degrees per second is not the same between the Tween that creates all the rotations and the initial and final ones, the ones that accelerates the wheel and the one that decreases the speed. Those differences creates the jumps you're seeing. Unfortunately correcting that requires some bit of custom logic and some math and I don't have the time to do that for you now, I'll see if I can dig into this tomorrow. You could borrow some logic from the Vertical Seamless loop function in this demo but it might not be the simplest thing: See the Pen bGKJRxN by GreenSock (@GreenSock) on CodePen. Other alternative is to use a single tween for the forward motion and use Custom Ease to create the easing for a single tween that handles all the rotation forward: https://gsap.com/docs/v3/Eases/CustomEase With that you could create an easing function for the initial acceleration a linear phase and a final deceleration. You can explore the custom ease tool and find a way to create that. Hopefully this helps Happy Tweening!
mvaneijgen Posted January 16 Posted January 16 I might be a bit out of line here, but what springs to mind after reading this was why not fake the selecting. The logic issue with targeting the same element with multiple animations is always so hard to solve, so why not split the selecting of the winning result and the spin animation in two steps with two different elements? Here a demo how I would set it up. I have a nested element the spinner always animates and multiple of 360 and the inner element selects the winning 'cell', in the demo the winning cell gets selected at the start, but you could run this code when ever you want. It would be probably invisible to the vistor because the spinner probably is spinning to fast to see it slow down or speed up a bit and because the spinner always spins a multiple of 360 it always ends at the same spot, but the inner element is what does the selecting. No complicated calculations! Hope it helps and happy tweening! See the Pen QwEdYoX?editors=0010 by mvaneijgen (@mvaneijgen) on CodePen.
Rodrigo Posted January 21 Posted January 21 Hi, Normally we don't create custom demos for our users but this was a fun challenge and a good way to show the Inertia Plugin working outside a Draggable environment. See the Pen emzRWqa by GreenSock (@GreenSock) on CodePen. Hopefully this helps Happy Tweening!
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now