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>