Skip to content

Instantly share code, notes, and snippets.

@SebastianHGonzalez
Created August 1, 2019 15:00
Show Gist options
  • Save SebastianHGonzalez/6618012a5ce3bda9ec5a6cdc574a8588 to your computer and use it in GitHub Desktop.
Save SebastianHGonzalez/6618012a5ce3bda9ec5a6cdc574a8588 to your computer and use it in GitHub Desktop.
import React, { useMemo } from "react";
import { Spring, animated } from "react-spring/renderprops";
import { interpolate } from "flubber";
import { string, arrayOf, number } from "prop-types";
export default function MorphPath({ paths, currentPath, id, fill, transform, scale }) {
const interpolators = useMemo(
// https://reactjs.org/docs/hooks-reference.html#usememo
() =>
paths.map((path, index, array) =>
interpolate(path, array[(index + 1) % array.length], { maxSegmentLength: 0.1 }),
),
paths,
);
const interpolator = interpolators[(currentPath || paths.length) - 1];
return (
<g id={id} fill={fill} transform={transform} scale={scale}>
<Spring reset native from={{ t: 0 }} to={{ t: 1 }}>
{({ t }) => <animated.path d={t.interpolate(interpolator)} />}
</Spring>
</g>
);
}
MorphPath.propTypes = {
id: string,
transform: string,
scale: number,
fill: string,
paths: arrayOf(string),
currentPath: number,
};
MorphPath.defaultProps = {
id: undefined,
transform: undefined,
scale: 1,
fill: "black",
paths: [],
currentPath: 0,
};
import React, { useState, useCallback, useEffect } from "react";
import { Spring } from "react-spring/renderprops";
export default function Demo() {
const paths = [defaultPaths.heart, defaultPaths.lighting, defaultPaths.plane];
const [currentPath, setPath] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setPath((state) => (state + 1) % paths.length);
}, 1000);
return () => clearInterval(interval);
}, [paths.length]);
const handleClick = useCallback(() => setPath((state) => (state + 1) % paths.length), [paths.length]);
return (
<button type="button" onClick={handleClick} style={{ width: "8rem" }}>
{currentPath}
{": "}
{currentPath === 0 && "heart"}
{currentPath === 1 && "lighting"}
{currentPath === 2 && "plane"}
<div style={{ width: "10rem" }}>
<svg>
<defs>
<Spring
from={{ color: "black" }}
to={{
start: currentPath ? "#B2DBBF" : "red",
end: currentPath ? "#247BA0" : "blue",
}}>
{({ start, end }) => (
<>
<linearGradient id="grad0" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style={{ stopColor: start, stopOpacity: "1" }} />
<stop offset="100%" style={{ stopColor: end, stopOpacity: "1" }} />
</linearGradient>
<linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style={{ stopColor: end, stopOpacity: "1" }} />
<stop offset="100%" style={{ stopColor: start, stopOpacity: "1" }} />
</linearGradient>
</>
)}
</Spring>
</defs>
<MorphPath paths={paths} currentPath={currentPath} fill="url(#grad0)" />
<MorphPath paths={paths} currentPath={currentPath} fill="url(#grad1)" transform="translate(22, 0)" />
</svg>
</div>
</button>
);
}
const defaultPaths = {
star: "M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z",
heart:
"M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z",
plane:
"M21 16v-2l-8-5V3.5c0-.83-.67-1.5-1.5-1.5S10 2.67 10 3.5V9l-8 5v2l8-2.5V19l-2 1.5V22l3.5-1 3.5 1v-1.5L13 19v-5.5l8 2.5z",
lighting: "M7 2v11h3v9l7-12h-4l4-8z",
scale: "M12 3v10.55c-.59-.34-1.27-.55-2-.55-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4V7h4V3h-6z",
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment