Jump to content
Search Community

Animations in Timeline share scope, how to disable this?

affka test
Moderator Tag

Recommended Posts

Hey,

 

I believe my issue is simple just that I do not find how to solve it in documentation. I have an object which initiates timeline animation:

let tl = gsap.timeline();
  tl.addLabel("start").add(rotate()).add(rotate()).add(rotate()).add(rotate());

"rotate" function:

function rotate(direction) {
  let newDegree;
  newDegree =
    direction !== undefined
      ? direction
        ? degree + 90
        : degree - 90
      : degree + 90;
  if (newDegree > 360) newDegree = 90;
  if (newDegree < 90) newDegree = 360;
  let tl = gsap.timeline();
  tl.to(".product-cube", {
    rotateY: newDegree,
    duration: 2,
    onComplete: () => {
      changeDegree(newDegree);
    },
  });
  return tl;
}

The thing I want to achieve is that upon each rotate, the variable "degree" gets increased/decreased by specific amount, however, it gets increased only once upon each iteration and it is always 90 (initially degree is 0). I believe it is due calling rotate() when adding to timeline. 

 

Another issue, when animation ends and when I start another iteration of 4 rotations, it immediatelly gets back to default state (degree 0) where I would like it to go from 360 to 90 instead of 360 to 0 instantly(360 is same as 0, but as it goes from 360 to 0 it goes backwards) and then to 90 as supposed. Is it possible to "pause" animation(timeline) at its current state (degree variable) when it gets called again? I could go from 360 to 450 and so on, but as it is my first issue, the rotate() animation doesn't care about global constants.

Let me know if my issue not clearly explained.

 

Thanks.

 

See the Pen YzwxNYx by affkatron (@affkatron) on CodePen

Link to comment
Share on other sites

If you're asking why your tweens don't use the updated degree value, that's because you're not changing that value until the onComplete for each tween. All of the tweens are created at the start (when the degree is equal to 0).

 

My demo above works around that by using a relative rotation value.

Link to comment
Share on other sites

Thanks for such quick reply! 

 

My goal is to have a cube (basically 4 walls) which would rotate by 90 degrees (would show a new wall upon each rotation). When I do this:

  let tl = gsap.timeline();
  tl.addLabel("start").add(rotate(90)).add(rotate(180)).add(rotate(270)).add(rotate(360));

I achieve my goal, but I also want the cube to be interactive, so that user could also rotate it either back or forward, thus I need global variable which would remember by many degrees cube has rotated so far. Thus I do this:

  let tl = gsap.timeline();
  tl.addLabel("start").add(rotate()).add(rotate()).add(rotate()).add(rotate());

and use the global variable inside rotate(), by incrementing current degree by 90 upon each rotation. The issue I am facing is that it calculates the degree when I am adding it to timeline, not when rotate animation actually happens which makes sense because I am calling the function rotate() when I am adding it to timeline. 

 

Would approach below would be correct?

  let tl = gsap.timeline();
  tl.addLabel("start").add(rotate).add(rotate).add(rotate).add(rotate);

Or how I could achieve it?

 

Thanks.

 

Link to comment
Share on other sites

Hi,

 

Thanks for a demo, seems like I am getting a better understanding of GSAP. However, I am still stuck with 2 issues:

The  chain of animation does not repeat if gets added to another timeline:

function startRotation(element) {
  let tl = gsap.timeline();
  tl.add(rotate(1, element))
      .then(() => rotate(1, element))
      .then(() => rotate(1, element))
      .then(() => rotate(1, element));
  return tl;
}

// Master timeline
let tl = gsap.timeline({
  repeat: -1,
  repeatDelay: 1,
  delay: 1,
});
tl.addLabel("start")
  .add(startRotation(1), "start")
  .add(startRotation(2), "start+=0.5")

I am working with several cubes and want them to rotate independently thus I wrap the rotation flow into "startRotation" tween and return it to master tween where I can change their start time in the master timeline. However, the master timeline's repeat settings do not seem to apply. If I have startRotation as follows:

let tl = gsap.timeline();
  tl.add(rotate(1, element))
      .add(rotate(1, element))
      .add(rotate(1, element))
      .add(rotate(1, element));
  return tl;

where then() gets replaced by add(), then repeat of master timeline works. Why is that so? How the method then() changes the behavior of a tween?

 

Another issue is that startRotation() tween gets "broken" if I rotate() the cube from any other interaction (let's say button click). By broken I mean that let's say initially each rotate() has delay of 2 seconds, but if I rotate() the cube from somewhere else in the middle of the chain, then the next rotation from the chain gets extended by 2 seconds from the last user initiated rotate(), not from the last rotate() in the initial chain.
How do I make that rotate() delay setting gets applied only to rotations set in startRotation and ignores any other rotate(). Would having another tween for user interactions fix this? Or is it solvable with one tween (would be more convenient as there might be a lot of user interaction origins and I would like to stick with only one tween for them all)? 


See the Pen BajdJEN by affkatron (@affkatron) on CodePen

 

Huge thanks!

Link to comment
Share on other sites

Hey affka. You've got lots of questions there :) Instead of answering them all right now, how about you share about your end goal? Then I can help you get what you're after and maybe seeing the full picture would help you get a better grasp of how things are working.

 

If someone clicks the left button, do you want both cubes to rotate? Or just one? If just one, are there going to be buttons for every cube?

Link to comment
Share on other sites

For the record, you can think of then() like a callback that simply fires a function but the actions that resulting function have nothing to do with the timeline itself. They're totally separate. So if you have a bunch of then() calls chained together that do various animations and then you rewind, it wouldn't revert all the things you did inside those functions. It's not supposed to. When you embed animations into a timeline itself (like with add()), it's all one cohesive unit so you can scrub the playhead and everything responds. 

  • Like 1
Link to comment
Share on other sites

I appreciate your help a lot.

 

The full idea of what I want to achieve can be seen at the codepen below, although there is a lot of smelly code not related to GSAP so I do not recommend diving into it. The idea is several cubes or more, where everyone rotates from 0 to 360, or does any animation of any sort by 0.5 delay from the previous cube, and repeats the cycle after all cubes have finished the animation. In addition, the animation must be playable by outside interactions.

So I at the moment I have a rotate method (in ProductCube) which rotates the cube by 90 degrees to a given direction:

rotate(dir = 1, origin) {
    this.iteration += dir;
    let delay = origin === "mouse" ? 0 : 2;

    console.log("rotate", this.iteration % 4, "wall data");

    let tl = gsap.timeline();
    console.log(this.iteration);
    tl.to(this.productCube, {
      rotateY: this.iteration * 90,
      delay: delay,
      duration: 2,
      overwrite: true,
    });
    return tl;
  }

Furthermore, I have a method to start initial timeline for one single cube, to rotate the cube from 0 to 360 degrees.

startRotation() {
    console.log("start rotation");
    let tl = gsap.timeline();
    tl.add(this.rotate())
      .then(() => this.rotate())
      .then(() => this.rotate())
      .then(() => this.rotate());
    return tl;

Lastly, I have another class and method (in ProductShowcase) which starts rotations for all cubes.

init() {
    let tl = gsap.timeline({
      repeat: this.repeat,
      repeatDelay: this.repeatDelay,
      delay: this.delay,
    });
    tl.addLabel("start")
      .add(this.cubes[0].startRotation(), "start")
      .add(this.cubes[1].startRotation(), "start+=0.5")
      .add(this.cubes[2].startRotation(), "start+=1");
    this.animation = tl;
  }

Additionally, I have a function for user interactions which upon scroll rotates any cube additionally.

function cubeScroll(target, event) {
  window.clearTimeout(isScrolling);

  isScrolling = setTimeout(function () {
    console.log("Scrolling has stopped.", event.deltaY);
    console.log(target);

    if (event.deltaY < 0) {
      target.rotate(-1, "mouse");
    } else if (event.deltaY > 0) {
      target.rotate(1, "mouse");
    }
  }, 200);
}

 

My Issues:

  • The main timeline "init" does not repeat if any child tween has .then() callbacks
  • If I use .add() instead .then(), then all tweens starts from 0, which basically ends up with the last one, from 0 to 360 running instantly rather than by slice, 0-90, 90-180, so on. Will try ".fromTo" in the meantime, by remembering the last degree. 
  • any rotate from anywhere else than the main timeline ( in "init" method) breaks the timeline. If you would try to to scroll on the second cube, it would fall out of the chain, and later on, would be the last rotating cube.  Will try  to have a seperate tween for it.

See the Pen yLezLGm?editors=0010 by affkatron (@affkatron) on CodePen

 

Thanks for your ideas in advance.

Link to comment
Share on other sites

I'm afraid I confused more than helped you with the .then() function.

 

You still didn't answer my question about the buttons:

On 6/27/2020 at 1:41 PM, ZachSaucier said:

If someone clicks the left button, do you want both cubes to rotate? Or just one? If just one, are there going to be buttons for every cube?

 

Link to comment
Share on other sites

.then() function helps in solving my first issue, just that it came with another - tweens with .then callbacks breaks repeat settings for parent timeline.

 

Regarding your question:

If someone clicks the left button, do you want both cubes to rotate? Or just one? If just one, are there going to be buttons for every cube?

 

In my case, I do not have buttons, but rather a scroll event when mouse is being hovered on any cube. So for simplicity, let's say, buttons for each cube. What I want is that when user clicks a button (of any cube, or scrolls on any cube in my case) for master timeline keep its' time settings so that when clicking and calling rotate().  
 

rotate(dir = 1, origin) {
    this.iteration += dir;
    let delay = origin === "mouse" ? 0 : 2;

    console.log("rotate", this.iteration % 4, "wall data");

    let tl = gsap.timeline();
    console.log(this.iteration);
    tl.to(this.productCube, {
      rotateY: this.iteration * 90,
      delay: delay,
      duration: 2,
      overwrite: true,
    });
    return tl;
  }

it wouldn't interfere (make any changes in regards to time) with: 

startRotation() {
    console.log("start rotation");
    let tl = gsap.timeline();
    tl.add(this.rotate())
      .then(() => this.rotate())
      .then(() => this.rotate())
      .then(() => this.rotate());
    return tl;

 

Any help is appreciated.


 

Link to comment
Share on other sites

2 hours ago, affka said:

a scroll event when mouse is being hovered on any cube

From a user perspective, I'd have no idea that this was an option. I think it's a poor UX choice and that little to no people would use that functionality. I highly recommend using buttons if you want the user to be able to rotate the cubes :) 

 

2 hours ago, affka said:

when user clicks a button (of any cube, or scrolls on any cube in my case) for master timeline keep its' time settings so that when clicking and calling rotate().  

That's logically impossible. The cube cannot both be in the rotation location set by the user and in the location set by the timeline. If you ask me, it makes the most sense to run the timeline animation and stop the timeline animation as soon as a user interacts with the cubes.

 

Here's how I'd set it up (approximately):

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

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