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>