<div class="container">
  <div class="box"></div>
  <div class="box"></div>
  <div class="box"></div>
  <div class="box"></div>
</div>
body {
  min-height: 100vh;
  margin: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  background: #f5f5f5;
  overflow: hidden;
}

.container {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  grid-template-rows: repeat(2, 1fr);
}

.box {
  width: 100px;
  height: 100px;
  background: var(--color, #eeeeee);
}

.box:nth-child(1) {
  --color: #ED1C24;
}

.box:nth-child(2) {
  --color: #22B14C;
}

.box:nth-child(3) {
  --color: #00A2E8;
}

.box:nth-child(4) {
  --color: #FFC90E;
}
const boxes = document.querySelectorAll('.box');
const items = [];

const Mode = Object.freeze({
  Idle: 1 << 0,
  Move: 1 << 1,
  Return: 1 << 2
});

const update = () => {
  for (const item of items) {
    if (item.mode !== Mode.Idle) {
      item.position.x += item.velocity.x;
      item.position.y += item.velocity.y;

      item.entity.style.setProperty('transform', `translate(${item.position.x}px, ${item.position.y}px)`);

      if (item.position.x !== 0 || item.position.y !== 0) {
        if (item.mode === Mode.Move) {
          item.velocity.x *= 0.975;
          item.velocity.y *= 0.975;

          if (Math.abs(item.velocity.x) < 0.1 && Math.abs(item.velocity.y) < 0.1) {
            [item.mouse.current.x, item.mouse.current.y] = [null, null];
            [item.mouse.previous.x, item.mouse.previous.y] = [null, null];

            item.mode = Mode.Return;
          }
        } else {
          [item.velocity.x, item.velocity.y] = [item.position.x / -10, item.position.y / -10];

          if (Math.abs(item.velocity.x) < 0.1 && Math.abs(item.velocity.y) < 0.1) {
            [item.position.x, item.position.y] = [0, 0];
            [item.velocity.x, item.velocity.y] = [0, 0];
          }
        }
      } else {
        item.mode = Mode.Idle;
      }
    }
  }
  
  requestAnimationFrame(update);
};

const init = () => {
  for (const box of boxes) {
    const item = {
      entity: box,
      position: {
        x: 0,
        y: 0
      },
      velocity: {
        x: 0,
        y: 0
      },
      mouse: {
        current: {
          x: null,
          y: null
        },
        previous: {
          x: null,
          y: null
        }
      },
      mode: Mode.Idle
    };

    box.addEventListener('mousemove', event => {
      [item.mouse.current.x, item.mouse.current.y] = [event.offsetX, event.offsetY];

      if (item.mouse.previous.x !== null && item.mouse.previous.y !== null) {
        item.velocity.x += (item.mouse.current.x - item.mouse.previous.x) / 25;
        item.velocity.y += (item.mouse.current.y - item.mouse.previous.y) / 25;
      }
      
      [item.mouse.previous.x, item.mouse.previous.y] = [item.mouse.current.x, item.mouse.current.y];
      
      item.mode = Mode.Move;
    });
    
    box.addEventListener('mouseleave', event => {
      [item.mouse.current.x, item.mouse.current.y] = [null, null];
      [item.mouse.previous.x, item.mouse.previous.y] = [null, null];
    });
    
    items.push(item);
  }
  
  requestAnimationFrame(update);
};

window.addEventListener('DOMContentLoaded', init);

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.