Jump to content
Search Community

Recommended Posts

Posted

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

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 : 

 

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:

 

 

 

Posted (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 by Neek
checked documentation for inertia plugin
mvaneijgen
Posted

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!

 

 

Posted

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

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.

Posted

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!

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...