Scrollable CSS Filter

Scrollable Gecko

This was suprisingly complicated because of how React handles passive events. I had to bring to this three different hooks: useState, useEffect and useRef. Let's see how it plays out

const scroll = useRef(null)

const [filter, setFilter] = useState("")

useEffect(()=> {
  const element = scroll.current
  element.addEventListener("wheel", <handler>)
  return ()=> element.removeEventListener("wheel", <handler>)
})

We have to use both useRef and useEffect for this to work because of the passive listener behavoiur thingy (check out the details here). That way the wheel event will be handled properly

<img
  src={<image>}
  ref={scroll}
  style={{ filter: `grayscale(${filter}%)` }}
/>

So we've created an image and we've linked it to the ref element created with the useRef hook, also, we are using the filter value from the state to set a grayscale filter (which is what we've aimed for). Now, all that's left is dealing with the event listener handler. I've separated it because that way it's much easier to change to do other cool things when scrolling through an element.

const handleScroll = event=> {
  event.preventDefault()
  const direction_y = event.deltaY !== 0 ? event.deltaY > 0 ? "forward" : "backwards" : "none"
  requestAnimationFrame(()=> {
    const current_grayscale = Math.min(Math.max(1, filter), 99)
    if (direction_y === "forward") setFilter(current_grayscale + 2)
    if (direction_y === "backwards") setFilter(current_grayscale - 2)
  })
}

Thanks to the scroll's deltaY it's easy to tell what the scroll is doing, and increase or decrease the filter accordingly. A bit of clamping to avoid going overboard, and that's pretty much it.

More concepts