Skip to content
Advertisement

CSS chain multiple animations from different classes

I am developing a ReactJs app, I need to animate a bus in a visual path.

The bus should make stops. So the bus first go from A -> B , then when the user click a button it goes from B -> C , ect..

I have already made the animations and the logic

.bus_0 {
  animation: move 3s linear forwards;
}

.bus_1 {
  animation: move2nd 3s linear forwards;
}

@keyframes move {
  20% {
    transform: translate(50px, 0px);
  }
  40% {
    transform: translate(50px, 0px) rotate(-90deg);
  }
  80% {
    transform: translate(50px, -130px) rotate(-90deg);
  }
  100% {
    transform: translate(50px, -125px) rotate(0deg);
  }
}

@keyframes move2nd {
  20% {
    transform: translate(100px, 0px);
  }
  40% {
    transform: translate(100px, 0px) rotate(-90deg);
  }
  100% {
    transform: translate(100px, -50px) rotate(-90deg);
  }
}

Here I add classNames based on an index

let bus = document.getElementById('bus');
bus && bus.classList.add(`bus_${data.selectedIndex}`); 

The problem is when the first animation starts, the bus reachs point B , but for the second animation , it starts from point A.

Example the bus first position (0, 0), after the first animation (100, 100), when the second animation plays it start from (0, 0) but i need it the start from the last position reached.

Advertisement

Answer

There must be a better way probably but that is what i got and i think it will solve your problem

So i collect boxes x and y coordinates when animation button clicks and after the animationend event, i collect both x and y coordinates again and calculate difference of them and add to box style.

const firstButton = document.querySelectorAll('button')[0];

const secondButton = document.querySelectorAll('button')[1];

const myBox = document.querySelector('div')

let rectBefore;
let rectAfter;
let positionBeforeX;
let positionBeforeY;
let positionAfterX;
let positionAfterY;

let differenceX;
let differenceY;


firstButton.addEventListener('click', () => {
    rectBefore = myBox.getBoundingClientRect();
    positionBeforeX  = rectBefore.left;
    positionBeforeY  = rectBefore.top;
    
    myBox.classList.toggle('first');
})

secondButton.addEventListener('click', () => {
    rectBefore = myBox.getBoundingClientRect();
    positionBeforeX  = rectBefore.left;
    positionBeforeY  = rectBefore.top;

    myBox.classList.toggle('second');

})

myBox.addEventListener('animationend', (event) =>{
    rectAfter = myBox.getBoundingClientRect();
    positionAfterX = rectAfter.left;
    positionAfterY = rectAfter.top;

    differenceX = positionAfterX - positionBeforeX;
    differenceY = positionAfterY - positionBeforeY;

    if(myBox.style.left !== ""){
        myBox.style.left = `${parseInt(myBox.style.left.split('px')) + differenceX}px`;
        myBox.style.top = `${parseInt(myBox.style.top.split('px')) + differenceY}px`;
    }
    else{
        myBox.style.left = `${differenceX}px`;
        myBox.style.top = `${differenceY}px`;
    }
    
    myBox.classList.remove(`${event.animationName}`);
})
*,
*::before,
*::after {
  box-sizing: border-box;
}

body{
  min-height: 100vh;
  position: relative;
  display: grid;
  place-content: center;
}

button{
  position: absolute;
  background-color: greenyellow;
  width: 5rem;
  height: 5rem;
  
}

button:nth-of-type(1){
  top:5rem;
  right: 10rem;
  margin-right: 1rem;
}

button:nth-of-type(2){
  top:5rem;
  right: 5rem;
}

.box{
  position:relative;
  width: 100px;
  height: 100px;
  background-color: blue;

}

.first {
  animation: first 3.0s linear forwards;
}

.second {
  animation: second 3.0s linear forwards;
}

@keyframes first {
  20% {
    transform: translate(50px, 0px);
  }
  40% {
    transform: translate(50px, 0px) rotate(-90deg);
  }
  80% {
    transform: translate(50px, -130px) rotate(-90deg);
  }
  100% {
    transform: translate(50px, -125px) rotate(0deg);
  }
}

@keyframes second {
  20% {
    transform: translate(100px, 0px);
  }
  40% {
    transform: translate(100px, 0px) rotate(-90deg);
  }
  100% {
    transform: translate(100px, -50px) rotate(-90deg);
  }
}
<div class="box"></div>

<button>First Animation</button>
<button>Second Animation</button>
User contributions licensed under: CC BY-SA
3 People found this is helpful
Advertisement