Search code examples
javascriptmouseeventpixi.js

Capture mouse events from sibling or parent DOM element in PIXI.js


I would like to capture mouse events on two layers: PIXI's canvas and and overlaying div. I have the following kind of HTML setup where div.overlay is above canvas.pixi:

<div class="parent">
  <canvas class="pixi"></canvas>
  <div class="overlay"></div>
</div>

PIXI interaction work fine when the canvas is on top but I am unable to capture any events when the canvas is overlaid with div.overlay.

I found setTargetElement which seem to let us define the DOM element for capture elements and I tried to use it like so:

const renderer = PIXI.autoDetectRenderer(...);
renderer.plugins.interaction.setTargetElement(document.querySelector('.overlay'));

Using this technique I am able to capture mousemove events but unfortunately click, mousedown, etc. do not work.

I've also tried to copy the original events captured on div.overlay and duplicate ans dispatch the events on canvas as shown below but that also doesn't do the trick.

document.querySelector('.overlay').addEventListener('mousedown', (e) => {
  const eventCopy = document.createEvent('MouseEvents');
  eventCopy.initMouseEvent(
    e.type, e.bubbles, e.cancelable, e.view,
    e.detail, e.pageX || e.layerX,
    e.pageY || e.layerY, e.clientX,
    e.clientY, e.ctrlKey, e.altKey, e.shiftKey,
    e.metaKey, e.button, e.relatedTarget
  );

  document.querySelector('.pixi').dispatchEvent(eventCopy);
});

Is there any way to capture mouse events on an overlaid DOM element and to pass the events to PIXI?

Why?

I would like to interact with PIXI elements while at the same time being able to utilize D3's zoom and brush functionality, which is currently being handled on the overlaying div.

Update & Code Example

I managed to forward events and all but click events are registered by PIXI. Click events can be manually triggered by re-firing pointerdown and pointerup events. Check out https://jsfiddle.net/v3chhhjk/1/


Solution

  • I got it working for mouseover, mouseout, mousedown, mouseup, and click events by cloning the events and reconstructing click events from mousedown and mouseup events. All but click events work out of the box when cloning events from the event layer but somehow click events do not fire.

    So given the following HTML structure:

    <div id="parent">
      <canvas id="pixi"></canvas>
      <div id="overlay"></div>
    </div>
    

    I listen to a bunch of events like so:

    const overlay = document.getElementById('overlay');
    
    overlay.addEventListener('pointerdown', forwardDown);
    overlay.addEventListener('pointerup', forwardUp);
    overlay.addEventListener('pointermove', forward);
    overlay.addEventListener('pointerover', forward);
    overlay.addEventListener('pointerout', forward);
    

    And then forwarding / cloning the events:

    const canvas = document.getElementById('pixi');
    let mousedown = false;
    
    const clone = e => new e.constructor(e.type, e);
    const forward = (e) => { canvas.dispatchEvent(clone(e)); };
    const forwardDown = (e) => { mousedown = true; forward(e); };
    const forwardUp = (e) => {
      forward(e);
      if (mousedown) console.log('It\'s a click!');
      mousedown = false;
    };
    

    The event can also be dispatched elsewhere do be recognized by D3 for example.

    Demo: https://jsfiddle.net/v3chhhjk/3/

    The top-left corner demonstrates how it works when directly catching events with PIXI. Top-right shows the issue when cloning events. Bottom-left shows a weird behavior where re-firing the same events actually makes PIXI recognize click events and finally, bottom right.