Jump to content
Search Community

3d book animations problem

Valentin__ test
Moderator Tag

Recommended Posts

Hi all,
 

I have several problems with the animation of my book.
 

1. I tried to make a rotateY on my book but the controls did not work on my code (play(), pause(), paused()), so I make a rotate in CSS that I disable by adding or removing a class
 

2. The animation works perfectly on the first opening and closing cycle, but on the following ones the currentAngle variable is buggy
 

3. I would like to be able to click on the book only if the animation is finished

Here is my codehttps://codesandbox.io/s/sad-mcnulty-7k49my

 

I really need your help ;)

Thanks to all

 

Link to comment
Share on other sites

I'm not sure I understand your questions, but please keep in mind that you should always apply transforms directly via GSAP. It looks to me like you're using a CSS animation to affect transforms which will be problematic. You can set parseTransform: true to force it in a particular tween, but that's not ideal. Here's why: 

 

 

There's an undocumented feature the lets you force it to ignore cached values using the 4th parameter: 

// old
let currentAngle = gsap.getProperty(".book", "rotationY");

// new
let currentAngle = parseFloat(gsap.getProperty(".book", "rotationY", "deg", true));

 

But I'd definitely recommend using GSAP for the rotation rather than CSS animations (mixing CSS and GSAP on the same property is almost never a good idea). 

 

By the way, you can't set a duration on a timeline like this: 

gsap.timeline({ duration: 1 });

Timelines are just wrappers for child tweens/timelines, thus their duration is determined by their contents. 

 

You're also setting tl.duration(0.1) and tl.duration(0.3) before you even populate it which is super odd because when you set the duration of a timeline that only adjusts the timeScale() accordingly so that won't work well if it's empty and has a duration of 0. If you want to adjust the timeScale like that, you should set the duration of the timeline AFTER you populate it. 

 

I don't have time to go over it with a fine tooth comb, but here's a quick tweak of your useEffect(): 

useEffect(() => {
  let ctx = gsap.context(() => {
    let tl = gsap.timeline();

    switch (isOpen) {
      case false:
        tl.fromTo(
          ".book_pages li:nth-child(2)",
          { rotationY: -140, parseTransform: true },
          { rotationY: 1, stagger: 1 }
        );
        tl.fromTo(
          ".book_pages li:nth-child(1)",
          { rotationY: -140, parseTransform: true },
          { rotationY: 1, stagger: 1 }
        )
          .fromTo(
          containerBookRef.current.querySelector(".book"),
          {
            x:
            containerBookRef.current.querySelector(".book").offsetWidth /
            2, parseTransform: true
          },
          { x: 0 }
        )
          .fromTo(
          containerBookRef.current
          .querySelector(".book")
          .querySelector(".book_front"),
          { rotationY: -145, parseTransform: true },
          { rotationY: 0 }
        )
          .call(() => {
          // Callback timeline
          containerBookRef.current
            .querySelector(".book")
            .classList.add("rotate");
        });
        tl.duration(0.5);
        break;
      case true:

        let currentAngle = parseFloat(gsap.getProperty(".book", "rotationY", "deg", true));

        let remainingRotation = 360 - parseInt(currentAngle, 10);

        containerBookRef.current
          .querySelector(".book")
          .classList.remove("rotate");

        const openBook = () => {

          tl.fromTo(".book_front", { rotationY: 0, parseTransform: true }, { rotationY: -145 })
            .fromTo(
            ".book",
            { x: 0 },
            {
              x:
              containerBookRef.current.querySelector(".book")
              .offsetWidth / 2
            }
          )
            .fromTo(
            ".book_pages li:nth-child(1)",
            { rotationY: 1 },
            { rotationY: -140, stagger: 1 }
          )
            .fromTo(
            ".book_pages li:nth-child(2)",
            { rotationY: 1 },
            { rotationY: -140, stagger: 1 }
          );
          tl.duration(0.5);
        };

        // On remet le livre dans sa position initial puis on l'ouvre
        gsap.to(".book", {
          rotationY: `+=${remainingRotation}`,
          parseTransform: true,
          duration: 1,
          onComplete: openBook
        });
        break;

      default:
        break;
    }
  }, containerBookRef);

  return () => ctx.revert();
}, [isOpen]);

There are warnings in the console about parseTransform but those will be gone in the next release and can be safely ignored. 

 

Again, I'd strongly recommend getting rid of the parseTransform stuff and just use GSAP do to all your animation instead of using a CSS animation mixed with GSAP. :)

Link to comment
Share on other sites

Thanks for your answer :)

I will try to explain a little better what I would like to do.

The animation consists in making a book spin endlessly.
At the first click on the book it should finish its rotation to its initial position (0deg) then open
At the second click it must close then restart the infinite rotation

I understood that mixing GSAP and CSS was not good, but I did this because I can't use the animation controls (nothing works on my React project)

Sorry for my English, I'm French :/

Link to comment
Share on other sites

4 hours ago, Valentin__ said:

I understood that mixing GSAP and CSS was not good, but I did this because I can't use the animation controls (nothing works on my React project)

 

I don't understand what you mean. You can't get GSAP to animate the rotationY and/or you can't pause()/resume()? Sorry, I don't follow. It should be very doable with GSAP to create a tween that animates rotationY and then pause()/resume(). If you're stuck with that, please post a minimal demo showing your attempt. 

 

You will definitely increase your chances of getting a good answer here if you can provide a minimal demo that doesn't leverage a 3rd party framework like React. 

Link to comment
Share on other sites

Vague details like 'the angle isn’t good' or 'it doesn't work' are very difficult for people to help with. Here are some tips that will increase your chance of getting a solid answer:
 

  • A clear description of the expected result - "I am expecting the purple div to spin 360degrees"
  • A clear description of the issue -  "the purple div only spins 90deg"
  • A list of steps for someone else to recreate the issue - "Open the demo on mobile in IOS safari and scroll down to the grey container" 
  • A minimal demo - if possible, using no frameworks, with minimal styling, only include the code that's absolutely necessary to show the issue. Please don't include your whole project. Just some colored <div> elements is great.

 

Link to comment
Share on other sites

Hi,

 

I don't know if this is related to the final angle of the book, the initial angle (there is a jump when the book is clicked) or both.

 

I don't have time to go through all your code so instead I created a super simple example to explain my approach:

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

 

Basically you have the endless rotation stored, what you can do when you click the book is to pause that timeline and animate the remainder progress of it at a greater speed (short time), then use the onComplete callback to do the pages animation. Then when the book is closed, simply restart the rotation.

 

Hopefully this gets you closer to what you're trying to achieve. If is not, please be more specific and even fork the example above.

 

Happy Tweening!

Link to comment
Share on other sites

Hi,

 

Please be super specific about the problem you're facing, what we should pay attention in your example and how it should actually work. Be as descriptive as you can, if possible add some images showing both cases (what you need v/s what you're getting). I played with your latest example and I can't tell what the issue is exactly.

 

Happy Tweening!

Link to comment
Share on other sites

Hi,

 

 

Are you still having issues preventing a click event on the book while the rotation animation is running?

 

If so just create a boolean to prevent the code from being executed and toggle that boolean once the animation is completed:

let canClick = true;
const t = gsap.to(book, {
  duration: 6,
  ease: "none",
  repeat: -1,
  rotation: 360,
});

book.addEventListener("click", () => {
  if (!canClick) = return;
  canClick = false;
  gsap.to(t, {
    progress: 1,
    duration: 1,
    ease: "power2.out",
    onComplete: () => canClick = true,
  });
});

If this is about something else, please be specific about it.

 

Happy Tweening!

  • Like 1
Link to comment
Share on other sites

Yeah, when you say "The only thing left is the angle which is not very functional," I have no idea what that means, sorry. Can you please be very specific about what exactly the problem is and the desired result? I think we're all struggling to understand what you're looking for, what you think is broken, etc. 

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