skip to Main Content

On my current project, I’m using Excalidraw, and I’m drawing a top layer of <div>s on top of the <canvas>. It all works fine, but, when it comes to scrolling through the infinite canvas, if the mouse is on top of one of the <div>s, scroll events won’t pass through to the canvas below, so these <div>s are making the UX quite clunky.

Is there a way of having these <div>s not react to scroll events? I’ve tried something like pointer-events: none in CSS, and it works, but then the <div>s won’t react to anything at all.

Here’s a CodeSandbox with an example, which could be summarized by this code block:

"use client";

import { useMemo } from "react";

import dynamic from "next/dynamic";

const Excalidraw = dynamic(
  async () => {
    const mod = await import("@excalidraw/excalidraw");
    return mod.Excalidraw;
  },
  { ssr: false },
)

export default function Home() {
  const Excal = useMemo(() => <Excalidraw gridModeEnabled />, [])

  return (
    <main className="absolute w-screen h-screen">
      <div
        style={{
          position: "fixed",
          zIndex: 100,
          top: "100px",
          left: "100px",
          backgroundColor: "yellow",
          cursor: "pointer",
        }}
      >
        <p>here</p>
      </div>
      {Excal}
    </main>
  )
}

2

Answers


  1. Well, I don’t know how to edit the code in your sandbox, but I have succeded editing it in Chrome DevTools, so you can apply the same solution in your source code.
    Basically you have to intercept the wheel event and manually dispatch it to the canvas.

    I have assigned an ID (my123) to the yellow box to be able to reference it in my javascript code.
    I have also created a variable to point to the canvas from our event handler:

    myCanvas = $('.excalidraw__canvas.interactive')
    

    and then

    $('#my123').onmousewheel = function(e) {
        e.preventDefault();
        myCanvas.dispatchEvent(new WheelEvent('wheel', {
            bubbles: e.bubbles,
            cancelable: e.cancelable,
            ctrlKey: e.ctrlKey,
            shiftKey: e.shiftKey,
            deltaX: e.deltaX,
            deltaY: e.deltaY,
            deltaMode: e.deltaMode
        }));
    }
    

    Passing ctrlKey and shiftKey properties allows to zoom and scroll horizontally too.

    Be sure to set myCanvas after the canvas has been created.

    Login or Signup to reply.
  2. To achieve smooth scrolling on the Excalidraw canvas while having interactive elements on top, you can utilize a combination of CSS and JavaScript. The goal is to ensure that scroll events pass through the top-layer elements to the Excalidraw canvas while other events like clicks or drags remain interactive on the elements.

    Here’s the updated code for your Home component:

    >   "use client";
    >     
    >     import { useMemo, useEffect } from "react"; import dynamic from "next/dynamic";
    >     
    >     const Excalidraw = dynamic(   async () => {
    >         const mod = await import("@excalidraw/excalidraw");
    >         return mod.Excalidraw;   },   { ssr: false }, );
    >     
    >     export default function Home() {   const Excal = useMemo(() => <Excalidraw gridModeEnabled />, []);
    >     
    >       useEffect(() => {
    >         const overlayDiv = document.querySelector(".overlay");
    >         if (overlayDiv) {
    >           const handleWheel = (event) => {
    >             event.preventDefault(); // Prevent the default scroll behavior
    >             const excalidrawWrapper = document.querySelector(".excalidraw-wrapper");
    >             if (excalidrawWrapper) {
    >               excalidrawWrapper.scrollBy(event.deltaX, event.deltaY);
    >             }
    >           };
    >     
    >           overlayDiv.addEventListener("wheel", handleWheel);
    >     
    >           return () => {
    >             overlayDiv.removeEventListener("wheel", handleWheel);
    >           };
    >         }   }, []);
    >     
    >       return (
    >         <main className="absolute w-screen h-screen">
    >           <div
    >             className="overlay"
    >             style={{
    >               position: "fixed",
    >               zIndex: 100,
    >               top: "100px",
    >               left: "100px",
    >               backgroundColor: "yellow",
    >               cursor: "pointer",
    >             }}
    >           >
    >             <p>here</p>
    >           </div>
    >           {Excal}
    >         </main>   ); }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search