.container
  svg
    path
  .element
View Compiled
:root {
  --size: 75;
}
* {
  box-sizing: border-box;
}
body {
  display: -webkit-box;
  display: flex;
  -webkit-box-align: center;
          align-items: center;
  -webkit-box-pack: center;
          justify-content: center;
  min-height: 100vh;
  background: #ddd;
  -webkit-box-orient: vertical;
  -webkit-box-direction: normal;
          flex-direction: column;
}
@media (prefers-color-scheme: dark) {
  body {
    background: #222;
  }
}
.container {
  position: relative;
  height: calc(var(--size) * 1vmin);
  width: calc(var(--size) * 1vmin);
  border: 2px solid #222;
}
@media (prefers-color-scheme: dark) {
  .container {
    border: 2px solid #ddd;
  }
}
.element {
  height: 40px;
  width: 40px;
  background: rgba(128,191,255,0.5);
  border: 2px #80bfff solid;
  position: absolute;
  top: 0%;
  left: 0%;
  offset-path: path(var(--path));
  -webkit-animation: travel 2s infinite alternate linear;
          animation: travel 2s infinite alternate linear;
}
svg {
  position: absolute;
  height: calc(var(--size) * 1vmin);
  opacity: 0.5;
  width: calc(var(--size) * 1vmin);
}
svg path {
  fill: none;
  stroke-width: 4px;
  stroke: #222;
}
@media (prefers-color-scheme: dark) {
  svg path {
    stroke: #ddd;
  }
}
@-webkit-keyframes travel {
  from {
    offset-distance: 0%;
  }
  to {
    offset-distance: 100%;
  }
}
@keyframes travel {
  from {
    offset-distance: 0%;
  }
  to {
    offset-distance: 100%;
  }
}
const { d3 } = window

const POINTS = [
  [0, 0],
  [5, -5],
  [10, 0],
  [15, 5],
  [20, 0]
]

const CONTAINER = document.querySelector('.container')
const PADDING = 40
const assignPath = () => {
  const { height: size } = CONTAINER.getBoundingClientRect()
  // Create an X scale
  const xScale = d3.scaleLinear().domain([0, 20]).range([PADDING, size - PADDING])
  // Create a Y scale
  const yScale = d3.scaleLinear().domain([-5, 5]).range([PADDING, size - PADDING])
  // Map the POINTS using our scales
  const SCALED_POINTS = POINTS.map(POINT => [xScale(POINT[0]), yScale(POINT[1])])
  // Generate a path string with our points
  const LINE = d3.line().curve(d3.curveBasis)(SCALED_POINTS)
  // Assign attributes and path values
  d3.select('svg')
    .attr('viewBox', `0 0 ${size} ${size}`)
  d3.select('path')
    .attr('d', `${LINE}`)
  // Assign path to animated element
  document.querySelector('.element').style.setProperty('--path', `"${LINE}"`)
}

assignPath()

window.addEventListener('resize', assignPath)

View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/d3/5.15.0/d3.min.js