Анимация по полукругу

Учусь перемещать div элементы вдоль кривой и для этого нашел хороший пример. Как перемещать между двумя кадрами разобрался, а с тремя сложнее. Не понимаю какой animation-timing-function нужно указать в @keyframes moveX на 50%. А то в одном случае анимация идет вдоль кривой с двумя изгибами, в другом - замечен рывок на кадре 50%.

Вот чего хочу добиться:

.contaiter {
    position: relative;
    width: 120px;
    height: 200px;
    border: 1px solid gray;
    margin: 0 auto;
}

.x-axis, .y-axis {
    animation-duration: 3s;
    animation-fill-mode: both;
    animation-iteration-count: infinite;
    animation-timing-function: linear;
}

.x-axis {
    position: absolute;
    animation-name: moveX;
}

.y-axis {
    animation-name: moveY;
}

.dot {
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background-color: red;
}

@keyframes moveX {
    0% {
        transform: translateX(10px);
        animation-timing-function: cubic-bezier(0.333333, 1, 0.666667, 1);
    }
    50% {
        transform: translateX(100px);
    }
    100% {
        transform: translateX(10px);
    }
}

@keyframes moveY {
    0% {
        transform: translateY(190px);
        animation-timing-function: cubic-bezier(0.333333, 0.305815, 0.666667, 0.806922);
        
    }
    100% {
        transform: translateY(10px);
    }
}
<div class="contaiter">
    <div class="x-axis">
        <div class="y-axis">
            <div class="dot"></div>
        </div>
    </div>
</div>


Ответы (3 шт):

Автор решения: UModeL

Максимально приближенное движение по полуокружности с помощью стандартных значений для animation-timing-function:

.contaiter {
  margin: 0 auto;
  height: 200px; width: 120px;
  border: 1px solid gray;
  background: radial-gradient(circle at 0% 50%, #fff8 98px, #000f 100px) no-repeat;
}

.x-axis, .y-axis {
  display: inline-block;
  animation: 6s ease-in-out alternate infinite;
}

.x-axis { animation-name: moveX; }
.y-axis { animation-name: moveY; }

.dot {
  height: 10px; width: 10px;
  border-radius: 50%;
  background-color: red;
}

@keyframes moveX {
  0% {
    animation-timing-function: ease-out;
    transform: translateX(0px);
  }
  50% {
    animation-timing-function: ease-in;
    transform: translateX(94px);
  }
  100% {
    transform: translateX(0px);
  }
}

@keyframes moveY {
  0% { transform: translateY(190px); }
  100% { transform: translateY(-10px); }
}
<div class="contaiter">
  <div class="x-axis">
    <div class="y-axis">
      <div class="dot"></div>
    </div>
  </div>
</div>

Более точного и конкретного поведения можно добиться используя cubic-bezier().

→ Ссылка
Автор решения: Alexandr_TT

Решение SVG

Мне всегда интересно смотреть, как задачи SVG решаются с помощью CSS. В частности движение по криволинейной кривой. Хорошо, когда траектория движения это круг, а если это будет более сложная траектория, как во втором примере ниже?
Поверьте, я ни к чему особо не призываю, но наверное стоит присмотреться к SVG.
Многие графические задачи и анимации с помощью SVG решаются легче.

#1. Движение шарика вперед назад

Направление движение определяет пара атрибутов в animateMotion:

1.1 Вперед от начальной точки path

keyPoints="1;0" 
keyTimes="0;1"

1.2 Назад к начальной точке path

keyPoints="0;1" 
keyTimes="0;1"

1.3 Вперед - назад относительно начальной точки path

keyPoints="1;0;1" 
keyTimes="0;0.5;1"

<div class="container" style="width:20vw;height:auto;">
<div><button onclick="forward.beginElement()">forward</button></div>

<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
        viewBox="0 10 125 125" >  

 <path id="circ1" fill="none" stroke="black" stroke-dasharray="5 5" d="M60,20 A50,50 0 0, 1 60 120" /> 
  <circle class="circ1" r="5" fill="red">
     <animateMotion
       id="forward"
       dur="4s"
       begin="indefinite"
       repeatCount="indefinite"
       keyPoints="0;1;0"
       keyTimes="0;0.5;1"
       calcMode="linear"
       restart="whenNotActive">
       <mpath xlink:href="#circ1"/>
       </animateMotion>
      </circle> 
</svg>   
</div>

#2. Движение шарика по синусоидальной траектории

Три кнопки управляют движением шарика вперед, с середины траектории, назад,

    <button onclick="forward.beginElement()">forward</button>
    <button onclick="middle.beginElement()">Middle</button>
    <button onclick="back.beginElement()">Back</button>

<div>
    <button onclick="forward.beginElement()">forward</button>
    <button onclick="middle.beginElement()">Middle</button>
    <button onclick="back.beginElement()">Back</button>
</div>  
<div id="container">
            <svg id="svg1" height="160" width="360">
  <g  transform="translate(0 -10)"  stroke="black" stroke-width="1">
    <path fill="none" stroke-dasharray="3" id="circ1"
        d="M 10 80 C 40 10, 65 10, 95 80 S 150 150, 180 80" />

       <circle class="circle2" r="8" fill="red">
             
     <animateMotion
       id="forward"
       dur="2s"
       begin="indefinite"
       repeatCount="1"
       keyPoints="0;1"
       keyTimes="0;1"
       calcMode="linear"  >
         <mpath href="#circ1" />
     </animateMotion> 
        <animateMotion
           id="middle"
           dur="2s"
           begin="indefinite"
           repeatCount="1"
           keyPoints="0.5;1"
           keyTimes="0;1"
           calcMode="linear" >
         <mpath href="#circ1" />
        </animateMotion> 
           <animateMotion
           id="back"
           dur="2s"
           begin="indefinite"
           repeatCount="1"
           keyPoints="1;0"
           keyTimes="0;1"
           calcMode="linear" >
         <mpath href="#circ1" />
        </animateMotion> 
         </circle> 
 </g>        
</svg>

→ Ссылка
Автор решения: Михаил Камахин

Можно ещё вот так на CSS

Использована CSS функция @property

Работает только в Chromium движках (02 апреля 2021 года)

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  min-height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
  background: #d1f3fa;
}

@property --x {
  inherits: false;
  initial-value: 0;
  syntax: '<number>';
}

@property --y {
  inherits: false;
  initial-value: 0;
  syntax: '<number>';
}

@property --rotate {
  inherits: false;
  initial-value: 0deg;
  syntax: '<angle>';
}

.ball {
  height: 10vmin;
  width: 10vmin;
  border-radius: 50%;
  fill: #e69119;
  background: #a16512;
  animation: throw 2s infinite alternate ease-in-out;
  transform: translateX(calc( var(--x) * 1vmin)) translateY(calc( var(--y) * 1vmin)) rotate(var(--rotate));
}

@keyframes throw {
  0% {
    --y: -40;
    --rotate: 0deg;
  }
  50% {
    --x: -30;
  }
  100% {
    --y: 40;
    --rotate: 360deg;
  }
}
<svg class="ball" viewBox="0 0 496 512" title="basketball-ball">
    <path d="M212.3 10.3c-43.8 6.3-86.2 24.1-122.2 
    53.8l77.4 77.4c27.8-35.8 43.3-81.2 44.8-131.2zM248 
    222L405.9 64.1c-42.4-35-93.6-53.5-145.5-56.1-1.2 63.9-21.5
    122.3-58.7 167.7L248 222zM56.1 98.1c-29.7 36-47.5 78.4-53.8
    122.2 50-1.5 95.5-17 131.2-44.8L56.1 98.1zm272.2 204.2c45.3-37.1
    103.7-57.4 167.7-58.7-2.6-51.9-21.1-103.1-56.1-145.5L282
    256l46.3 46.3zM248 290L90.1 447.9c42.4 34.9 93.6 53.5
    145.5 56.1 1.3-64 21.6-122.4 58.7-167.7L248 290zm191.9
    123.9c29.7-36 47.5-78.4 53.8-122.2-50.1 1.6-95.5 17.1-131.2
    44.8l77.4 77.4zM167.7 209.7C122.3 246.9 63.9 267.3 0
    268.4c2.6 51.9 21.1 103.1 56.1 145.5L214 256l-46.3-46.3zm116
    292c43.8-6.3 86.2-24.1 122.2-53.8l-77.4-77.4c-27.7
    35.7-43.2 81.2-44.8 131.2z" />
  </svg>

→ Ссылка