2023-07-04 08:05:08 +00:00
|
|
|
import React from "react"
|
2023-07-04 09:16:41 +00:00
|
|
|
import { Box, Center } from "@chakra-ui/react"
|
2023-07-05 04:49:14 +00:00
|
|
|
|
|
|
|
import { GradientPath } from "~/gp/GradientPath"
|
2023-07-05 10:05:35 +00:00
|
|
|
import { useProps } from "~/hooks/useProps"
|
|
|
|
import { useTrace } from "~/hooks/useTrace"
|
|
|
|
import { useUuid } from "~/hooks/useUuid"
|
2023-07-04 08:05:08 +00:00
|
|
|
|
2023-07-05 10:05:35 +00:00
|
|
|
export const Rainbow = (allProps: any): JSX.Element => {
|
2023-07-07 07:42:14 +00:00
|
|
|
// return allProps.children
|
|
|
|
|
2023-07-04 08:05:08 +00:00
|
|
|
const rainbow = ["#f34a4a", "#ffbc48", "#58ca70", "#47b5e6", "#a555e8"]
|
|
|
|
|
2023-07-05 10:05:35 +00:00
|
|
|
const props = useProps(allProps)
|
|
|
|
|
|
|
|
const uuid = useUuid()
|
|
|
|
|
2023-07-05 10:14:49 +00:00
|
|
|
props.expand = props?.expand || true
|
|
|
|
|
2023-07-05 10:05:35 +00:00
|
|
|
const [hidden, setHidden] = React.useState(true)
|
|
|
|
const repeats = props?.repeats || 1
|
|
|
|
const [filter, setFilter] = React.useState(props?.filter)
|
|
|
|
const opacity = props?.opacity !== undefined ? props?.opacity : 1
|
2023-07-04 08:05:08 +00:00
|
|
|
const [colors, setColors] = React.useState(props?.colors || rainbow)
|
2023-07-05 10:05:35 +00:00
|
|
|
const [pathWidth, setPathWidth] = React.useState(props?.thickness || 1)
|
|
|
|
const [overflow, setOverflow] = React.useState(props?.overflow || "hidden")
|
|
|
|
const [opacityTransition, setOpacityTransition] = React.useState(
|
2023-08-14 03:06:26 +00:00
|
|
|
props?.opacityTransition || "all 10000ms ease-out"
|
2023-07-05 10:05:35 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
const parentRef = React.useRef(null)
|
2023-07-04 08:05:08 +00:00
|
|
|
|
2023-07-04 09:16:41 +00:00
|
|
|
const repeatedColours = React.useMemo(() => {
|
2023-07-05 10:05:35 +00:00
|
|
|
const ret = []
|
|
|
|
|
|
|
|
for (let i = 0; i < repeats; i++) {
|
|
|
|
ret.push(...colors)
|
|
|
|
}
|
|
|
|
|
|
|
|
ret.push(colors[0])
|
|
|
|
|
|
|
|
return ret
|
|
|
|
}, [colors, repeats])
|
2023-07-04 09:16:41 +00:00
|
|
|
|
|
|
|
// make SVG that takes makes path in the shape of a box
|
|
|
|
// which adjusts to the parent containers size
|
|
|
|
|
2023-07-05 10:05:35 +00:00
|
|
|
const [state, setState] = React.useState({
|
|
|
|
width: 100,
|
|
|
|
height: 100,
|
|
|
|
})
|
|
|
|
|
2023-07-04 09:16:41 +00:00
|
|
|
const [strokeWidth, setStrokeWidth] = React.useState(1)
|
|
|
|
|
|
|
|
const [extraStroke, setExtraStroke] = React.useState(0)
|
|
|
|
|
2023-07-05 10:05:35 +00:00
|
|
|
const [width, setWidth] = React.useState(props?.width || "100%")
|
|
|
|
const [height, setHeight] = React.useState(props?.height || "100%")
|
2023-07-04 09:16:41 +00:00
|
|
|
|
|
|
|
const pathString = React.useMemo(() => {
|
|
|
|
const startPoint = 0 + strokeWidth / 2
|
|
|
|
const endPoint = 100 - strokeWidth / 2
|
|
|
|
|
|
|
|
return `M -${4} ${startPoint} H ${endPoint} V ${endPoint} H ${startPoint} V ${startPoint}`
|
|
|
|
}, [strokeWidth])
|
|
|
|
|
|
|
|
const svgRef = React.useRef(null)
|
|
|
|
|
2023-07-05 04:49:14 +00:00
|
|
|
const colourKeyframes = React.useMemo(() => {
|
|
|
|
const ret = {}
|
|
|
|
|
|
|
|
repeatedColours.map((colour, i) => {
|
|
|
|
const keyframe = `${(i / (repeatedColours.length - 1)) * 100}%`
|
|
|
|
ret[keyframe] = {
|
|
|
|
fill: colour,
|
2023-07-15 08:08:36 +00:00
|
|
|
animationTimingFunction: "ease-out",
|
2023-07-05 04:49:14 +00:00
|
|
|
stroke: colour,
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
return ret
|
|
|
|
}, [repeatedColours])
|
|
|
|
|
2023-07-05 10:05:35 +00:00
|
|
|
React.useEffect(() => {
|
|
|
|
const updateChildSize = () => {
|
2023-08-03 13:48:56 +00:00
|
|
|
const { width, height } =
|
|
|
|
parentRef?.current?.getBoundingClientRect() || {}
|
2023-07-05 10:05:35 +00:00
|
|
|
setState({ width, height })
|
|
|
|
}
|
2023-07-05 04:49:14 +00:00
|
|
|
|
2023-07-05 10:05:35 +00:00
|
|
|
updateChildSize()
|
2023-07-05 04:49:14 +00:00
|
|
|
|
2023-07-05 10:05:35 +00:00
|
|
|
new ResizeObserver(updateChildSize).observe(parentRef?.current)
|
|
|
|
}, [])
|
2023-07-05 04:49:14 +00:00
|
|
|
|
2023-07-05 10:05:35 +00:00
|
|
|
const rect = React.useMemo(() => {
|
|
|
|
return (
|
|
|
|
<rect
|
|
|
|
// stroke="url(#linear-gradient)"
|
|
|
|
x={0}
|
|
|
|
y={0}
|
|
|
|
width={state?.width || 100}
|
|
|
|
height={state?.height || 100}
|
|
|
|
rx={10}
|
|
|
|
ry={10}
|
|
|
|
></rect>
|
|
|
|
)
|
|
|
|
}, [state?.width, state?.height])
|
2023-07-05 04:49:14 +00:00
|
|
|
|
2023-07-04 09:16:41 +00:00
|
|
|
const svg = React.useMemo(() => {
|
|
|
|
return (
|
2023-07-15 08:08:36 +00:00
|
|
|
<Box width="100%" height="100%" id={"rainbow-svg-container-" + uuid}>
|
2023-07-04 09:16:41 +00:00
|
|
|
<svg
|
|
|
|
overflow="visible"
|
2023-07-05 10:05:35 +00:00
|
|
|
viewBox={`0 0 ${state?.width || 100} ${state?.height || 100}`}
|
2023-07-04 09:16:41 +00:00
|
|
|
width="100%"
|
|
|
|
height="100%"
|
2023-07-05 10:05:35 +00:00
|
|
|
preserveAspectRatio="none"
|
2023-07-04 09:16:41 +00:00
|
|
|
>
|
2023-07-05 10:05:35 +00:00
|
|
|
{rect}
|
2023-07-04 09:16:41 +00:00
|
|
|
{/* <path
|
|
|
|
fill="none"
|
|
|
|
stroke="blue"
|
|
|
|
strokeAlignment="inner"
|
|
|
|
strokeWidth={`${strokeWidth + extraStroke}px`}
|
|
|
|
d={pathString}
|
|
|
|
></path> */}
|
|
|
|
</svg>
|
|
|
|
</Box>
|
|
|
|
)
|
2023-07-15 08:08:36 +00:00
|
|
|
}, [state, rect, uuid])
|
2023-07-04 09:16:41 +00:00
|
|
|
|
|
|
|
React.useEffect(() => {
|
2023-07-05 10:05:35 +00:00
|
|
|
if (uuid) {
|
|
|
|
const svg = svgRef?.current?.querySelector("svg")
|
|
|
|
|
|
|
|
// path is rect or insert new rect if empty svg
|
|
|
|
const rectSource = parentRef?.current?.querySelector(".svg-source rect")
|
|
|
|
const path =
|
|
|
|
svg?.querySelector?.("rect") ||
|
|
|
|
svg?.appendChild?.(rectSource?.cloneNode?.())
|
|
|
|
|
|
|
|
if (path) {
|
|
|
|
const gp = new GradientPath({
|
|
|
|
path,
|
|
|
|
segments: props?.segments || 1000,
|
|
|
|
samples: props?.samples || 1,
|
|
|
|
precision: props?.precision || 5,
|
|
|
|
})
|
|
|
|
|
|
|
|
gp.render({
|
|
|
|
type: "path",
|
|
|
|
width: pathWidth || 1,
|
|
|
|
animation: {
|
|
|
|
name: `rainbow-${uuid}`,
|
2023-07-07 07:42:14 +00:00
|
|
|
duration: props?.duration || 7,
|
2023-07-05 10:05:35 +00:00
|
|
|
},
|
|
|
|
})
|
|
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
setHidden(false)
|
|
|
|
}, 500)
|
|
|
|
|
|
|
|
return () => {
|
|
|
|
// setHidden(true)
|
|
|
|
gp.remove()
|
2023-07-04 09:16:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-07-05 10:05:35 +00:00
|
|
|
}, [
|
|
|
|
uuid,
|
2023-07-07 07:42:14 +00:00
|
|
|
props?.duration,
|
2023-07-05 10:05:35 +00:00
|
|
|
props?.segments,
|
|
|
|
props?.samples,
|
|
|
|
props?.precision,
|
|
|
|
pathWidth,
|
|
|
|
repeatedColours,
|
|
|
|
parentRef,
|
|
|
|
svgRef,
|
|
|
|
rect,
|
|
|
|
])
|
|
|
|
|
|
|
|
const render = true
|
|
|
|
|
2023-07-24 01:36:25 +00:00
|
|
|
// useTrace("Rainbow", {
|
|
|
|
// props,
|
|
|
|
// })
|
2023-07-04 09:16:41 +00:00
|
|
|
|
2023-07-04 08:05:08 +00:00
|
|
|
return (
|
2023-07-05 10:05:35 +00:00
|
|
|
<>
|
2023-07-04 09:16:41 +00:00
|
|
|
<Center
|
2023-07-05 10:05:35 +00:00
|
|
|
className="Rainbow_n__n"
|
|
|
|
ref={parentRef}
|
|
|
|
sx={{
|
|
|
|
[`@keyframes rainbow-${uuid}`]: {
|
|
|
|
...colourKeyframes,
|
|
|
|
},
|
|
|
|
}}
|
|
|
|
position={props?.position || "relative"}
|
|
|
|
overflow={overflow}
|
|
|
|
width={props?.expand ? "100%" : ""}
|
|
|
|
height={props?.expand ? "100%" : ""}
|
2023-07-04 09:16:41 +00:00
|
|
|
>
|
2023-07-05 10:05:35 +00:00
|
|
|
<Center
|
|
|
|
className="main-svg-container"
|
|
|
|
position="absolute"
|
|
|
|
top={0}
|
|
|
|
left={0}
|
|
|
|
overflow="visible"
|
|
|
|
width="100%"
|
|
|
|
height="100%"
|
|
|
|
opacity={hidden ? "0" : opacity}
|
2023-08-13 05:53:47 +00:00
|
|
|
// pointerEvents="none"
|
2023-07-05 10:05:35 +00:00
|
|
|
transition={opacityTransition}
|
|
|
|
>
|
|
|
|
{/* debug svg */}
|
|
|
|
<Box
|
|
|
|
className="svg-source"
|
|
|
|
flexShrink={0}
|
|
|
|
display="none"
|
|
|
|
overflow="visible"
|
|
|
|
width={width}
|
|
|
|
height={height}
|
|
|
|
>
|
|
|
|
{svg}
|
|
|
|
</Box>
|
|
|
|
<Box
|
|
|
|
ref={svgRef}
|
|
|
|
flexShrink={0}
|
|
|
|
overflow="visible"
|
|
|
|
width={width}
|
|
|
|
height={height}
|
|
|
|
opacity={hidden ? 0 : !render ? "0" : 1}
|
|
|
|
filter={filter}
|
|
|
|
>
|
|
|
|
{svg}
|
|
|
|
</Box>
|
|
|
|
</Center>
|
|
|
|
{allProps?.children}
|
2023-07-04 09:16:41 +00:00
|
|
|
</Center>
|
2023-07-05 10:05:35 +00:00
|
|
|
</>
|
2023-07-04 08:05:08 +00:00
|
|
|
)
|
|
|
|
}
|