Search code examples
javascriptreactjssvgsvg-animate

Make SVG Circle stroke-dashoffset animate


I would like to animate the SVG Circle's strokeDashoffset,

I make a circle, I get data from an array (will be a database later on) which changes the strokeDashoffset at the moment it doesn't animate smoothly, I'd like a ease-in-out 1s if I wanted it to go from 0 to a 100% I'd use keyframes with "from{}" and "to{}" in css, but I would like to animate any changes happening, cause there will be some color changing as well.

<svg>
      <circle
        cx="50%"
        cy="50%"
        r={radiusHR}
        fill="none"
        stroke={strokedefaultcolor}
        strokeWidth={strokewidth}
      />
      <circle
        id="HRprogress"
        cx="50%"
        cy="50%"
        r={radiusHR}
        fill="none"
        stroke="#E1E5FA"
        strokeWidth={strokewidth}
        strokeDasharray={circumferenceHR}
        strokeDashoffset={circumferenceHR * (1 - this.props.value1 / 101)}
      />

</svg>

Solution

  • Just use the props in style attribute, and set a transition. For example:

    const Circle = (props) => (
      <circle
        cx="50%"
        cy="50%"
        r={radius}
        fill="none"
        style={{
          stroke: props.color,
          transition: 'all 0.3s',
          strokeWidth: props.width,
          strokeDasharray: props.dashArray,
          strokeDashoffset: props.dashOffset
        }}
      />
    );
    

    Full example with random values every 800 ms:

    const Circle = ({ // default values used if not given in props
      radius,
      color = 'lightslategray',
      width = 1,
      dashArray = '0',
      dashOffset = 0
    }) => (
      <circle
        cx="50%"
        cy="50%"
        r={radius}
        fill="none"
        style={{
          stroke: color,
          transition: 'all 0.3s ease-in-out', // animation duration and easing function
          strokeWidth: width,
          strokeDasharray: dashArray,
          strokeDashoffset: dashOffset
        }}
      />
    );
    
    class Circles extends React.Component {
      constructor() {
        super();
        this.state = { // initial values
          dashOffset: 0,
          dashArray: '10, 10',
          width: 1
        };
      }
    
      componentDidMount() {
        setInterval( // change to some random crap every 800 ms
          () =>
            this.setState({
              dashOffset: this.state.dashOffset + 10,
              dashArray: `${Math.random() * 10}, 10`,
              width: 1 + Math.random() * 4
            }),
          800
        );
      }
    
      render() {
        return (
          <svg>
            <Circle
              radius={50}
              color="#eb26a8"
              width={this.state.width}
              dashArray={this.state.dashArray}
              dashOffset={this.state.dashOffset * 1}
            />
            <Circle
              radius={60}
              color="#2645eb"
              width={this.state.width * 2}
              dashArray={this.state.dashArray}
              dashOffset={this.state.dashOffset * 4}
            />
            <Circle
              radius={70}
              width={2}
              dashArray="1, 5"
            />
            <Circle
              radius={25}
              width={2}
              dashArray="1, 5"
            />
          </svg>
        );
      }
    }
    
    ReactDOM.render(
      <Circles />,
      document.getElementById("mount")
    );
    <div id="mount"></div>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

    List of timing functions: https://developer.mozilla.org/en-US/docs/Web/CSS/transition-timing-function

    You can even create a custom one: https://developer.mozilla.org/en-US/docs/Web/CSS/single-transition-timing-function