<div id="root"></div>
body {
  margin: 0;
  background: #f1f1f1;
  position: relative;
}

.container {
  position: absolute;
  top: 50vh;
  left: 50%;
  transform: translate(-50%, -50%);
  background: #fff;
  width: 700px;
  height: 400px;
  position: relative;
  border-radius: 4px;
  box-shadow: 0 0 30px 15px rgba(200, 200, 200,.75);
  overflow: hidden;
}

.part {
  position: absolute;
  &__figure {
    border-radius: 50%;
  }
}
View Compiled
const data = [
  {
    color: '#51c4c2',
    diameter: 90,
    position: {
      left: 180,
      top: 45,
    },
    interval: 1,
    distance: 40,
    step: 7,
  },
  {
    color: '#1fbecf',
    diameter: 350,
    position: {
      left: -100,
      top: 150,
    },
    interval: 1,
    distance: 25,
    step: 5,
  },
  {
    color: '#60c7bc',
    diameter: 170,
    position: {
      left: 300,
      top: 300,
    },
    interval: 1,
    distance: 30,
    step: 8,
  },
  {
    color: '#f47f61',
    diameter: 150,
    position: {
      left: 600,
      top: 150,
    },
    interval: 1,
    distance: 20,
    step: 5,
  },
  {
    color: '#f05572',
    diameter: 60,
    position: {
      left: 510,
      top: 170,
    },
    interval: 1,
    distance: 25,
    step: 6,
  },
  {
    color: '#f1666e',
    diameter: 100,
    position: {
      left: 535,
      top: 50,
    },
    interval: 1,
    distance: 10,
    step: 3,
  },
];

class MovingPart extends React.Component {
  constructor(props) {
    super(props);

    this.maxRotate = 55;
    this.deg = +(Math.random() * 360).toFixed();
    this.state = { x: 0, y: 0 };
  }

  componentDidMount() {
    const { interval, distance, step } = this.props;
    const { maxRotate, getShift } = this;
    
    setInterval(() => {
      this.setState((prevState) => {
        this.deg += +(Math.random() * maxRotate * 2 - maxRotate).toFixed();
        let shift = getShift(this.deg, step);
        while (Math.abs(prevState.x + shift.x) >= distance || Math.abs(prevState.y + shift.y) >= distance) {
          this.deg += +(Math.random() * maxRotate * 2 - maxRotate).toFixed();
          shift = getShift(this.deg, step);
        }
        return {
          x: prevState.x + shift.x,
          y: prevState.y + shift.y,
        };
      });
    }, interval * 1000);
  }

  getShift = (deg, step) => {
    return {
      x: +(Math.cos(deg * Math.PI / 180) * step).toFixed(),
      y: +(Math.sin(deg * Math.PI / 180) * step).toFixed(),
    };
  };

  render() {
    const { interval, children } = this.props;
    const { x, y } = this.state;
    
    return (
      <div style={{
          transform: `translate(${x}px,${y}px)`,
          transition: `transform ${interval}s linear`,
          'backface-visibility': 'hidden',
        }}>
        {children}
      </div>
    );
  }
}

ReactDOM.render((
  <div className="container">
    {data.map((item) => (
      <div 
        className="part" 
        style={{
          left: `${item.position.left}px`,
          top: `${item.position.top}px`,
        }}>
        <MovingPart
          interval={item.interval}
          distance={item.distance}
          step={item.step}
          >
          <div 
            className="part__figure" 
            style={{
              background: item.color,
              width: `${item.diameter}px`,
              height: `${item.diameter}px`,
            }}
           />
        </MovingPart>
      </div>
    ))}
  </div>
), document.getElementById('root'));
View Compiled
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/react/16.8.2/umd/react.production.min.js
  2. https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.2/umd/react-dom.production.min.js