react-circular-slider
react-circular-slider copied to clipboard
Support multi-stop gradients for track and progress
I love how smooth this implementation of a circular slider is! My end goal is to create a hue slider for a color picker.
Luckily, svg stop-color
accepts transparent
as a value. Translucent colors with stop-opacity
are not currently supported as props in this library.
The main problem is the trackColor
, which is eventually passed down to the svg circle as stroke={trackColor}
. Unfortunately, the stroke does not support the css gradient syntax. Instead, it requires a linearGradient
definition within the svg. The current implementation of the progress bar supports two color gradients, but not the near infinite (~360) multi-color gradient needed for the color picker.
I think it would be great to upgrade the stroke of the trackColor and the progress bar at the same time. I noticed this library prides itself on having zero dependencies; otherwise, I would suggest parsing colors via tinycolor.
I think it would be great to have a new color and gradient syntax for the trackColor and the progress bar. Perhaps a syntax like:
const example = (
<CircularSlider
trackColor="#eeeeee"
progressGradient={[
"#ffb347",
{
offset: '100%',
stopColor: "#ffcc33",
stopOpacity: 0.5,
},
]}
/>
);
Implemented like:
const createColorStops = (color, index, colors) => {
if (typeof color === "string") {
color = { stopColor: color };
}
let { offset, stopColor, stopOpacity } = color;
if (index === 0) {
return (
<stop
offset={offset ?? `0%`}
stopColor={stopColor}
stopOpacity={stopOpacity ?? 1}
/>
);
} else if (index === gradients.length - 1) {
return (
<stop
offset={offset ?? `100%`}
stopColor={stopColor}
stopOpacity={stopOpacity ?? 1}
/>
);
} else {
return (
<stop
offset={offset ?? `${(100 / (gradients.length - 1)) * index}%`}
stopColor={stopColor}
stopOpacity={stopOpacity ?? 1}
/>
);
}
};
const svg = (props) => {
const {
label,
trackGradient,
progressGradient,
trackColor,
progressColor,
} = props;
let defs;
if (trackGradient || progressGradient) {
defs = (
<defs>
{trackGradient && (
<linearGradient id={`${label}-track`} x1="100%" x2="0%">
{trackGradient.map(createColorStops)}
</linearGradient>
)}
{progressGradient && (
<linearGradient id={`${label}-progress`} x1="100%" x2="0%">
{progressGradient.map(createColorStops)}
</linearGradient>
)}
</defs>
);
}
return (
<svg>
{defs}
<circle stroke={trackColor || `url(#${label}-track)`} />
<path stroke={progressColor || `url(#${label}-progress)`} />
</svg>
);
};
My only other comment on the implementation is that we could cache the defs
with React.useMemo
, since we don’t need to recalculate that on every input change.
Of course, changing the syntax would probably mean a major version bump. If you’re okay with that, I could formalize a pull request. Anyway, thanks for this awesome library!
TODO: references
Hi @jlarmstrongiv, I always appreciate it when someone contributes to my library. You are welcomed to create a PR. Thanks!
progressGradient
@jlarmstrongiv hi do you have working sample for multi stops
@dev2-piniada The example code above should work for multiple color stops. Unfortunately, my project ended up going in a different direction and I was unable to finish a PR for it.
That would be a great feature! I was hoping I could mimic a kelvin color temperature gradient, but now I see this is not yet implemented... Awesome Circular Slider by the way!