Allowing scroll events to flow through from a child <div> to <canvas> in Excalidraw

In my current project, I am utilizing Excalidraw to draw a layer of <div>s on top of the <canvas>. While everything is functioning well, I have encountered an issue with scrolling through the infinite canvas. When the mouse hovers over one of the <div>s, scroll events do not pass through to the canvas below, resulting in a clunky user experience.

Is there a way to prevent these <div>s from responding to scroll events? I have attempted using pointer-events: none in CSS, which does work, but then the <div>s become unresponsive to any interactions at all.

You can view an example in this CodeSandbox by following this link, and the relevant code snippet can be summarized as:

"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>
  )
}

Answer №1

In order to achieve a seamless scrolling experience with interactive <div> elements on top of the Excalidraw canvas, a combination of CSS and JavaScript can be utilized. The main objective is to allow scroll events to pass through the overlaying <div> elements to reach the Excalidraw canvas, while ensuring that other events like clicks or drags remain responsive within the <div> elements.

Below is the modified code snippet 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>
  )
}

Answer №2

Unfortunately, I am unable to make changes directly to the code in your sandbox. However, I was able to successfully edit it using Chrome DevTools. You can follow the same steps in your source code. To resolve the issue, you need to intercept the wheel event and manually dispatch it to the canvas.

In order to reference the yellow box in my javascript code, I assigned it an ID (my123). Additionally, I created a variable that points to the canvas within our event handler:

myCanvas = $('.excalidraw__canvas.interactive')

Then, I added the following code:

$('#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
    }));
}

By including the ctrlKey and shiftKey properties, you can enable zooming and horizontal scrolling as well.

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

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Is there a way to turn off the "swipe to navigate back" feature in Microsoft Edge using JavaScript or jQuery?

Working on a website that features horizontal object rotation. For instance, starting with the front view of an object followed by side, back, other side, and then back to the front. In this case, there are 24 images of an object, each taken at a 15 degre ...

The correct way to write media queries in CSS for the iPhone

After successfully formatting a website for various devices using an online responsive design tester and CSS declarations like @media only screen and (max-width: 480px), I encountered an issue during testing on an actual iPhone. The browser was not computi ...

Searching for a specific value within a list that has been fetched from a database using AngularJs can be achieved by simply clicking on the search button

Seeking assistance on a search functionality issue. I am able to filter search results from a list retrieved from the database and displayed on an HTML page, but facing difficulty in getting complete search results from the controller. When clicking the se ...

Conceal the secret key for the Firebase FCM service worker

I am facing an issue with Firebase FCM tokens where I need to hide key values from the service worker. Despite trying environment variables, they are not being applied. Can anyone provide a solution for this? I am looking to receive payload through the on ...

Fade effects on hover with a changing background color

Here is the CSS code to be used: #onec-fourth:hover { background-color: #F36DE1; color: white; } I am looking to keep the background color and text color effects for 1 second after I move my mouse off the object (#onec-fourth). Currently, when I mov ...

The information displayed in Next.js appears in the form of an array, which is distinct from the JSON object provided by the API

I am retrieving data from a Laravel API using getServerSideProps(): This is the method I use to fetch the data: function Product({ data }) { console.log(data); return ( <div>Hello</div> ) } export async function getServerSidePr ...

Using Bootstrap to Position DIVs at the Top, Middle, and Bottom

I'm looking to create a layout with three DIVs inside a container DIV, all centered horizontally. The first DIV should be aligned at the top of the container, the second in the vertical center, and the third at the bottom. While I found some helpful t ...

The latest bug fix and update in Bootstrap 4.5.2 now includes an updated version of Popper.js. Make sure to use the

Hello fellow developers, I am currently working with npm bootstrap version 4.5.2 and above. However, I am facing an issue with the compatibility of @popperjs/core. If anyone can assist me in resolving the bootstrap.js error temporarily, specifically re ...

Can I exclusively utilize selectFromResult within an RTK query component that is a descendant, or is it permissible to use it in any location?

Currently, I am utilizing next in conjunction with RTK query. In my implementation, I have a query called getPosts that retrieves and exhibits posts. Upon clicking on an individual post, a new page is opened. Now, I am looking to use selectFromResult to ...

The fancybox scroll feature is visible when using a browser, but unfortunately, it does not appear on the

I noticed that the scrollbar in my fancybox is not showing up on iPad, but it does display when using Google Chrome on Desktop. You can find the page here. The issue seems to be with the vertical scroll bar not appearing on iPad. Has anyone encountered th ...

Global Reject in Redux Toolkit allows you to reject or handle errors

Working with Redux Toolkit and managing multiple slices, there is one specific slice called sessionSlice. This slice includes various extraReducers that import thunks from other slices as shown below: [fetchDashboardUserReq.rejected]: (state, ...

Using NextJS App Router to implement redirects within the useEffect hook

I'm currently working on a Next.js 13.4.9 project and encountering an issue with redirecting logged in users within a client component using the useEffect hook. Despite trying a specific method, I keep getting an error in the browser (NEXT_REDIRECT). ...

React - Issue with state not being updated accurately

My current project involves using the <Select> component from Material-ui to create a drop-down menu. I need to update the state of the <Select> component after a selection has been made. To achieve this, I have set the value property of the & ...

A guide on implementing the href attribute in Material-ui

I previously asked about this code, but my wording was unclear. I am trying to get the menu items to work with href links. Currently, only the "Home" button works with href, while the "Sessions Home", "Book a Session", and "[S] Host a session" buttons do n ...

Capture the beauty of Safari with images displayed in the input file element

I'm facing an issue with the file upload element on my website. Most browsers, like Chrome and FF, display the image name when a file is chosen. However, Safari seems to show a tiny thumbnail instead. My goal is to create a custom upload button that ...

Acquiring HTML form information in ASP.NET controller

Currently, I have a view that includes an HTML login form and I am working on passing the form values to a controller via post. My goal is to display the user's username using the Content method once the form is submitted. However, I am facing an issu ...

jQuery Show/Hide Not Working Properly

I'm attempting to showcase one Tweet at a time and have it rotate through different tweets. I'm facing an issue where it works correctly on one type of page, but not on the other. Could this be due to a CSS precedence rule overriding the function ...

Route Handler 13 is encountering difficulties in retrieving data from the body in the (app/api/auth) endpoint

Whenever I attempt to retrieve the body from the new export async function POST( req: Request), it seems to come through as a stream instead of the expected content type. The route handler can be found in api/auth/signup See folder layout image export asyn ...

Unveiling the Secrets of Implementing Interactive HTML Tabs

I have a simple HTML page that contains a table with two columns and two rows. My goal is to convert these two columns into tabs, so that when each row is clicked, it will display either a paragraph or another basic table with one row and column. All of t ...

Unlocking the power of python with web2py by tapping into html form values

I'm working on a project using web2py and need to incorporate ajax/javascript with a form. Currently, when a user selects an option in the departure box, the arrival box appears. However, I'm unsure of how to filter the list of arrival options ba ...