cssreactjscss-animationsconditional-operatorreact-animations

How to play rotate-clockwise animation when slide-out-tl animation ends also final keyframe position is to be maintained before roatation


Certainly! I am seeking assistance with synchronizing CSS animations within a React component. Specifically, you want to ensure that one animation (`slide-out-tl`) plays immediately upon component rendering, maintaining its final position, while another animation (`rotate-clockwise`) starts infinitely only after the first animation concludes.

// Landing.js
import React from "react";
import logo from "/Sm.png";
import cover from "/logo_cover.png";
import "./Landing.css";

export default function Landing({ rotation, setLoading, setRotation }) {
  const handleAnimationEnd = () => {
    setRotation(true); // Start rotating the cover
    setLoading((prev) => !prev); // Corrected syntax
  };

  return (
    <div className="relative z-50">
      <img
        src={cover}
        alt="cover"
        className={`cover w-28 absolute top-64 z-10 left-[43rem] animated-slide-out-tl-cover 
         
        `}
        onAnimationEnd={handleAnimationEnd}
      />
      <img
        src={logo}
        alt="logo"
        className="logo absolute z-20 top-[13.5rem] left-[41.5rem] scale-110 opacity-100"
      />
    </div>
  );
}
.animated-slide-out-tl-cover {
  animation: slide-out-tl 1s  ease-in-out  1s forwards; /* Change 'both' to 'forwards' to retain final keyframe */
}

@keyframes slide-out-tl {
  0% {
    transform: translate(0, 0);
    opacity: 0;
  }
  100% {
    transform: translate(-555.8px, -231px);
    opacity: 1;
  }
}

@keyframes rotate-clockwise {
  0% {
    transform: translate(-555.8px, -231px);
    transform: rotate(0deg); /* Start rotating from 0 degrees */
  }
  100% {
    transform: translate(-555.8px, -231px);
    transform: rotate(360deg); /* Rotate 360 degrees for a full cycle */
  }
}

/* Adjust the scale and translation of the logo */
.logo {
  position: absolute;
  opacity: 1;
  animation: logo-animation 1s ease-in-out 1s both; /* Add 1 second delay */
  transform: scale(1) translate(0, 0); /* Set initial scale and translation */
}

@keyframes logo-animation {
  0% {
    transform: translate(0, 0) scale(1);
    opacity: 1;
  }
  100% {
    transform: translate(-555.5px, -232px) scale(0.16);
    opacity: 1;
  }
}

.rotate-clockwise {
  position: absolute;
  animation: rotate-clockwise 8s infinite 1s linear; /* Add slide-out-tl animation to rotate-clockwise */
}

I tried conditionally adding rotate-clockwise class to cover image like this->

import React, { useState } from "react";
import logo from "/Sm.png";
import cover from "/logo_cover.png";
import "./Landing.css";

export default function Landing({ setLoading, setRotation }) {
  const [animationFinished, setAnimationFinished] = useState(false);
  const [rotateAnimationStarted, setRotateAnimationStarted] = useState(false);

  const handleSlideOutEnd = () => {
    setRotation(true);
    setAnimationFinished(true);
    setLoading((prev) => !prev);
    setRotateAnimationStarted(true);
    console.log('completed');
  };

  return (
    <div className="relative z-50">
      <img
        src={cover}
        alt="cover"
        className={`cover w-28 absolute top-64 z-10 left-[43rem] animated-slide-out-tl-cover ${
          animationFinished ? "rotate-clockwise" : ""
        }`}
        onAnimationEnd={handleSlideOutEnd}
      />
      <img
        src={logo}
        alt="logo"
        className={`logo absolute z-20 top-[13.5rem] left-[41.5rem] scale-110 opacity-100 ${
          rotateAnimationStarted ? "rotate-clockwise" : ""
        }`}
      />
    </div>
  );
}

Solution

  • There is no need to conditionally add rotate-clockwise class. In fact, both animation needs to be specified together separated by a comma, or the first animation will be overwriten. Since slide-out-tl have a 1 second duration and 1 second delay, simply set a 2 second delay on rotate-clockwise.

    Next, since both animation is sharing the transform property, it will still be overwritten even if you are using forward. To retain translate(-555.8px, -231px) from the first animation, you need to include it in the second animation's transform in the same line.

    .animated-slide-out-tl-cover {
      animation: 
        slide-out-tl 1s ease-in-out 1s forwards,
        rotate-clockwise 8s linear 2s infinite;
    }
    
    @keyframes slide-out-tl {
      0% {
        transform: translate(0, 0);
        opacity: 0;
      }
      100% {
        transform: translate(-555.8px, -231px);
        opacity: 1;
      }
    }
    
    @keyframes rotate-clockwise {
      0% {
        /* retain the translated position from the last frame of the previous animation */
        transform: translate(-555.8px, -231px) rotate(0deg); 
      }
      100% {
        transform: translate(-555.8px, -231px) rotate(360deg); 
      }
    }
    
    /* Adjust the scale and translation of the logo */
    .logo {
      position: absolute;
      opacity: 1;
      animation: logo-animation 1s ease-in-out 1s both; 
      transform: scale(1) translate(0, 0);
    }
    
    @keyframes logo-animation {
      0% {
        transform: translate(0, 0) scale(1);
        opacity: 1;
      }
      100% {
        transform: translate(-555.5px, -232px) scale(0.16);
        opacity: 1;
      }
    }
    
    export default function Landing({ setLoading, setRotation }) {
      const handleSlideOutEnd = () => {
        setRotation(true);
        setLoading((prev) => !prev);
        console.log('completed');
      };
    
      return (
        <div className="relative z-50">
          <img
            src={cover}
            alt="cover"
            className={`cover w-28 absolute top-64 z-10 left-[43rem] animated-slide-out-tl-cover `}
            onAnimationEnd={handleSlideOutEnd}
          />
          <img
            src={logo}
            alt="logo"
            className={`logo absolute z-20 top-[13.5rem] left-[41.5rem] scale-110 opacity-100`}
          />
        </div>
      );
    }