Home > OS >  How to svg morph in ReactJs between two elements
How to svg morph in ReactJs between two elements

Time:02-24

I currently have two variables defining svg elements. I am having trouble morphing between the two, I tried react-svg-morph but the circle would disappear on each render. What is the best way to render a morph?

This is my setup:

    let hexagonShape = <svg width="100%" height="100%" viewBox="0 0 15 15" fill="green" xmlns="http://www.w3.org/2000/svg">
                            <path d="M1.5 4.5V10.5L7.5 14L13.5 10.5V4.5L7.5 1L1.5 4.5Z" stroke="green" strokeWidth=".6" />
                        </svg>
    let circleShape = <svg height="100%" width="100%" viewBox="0 0 15 15" fill='blue' xmlns="http://www.w3.org/2000/svg">
                            <circle cy="50%" cx="50%" r="49%" stroke="blue" strokeWidth=".3" />
                        </svg>

    const [updatedSvg, setUpdatedSvg] = useState(0)
    const [updatedCircleSvg, setUpdatedCircleSvg] = useState()
    const [updatedHexagonSvg, setUpdatedHexagonSvg] = useState()


    function changeSvg() {

        if (updatedSvg === 0) {
            setUpdatedCircleSvg(true)
            setUpdatedHexagonSvg(false)
        }
        if (updatedSvg === 1) {
            setUpdatedCircleSvg(false)
            setUpdatedHexagonSvg(true)
        }
    }

    useEffect(() => {
        changeSvg()
    }, [updatedSvg]) 

And I have two simple buttons that toggle between the two svg renders:

        <div className={classes.container}>
            <div className={classes.shape}>
            {/* this is where the svgs are being rendered */}
                {updatedCircleSvg && circleShape }
                {updatedHexagonSvg && hexagonShape}
            </div>
            <button onClick={() => setUpdatedSvg(0)}>circle</button>
            <button onClick={() => setUpdatedSvg(1)}>hexagon</button>
        </div>

A bit of direction would greatly help. Thank you

CodePudding user response:

Here is a simple example using Quadratic Bezier curves:

const getPoints = radius => {
  const factor = Math.sqrt(3);
  return [
    {x: 0, y: -radius},
    {x: radius / factor, y: -radius},
    {x: radius * factor / 2, y: -radius / 2},
    {x: radius * 2 / factor, y: 0},
    {x: radius * factor / 2, y: radius / 2},
    {x: radius / factor, y: radius},
    {x: 0, y: radius},
    {x: -radius / factor, y: radius},
    {x: -radius * factor / 2, y: radius / 2},
    {x: -radius * 2 / factor, y: 0},
    {x: -radius * factor / 2, y: -radius / 2},              
    {x: -radius / factor, y: -radius}
  ];
}


const getRoundPath = radius => {
  const p = getPoints(radius);
  return `M ${p[0].x},${p[0].y} 
    Q ${p[1].x},${p[1].y} ${p[2].x},${p[2].y} 
    Q ${p[3].x},${p[3].y} ${p[4].x},${p[4].y} 
    Q ${p[5].x},${p[5].y} ${p[6].x},${p[6].y} 
    Q ${p[7].x},${p[7].y} ${p[8].x},${p[8].y} 
    Q ${p[9].x},${p[9].y} ${p[10].x},${p[10].y} 
    Q ${p[11].x},${p[11].y} ${p[0].x},${p[0].y}`
};

const getHexagonPath = radius => {
  const p = getPoints(radius);
  for (let index = 1; index < 12; index  = 2) {
    const prev = p[index - 1];
    const next = p[(index   1) % 12];
    p[index].x = (prev.x   next.x) / 2;
    p[index].y = (prev.y   next.y) / 2;
  }
  return `M ${p[0].x},${p[0].y} 
    Q ${p[1].x},${p[1].y} ${p[2].x},${p[2].y} 
    Q ${p[3].x},${p[3].y} ${p[4].x},${p[4].y} 
    Q ${p[5].x},${p[5].y} ${p[6].x},${p[6].y} 
    Q ${p[7].x},${p[7].y} ${p[8].x},${p[8].y} 
    Q ${p[9].x},${p[9].y} ${p[10].x},${p[10].y} 
    Q ${p[11].x},${p[11].y} ${p[0].x},${p[0].y}`
}

const morphPath = (toHex) => {
  const path = toHex ? getHexagonPath(80) : getRoundPath(80);
  d3.select('path')
    .transition()
    .duration(750)
    .attr('d', path);
  setTimeout(() => morphPath(!toHex), 1000);
};

d3.select('path').attr('d', getRoundPath(80))

morphPath(true);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg width="200" height="200">
  <g transform="translate(100,100)">     
    <path />
  </g>
</svg>

  • Related