Slider

December 3rd, 2023

0

-10

-9

-8

-7

-6

-5

-4

-3

-2

-1

0

1

2

3

4

5

6

7

8

9

10

I was inspired by Fractional Slider by Rauno Freiberg and recreated it using Framer Motion and React. My focus was primarily on the motion and the sound design of these components.

<TabContent tabId="overview" activeTab={activeTab}>
  <motion.div
    className="flex gap-2 overflow-x-auto mx-auto h-[500px] max-w-[800px] "
    layoutId="images"
    transition={{ type: 'spring', bounce: 0.2, duration: 0.6 }}
  >
    {images.map((image) => {
      return (
        <motion.div
          className="shadow-lg"
          key={image.link}
          whileHover={{
            width: '350px',
            transition: { duration: 0.2, easings: 'spring' },
          }}
        >
          <Image
            src={image.link}
            width={400}
            height={600}
            onClick={() => clickHandler(image.id)}
            alt="placeholder"
            className="rounded-md h-full object-cover cursor-pointer "
          ></Image>
        </motion.div>
      )
    })}
  </motion.div>
</TabContent>

I started by creating my state variables as well as the refs and the individual slider-range. I used the useMotionValue hook from Framer Motion to track the drag position.

const range = [-20, 20];
const sliderRef = useRef<HTMLDivElement>(null);
const [value, setValue] = useState<number>(0);

const items = Array.from({
length: range[1] - range[0] + 1 }(_, i) => i + range[0]
);
const [maxWidth, setMaxWidth] = useState(0);
const x = useMotionValue(0);

TSX elements and Framer-motion setup

I created the individual slider elements and passed the value. I also passed an active prop to track if the element is currently within the selected range.

I used Framer Motion's "drag" option to implement the slider-dragging logic. To create a seamless experience, I used the dragConstraints option to limit the dragging to the slider-range.

To create the selecting logic, I used React state. I used some math to calculate the current value and set the state on every change. Then I also added a click sound effect whenever the value changes.

useEffect(() => {
  const updateMaxWidth = () => {
    const width = sliderRef.current?.offsetWidth
    if (width) {
      setMaxWidth(width / 2)
    }
  }
  updateMaxWidth()
  window.addEventListener('resize', updateMaxWidth)
  return () => window.removeEventListener('resize', updateMaxWidth)
}, [])

Next Interaction

Dynamic Island