Custom React Hook: Use Color on Mouse Move

This powerful configurable hook returns a color based on the mouse's x and y coordinates.

Posted on November 16, 2022

Watch the related tutorial on Full Stack Craft's YouTube!

React Hooks

React Hooks are extremely powerful - they allow you to build reactive code that can dropped into as many components as you want, all in one line. In this code walk-through, we'll be building a hook that returns a color based on the mouse's x and y coordinates. We'll first build a naïve implementation that gets the job done, but leaves many functionalities to be desired.

A Naive Implementation of the Hook

With our defined idea, we can already get started. Let's allow two colors that we want

Altogether, our initial implementation of this hook looks like this:

import { useCallback, useEffect, useRef, useState } from "react"
import chroma from "chroma-js"

export type ArrayOfTwoOrMore<T> = {
    0: T
    1: T
} & Array<T>

// this function takes the current x and y coordinates of the mouse and produces a color based on them
export const useColorOnMouseMove = (colors: ArrayOfTwoOrMore<string>) => {
    const [color, setColor] = useState<string>(colors[0])
    const maxX = useRef(0)
    const maxY = useRef(0)
    const scale = chroma.scale([colors[0], colors[1]])

    const onMouseMove = useCallback(
        (e: MouseEvent) => {
            if (maxX.current > 0 && maxY.current > 0) {
                const percentX = e.clientX / maxX.current
                const percentY = e.clientY / maxY.current

                const average = (percentX + percentY) / 2

                const color = scale(average).hex()
                setColor(color)
            }
        },
        [scale]
    )

    const onResize = () => {
        maxX.current = window.innerWidth
        maxY.current = window.innerHeight
    }

    useEffect(() => {
        window.addEventListener("mousemove", onMouseMove)
        window.addEventListener("resize", onResize)
        return () => {
            window.removeEventListener("mousemove", onMouseMove)
        }
    }, [onMouseMove])

    return color
}

Adding Various Improvements

What we've built so far "works", but in my opinion the hook needs a few improvements:

  1. More color interpolation algorithms - change in X only, change in Y only, radial
  2. What if the user doesn't want to use the whole background? Ideally the user could pass a ref to define which dimensions to use to calculate the color change
  3. What about multiple colors and coordinates?
  4. Performance issues, directly updating state every time the browser

We'll address each of these points in the next sections.

Next / Previous Post:

Find more posts by tag:

-~{/* */}~-