Infinite animation of an object flying a path using CSS3

Viewed 1222

Good day, there was the task to make the animation of an airplane flying around a path. I decided to take advantage of the opportunities in CSS3. But all I have achieved is one animation cycle. The plane flies one circle around the path and the animation stops. I tried using animation-iteration-count with infinite, but all I got was the flight of a plane in chaotic directions. Below is my code, please tell me how to loop this animation so that the plane constantly flies in a circle without stopping.

Code

.wrap {
  margin: 100px;
}

.route {
  height: 200px;
  width: 400px;
  border: 3px dotted #000;
  position: relative;
}

.plane {
  position: absolute;
  bottom: -13px;
  left: 100%;
  animation-iteration-count: 3;
  animation: flyLeft 1.5s linear forwards, rotatePlane 0.5s linear 1.5s forwards, flyUp 1s linear forwards 2s, RotateRight 0.5s linear 2.8s forwards, MoveRight 3s linear forwards 3s, RotateDown 1s linear 6s forwards, flyDown 1s linear forwards 7s, RotateLeft 1s linear 7.8s forwards;
}

@keyframes flyLeft {
  100% {
    left: -14px;
  }
}

@keyframes rotatePlane {
  100% {
    transform: rotateZ(90deg);
  }
}

@keyframes flyUp {
  100% {
    bottom: 100%;
  }
}

@keyframes RotateRight {
  0% {
    transform: rotateZ(90deg);
  }
  100% {
    transform: rotateZ(180deg);
  }
}

@keyframes MoveRight {
  0% {
    left: -14px;
  }
  100% {
    left: 380px;
  }
}

@keyframes RotateDown {
  0% {
    transform: rotateZ(180deg);
  }
  100% {
    transform: rotateZ(270deg);
  }
}

@keyframes flyDown {
  0% {
    bottom: 100%;
  }
  100% {
    bottom: -8%;
  }
}

@keyframes RotateLeft {
  0% {
    transform: rotateZ(270deg);
  }
  100% {
    transform: rotateZ(360deg);
  }
}
<div class="wrap">
  <div class="route">
    <img class="plane" src="http://p36099-290-14699.s290.upress.link/wp-content/uploads/2020/05/plane.png">
  </div>
</div>

3 Answers

You need to wrap all the animations in one @keyframes CSS at-rules to easily make repetitions. Here's a working solution below that wraps all the animations in one @keyframes.

.wrap {
  margin: 100px;
}

.route {
  height: 200px;
  width: 400px;
  border: 3px dotted #000;
  position: relative;
}

.plane {
  position: absolute;
  right: 0;
  bottom: 0;
  transform: translate(50%, 50%);
  animation: travelRoundTheBorder 10s linear infinite;
}

@keyframes travelRoundTheBorder {
  30% {
    bottom: 0;
    right: 100%;
    transform: translate(50%, 50%);
  }
  
  32.5% {
    bottom: 0;
    right: 100%;
    transform: translate(50%, 50%) rotate(90deg);
  }
  
  47.5% {
    right: 100%;
    bottom: 100%;
    transform: translate(50%, 50%) rotate(90deg);
  }
  
  50% {
    right: 100%;
    bottom: 100%;
    transform: translate(50%, 50%) rotate(180deg);
  }
  
  80% {
    right: 0;
    bottom: 100%;
    transform: translate(50%, 50%) rotate(180deg);
  }
  
  82.5% {
    right: 0;
    bottom: 100%;
    transform: translate(50%, 50%) rotate(270deg);
  }
  
  97.5% {
    right: 0;
    bottom: 0;
    transform: translate(50%, 50%) rotate(270deg);
  }
  
  100% {
    right: 0;
    bottom: 0;
    transform: translate(50%, 50%) rotate(360deg);
  }
}
<div class="wrap">
  <div class="route">
    <img class="plane" src="http://p36099-290-14699.s290.upress.link/wp-content/uploads/2020/05/plane.png">
  </div>
</div>

Splitting the movement along the path and the turns into TWO separate keyframes makes this easier.

The math of the percentages is based on a square but with a rectangle the percentages change.

CSS variable could help here to work out those percentages but I haven't gone deeper into that for the demo purposes.

.wrap {
  margin: 10px;
}

.route {
  height: 150px;
  width: 150px;
  margin: auto;
  border: 3px dotted #000;
  position: relative;
}

.plane {
  position: absolute;
  transform: translate(-50%, -50%) rotate(180deg);
  top: 0;
  left: 0;
  animation: path 6s linear infinite, turn 6s ease infinite;
}

@keyframes path {
  0%,
  100% {
    left: 0;
    top: 0;
  }
  25% {
    left: 100%;
    top: 0;
  }
  50% {
    left: 100%;
    top: 100%;
  }
  75% {
    left: 0;
    top: 100%;
  }
}

@keyframes turn {
  0%,
  24% {
    transform: translate(-50%, -50%) rotate(180deg);
  }
  25%,
  49% {
    transform: translate(-50%, -50%) rotate(270deg);
  }
  50%,
  74% {
    transform: translate(-50%, -50%) rotate(0deg);
  }
  75%,
  99% {
    transform: translate(-50%, -50%) rotate(90deg);
  }
  100% {
    transform: translate(-50%, -50%) rotate(90deg);
  }
}
<div class="wrap">
  <div class="route">
    <img class="plane" src="http://p36099-290-14699.s290.upress.link/wp-content/uploads/2020/05/plane.png">
  </div>
</div>

Just to begin testing new CSS posibilities, offset-path (not supported in IE, experimental in FF)

reference

#container {
  width: 400px;
  height: 300px;
  border: dotted 5px black;
  margin: 30px;
    
}

#motion-demo {
  offset-path: path('M0 -10 H400 A 10 10 1 0 1 410 0 V300 A 10 10 1 0 1 400 310 H0 A 10 10 1 0 1 -10 300 V0');
  animation: move 10s infinite linear;
  width: 40px;
  height: 40px;
  background: cyan;
}

@keyframes move {
  0% {
    offset-distance: 0%;
  }
  100% {
    offset-distance: 100%;
  }
}
<div id="container">
<div id="motion-demo">A</div>
</div>

Related