useSlider
A custom React hook that registers horizontal or vertical dragging actions from the user that start on the element
referenced by dragAreaRef
. This hook responds to touch and mouse events, and will return a value between minValue
and maxValue
corresponding to the distance of the dragging action relative to the drag area.
This hook responds to the user dragging the target, clicking or touching along the drag area, or using the mouse wheel.
This hook can be configured to return a value between minValue
and maxValue
inclusive (defaults to 0-1), and the
sensitivity of the mouse wheel can be configured as well. Finally, the hook can be configured to respond to horizontal
or vertical dragging motions.
Example: A Volume Slider
The example below shows a volume slider that uses the useSlider
hook.
Drag the slider to adjust the volume, or use the mouse wheel
import { useRef } from "react";import { useSlider } from "@gertalot/sliders";import Speaker from "./Speaker";
/** * A horizontal volume slider that uses the `useSlider` hook. */const VolumeSlider = () => { // The dragArea is an invisible rect placed over the slider giving us an "active" // area that the `useSlider` hook uses to determine if the user is dragging the // slider. The target is the slidey bit that the user can drag around. const dragAreaRef = useRef<SVGRectElement>(null); const targetRef = useRef<SVGPathElement>(null);
const { value, isAdjusting, isOnDragArea, isOnTarget } = useSlider({ dragAreaRef, targetRef, minValue: 0, maxValue: 1, initialValue: 0.4, isVertical: false, });
return ( <div> <p>Drag the slider to adjust the volume, or use the mouse wheel</p> <div className="w-full h-64 mt-0 flex items-center justify-center touch-none select-none"> {/* show the loudspeaker icon. The `--sl-color-accent-*` variables are from the Astro Starlight theme that is used to generate this site */} <Speaker volume={value} color={isAdjusting || isOnDragArea ? "var(--sl-color-accent)" : "var(--sl-color-accent-high)"} /> <svg width="256" height="32" viewBox="0 0 256 32" style={{ margin: 0, borderWidth: "1px", borderColor: "var(--sl-color-accent)" }} className="flex px-3 border-gray-800 border-solid rounded-2xl" > {/* This rect, placed directly over the slider, is the active drag area */} <rect x="8" y="0" width="240" height="32" fill="transparent" ref={dragAreaRef} />
{/* the grey background line for the slider */} <path style={{ stroke: "#666", strokeWidth: 10, strokeLinecap: "round", }} d="M 8,16 248,16" />
{/* the slidey bit that the user can drag around */} <path ref={targetRef} style={{ stroke: isAdjusting || isOnTarget ? "var(--sl-color-accent)" : "var(--sl-color-accent-high)", strokeWidth: isAdjusting || isOnTarget ? 18 : isOnDragArea ? 14 : 10, strokeLinecap: "round", }} d={`M 8,16 ${8 + value * 240},16`} /> </svg> </div> <div className="text-center w-full" style={{ color: "var(--sl-color-accent)" }}> {(value * 100).toFixed(0)}% </div> </div> );};
export default VolumeSlider;
/** * A simple component that renders a loud speaker icon as an SVG. The number of * bars is determined by the `volume` prop. */const Speaker = ({ volume, width = 32, height = 32, color = "white",}: { volume: number; width?: number; height?: number; color?: string;}) => { return ( <svg className="px-1" width={width} height={height} viewBox="0 0 512 512"> {/* Draw the speaker cone */} <rect style={{ fill: color, stroke: "none" }} width="48" height="128" x="48" y="192" /> <path style={{ fill: color, stroke: "none" }} d="m 112,192 96,-64 v 272 l -96,-80 z" />
{/* Draw the volume bars */} {volume == 0.0 && ( <path style={{ fill: "none", stroke: color, strokeWidth: 32, strokeLinecap: "round" }} d="M 288,192 416,320 M 416,192 288,320" /> )} {volume > 0.0 && ( <path style={{ fill: "none", stroke: color, strokeWidth: 32, strokeLinecap: "round" }} d="m 256,200 a 64,64 0 0 1 32,55 64,64 0 0 1 -32,55" /> )} {volume > 0.25 && ( <path style={{ fill: "none", stroke: color, strokeWidth: 32, strokeLinecap: "round" }} d="m 289,145 a 128,128 0 0 1 64,110 128,128 0 0 1 -64,110" /> )} {volume > 0.5 && ( <path style={{ fill: "none", stroke: color, strokeWidth: 32, strokeLinecap: "round" }} d="m 320,90 A 192,192 0 0 1 416,256 192,192 0 0 1 320,422" /> )} {volume > 0.75 && ( <path style={{ fill: "none", stroke: color, strokeWidth: 32, strokeLinecap: "round" }} d="m 356,34 A 256,256 0 0 1 484,256 256,256 0 0 1 356,478" /> )} </svg> );};
export default Speaker;
Usage
const { value, isAdjusting, isOnTarget, isOnDragArea } = useSlider({ targetRef, dragAreaRef, minValue = 0, maxValue = 1, initialValue = 0, sensitivity = 100, isVertical = true,});
Props
The following props can be passed to the custom hook:
dragAreaRef
(required) A RefObject
referencing a DOM element that functions as the
“active” area. Any dragging or sliding action that happens when the pointer is over this
element is registered as dragging the target.
targetRef
(required) A RefObject
referencing a DOM element in your component.
This is the “target” element of your slider, i.e. the thing you slide.
minValue
a number
representing the minimum possible value. If the user keeps dragging
beyond the minimum value, the hook’s value
will stay at minValue
. Default is 0.
maxValue
a number
representing the maximum possible value. If the user keeps dragging
beyond the maximum value, the hook’s value
will stay at maxValue
. Default is 1.
initialValue
a number
that is used as the initial value when rendering the component.
Defaults to 0.
sensitivity
a number
that is a measure of how much rotating the mouse wheel will
change the slider. Larger numbers mean slower changes. This uses useWheelToAdjust
under the hood.
isVertical
true
if the custom hook should register vertical dragging motion, false
for horizontal dragging motion. Defaults to true
.
Returns
The useSlider
hook returns an object with the following properties:
value
A value between minValue
and maxValue
that represents the current value of
the slider object. Use this to set the position of the target element or otherwise indicate
the value.
isAdjusting
true
if the user is actively dragging or using the wheel. This can be used
to conditionally render the component in a particular way.
isOnTarget
true
if the pointer is currently over the target element referenced by targetAreaRef
.
isOnDragArea
true
if the pointer is currently over the element referenced by dragAreaRef
.
Note that this actually checks if the pointer coordinates are within the bounding rect of the
dragArea elements; it doesn’t use the pointer event target or composed path, in case the element
is obscured by another element.