Search code examples
csssvgcss-animationscss-transitionssvg-animate

Animate SVG straight line by CSS transition


I want to move the line and the circle in the same way, and animate the transition with CSS.
It's working for the circle, not for the line.

What's wrong ?

const state = {
  x: 75,
  y: 75
};
const line = document.getElementsByTagName('line')[0];
const circle = document.getElementsByTagName('circle')[0];

setInterval(() => {
  state.x = Math.floor(Math.random() * 150);
  state.y = Math.floor(Math.random() * 150);
  line.setAttribute("x2", state.x);
  line.setAttribute("y2", state.y);
  circle.setAttribute("transform", `translate(${state.x} ${state.y})`);
}, 2000)
circle,
line {
  transition: all 1s ease;
}
<svg height="150" width="150" style="border: 1px solid black">
  <line 
    x1="0"
    y1="0"
    x2="75"
    y2="75"
    stroke="red"
  >
  </line>
  <circle 
    r="10"
    transform="translate(75 75)"
    fill="none"
    stroke="blue"
  >
  </circle>
</svg>


Solution

  • The easiest way to animate a circle and a line synchronously is to use the circle as a marker.

    In this case, the circle will always be attached to the line and will animate along with it.

    const state = {
      x: 75,
      y: 75
    };
    const line = document.getElementsByTagName('line')[0];
    const circle = document.getElementsByTagName('circle')[0];
    
    setInterval(() => {
      state.x = Math.floor(Math.random() * 150);
      state.y = Math.floor(Math.random() * 150);
      line.setAttribute("x2", state.x);
      line.setAttribute("y2", state.y);
     
    }, 2000)
    <svg height="160" width="160" style="border: 1px solid black"> 
    <defs>
      <marker id="markEnd"  viewBox="0 0 22 22" refX="10" refY="10"  markerUnits="userSpaceOnUse" markerWidth="22" markerHeight="22">
      <circle r="10" cx="10" cy="10" fill="none"  stroke="blue" />
      </marker> 
    </defs>
      <line 
        x1="0"
        y1="0"
        x2="75"
        y2="75"
        stroke="red" 
        marker-end="url(#markEnd)"
      >
      </line>
      
    </svg>

    Update

    Option @AOD to replace the line with path and with a marker

    const state = {
      x: 75,
      y: 75
    };
    const line = document.getElementsByClassName('line')[0];
    
    setInterval(() => {
      state.x = Math.floor(Math.random() * 150);
      state.y = Math.floor(Math.random() * 150);
      line.setAttribute("d", `M 0 0 L ${state.x} ${state.y}`);
    }, 2000)
    path {
      transition: all 1s ease;
    }
    <svg height="160" width="160" style="border: 1px solid black"> 
    <defs>
      <marker id="markEnd"  viewBox="0 0 22 22" refX="10" refY="10"  markerUnits="userSpaceOnUse" markerWidth="22" markerHeight="22">
      <circle r="10" cx="10" cy="10" fill="none"  stroke="blue" />
      </marker> 
    </defs>
        <path
        class="line"
        d="M 0 0 L 75 75"
        stroke="red"
        fill="none"
        marker-end="url(#markEnd)"
        />
    </svg>