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.