Отскок шарика от краев границ блока?

В родительском блоке имеется шар:

body {
  background: black;
}

.box {
  position: relative;
  overflow: hidden;
  margin: 0 auto;
  width: 500px;
  height: 300px;
  border-radius: 5px;
  box-shadow: inset 0 0 2px black;
  background-color: white;
}

.ball {
  position: absolute;
  width: 20px;
  height: 20px;
  border-radius: 100%;
  background-color: black;
  box-shadow: inset -5px -5px 5px rgba(0, 0, 0, .5), 15px 15px 2px rgba(0, 0, 0, .05);
}
<div class="box">
  <div class="ball"></div>
</div>

Как можно заставить его бесконечно двигаться и отскакивать от краев границ этого блока?


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

Автор решения: hu-fo

var box = document.querySelector('.box')
var ball = document.querySelector('.ball')
var boxRect = box.getBoundingClientRect()
var ballRect = ball.getBoundingClientRect()

// Границы
var boundaries = {
  maxX: boxRect.width,
  maxY: boxRect.height,
  minX: 0,
  minY: 0
}

// Позиция шарика
var ballPos = {
  x: boundaries.minX,
  y: boundaries.minY
}

// эти штуки можно как-нибудь по умному назвать,
// но я тупой, поэтому просто представь, что это
// совокупность скорости и направления движения
var dx = 3,
  dy = 3

function animate() {
  // если шарик вышел за границы по оси х
  // меняем направление
  // было dx = 3, стало dx = -3
  if (
    ballPos.x < boundaries.minX ||
    ballPos.x > boundaries.maxX - ballRect.width
  ) dx = -dx

  // тоже самое только по оси y
  if (
    ballPos.y < boundaries.minY ||
    ballPos.y > boundaries.maxY - ballRect.height
  ) dy = -dy

  // ну тут всё понятно
  ballPos.x += dx
  ballPos.y += dy

  ball.style.transform = `translate(${ballPos.x}px, ${ballPos.y}px)`

  requestAnimationFrame(animate)
}

requestAnimationFrame(animate)
body {
  background: black;
}

.box {
  position: relative;
  overflow: hidden;
  margin: 0 auto;
  width: 500px;
  height: 300px;
  border-radius: 5px;
  box-shadow: inset 0 0 2px black;
  background-color: white;
}

.ball {
  position: absolute;
  width: 20px;
  height: 20px;
  border-radius: 100%;
  background-color: black;
  box-shadow: inset -5px -5px 5px rgba(0, 0, 0, .5), 15px 15px 2px rgba(0, 0, 0, .05);
}
<div class="box">
  <div class="ball"></div>
</div>

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

Хм... а почему бы это не сделать на CSS? Просто добавить две строчки анимации:

body {
  background: black;
}

.box {
  position: relative;
  overflow: hidden;
  margin: 0 auto;
  width: 500px;
  height: 300px;
  border-radius: 5px;
  box-shadow: inset 0 0 2px black;
  background-color: white;
}

.ball {
  position: absolute;
  width: 20px;
  height: 20px;
  border-radius: 100%;
  background-color: black;
  box-shadow: inset -5px -5px 5px rgba(0, 0, 0, .5), 15px 15px 2px rgba(0, 0, 0, .05);
  animation: moveX 3s linear 0s infinite alternate, moveY 3.5s linear 0s infinite alternate;
}

@keyframes moveX {from {left: 0;} to {left: 480px;}}
@keyframes moveY {from {top: 0;} to {top: 280px;}}
<div class="box"><div class="ball"></div></div>

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

А почему бы это не сделать на SVG?

<svg version="1.1" xmlns="http://www.w3.org/2000/svg"  
xmlns:xlink="http://www.w3.org/1999/xlink" height="100vh" viewBox="0 0 400 400">

<rect width="100%" height="100%" rx="25"  fill="green" stroke="#9D8500" stroke-width="15"/>
<circle cx="50%" cy="20%" r="3%" fill="url(#gradB)" >

 <animate attributeName="cx" dur="3" values="3%;97%;3%"  
   repeatCount="indefinite" /> 
 <animate attributeName="cy" dur="2.8" values="3%;97%;3%"  
   repeatCount="indefinite" />
</circle>

<circle cx="30%" cy="70%" r="3%" fill="url(#gradR)" >

 <animate attributeName="cx" dur="2.7" values="97%;3%;97%"  
   repeatCount="indefinite" /> 
 <animate attributeName="cy" dur="3.1" values="3%;97%;3%"  
   repeatCount="indefinite" />
</circle>

 <radialGradient id="gradB" cx="20%" cy="20%" r="100%" fx="30%" fy="30%">
   <stop stop-color="white" offset="0"/>
   <stop stop-color="blue" offset="25%"/>
   <stop stop-color="rgb(0,0,192)" offset="50%"/>
   <stop stop-color="rgb(0,0,127)" offset="70%"/>
   <stop stop-color="rgb(0,0,64)" offset="85%"/>
   <stop stop-color="rgb(0,0,0)" offset="100%"/>
 </radialGradient>

 <radialGradient id="gradR" cx="20%" cy="20%" r="100%" fx="30%" fy="30%">
   <stop stop-color="white" offset="0"/>
   <stop stop-color="red" offset="25%"/>
   <stop stop-color="rgb(192,0,0)" offset="50%"/>
   <stop stop-color="rgb(127,0,0)" offset="70%"/>
   <stop stop-color="rgb(64,0,0)" offset="85%"/>
   <stop stop-color="rgb(0,0,0)" offset="100%"/>
 </radialGradient>

</svg>

→ Ссылка