williamngan / pts

A library for visualization and creative-coding
https://ptsjs.org
Apache License 2.0
5.19k stars 182 forks source link

Can events be bound to a different element? #103

Open chrisui opened 4 years ago

chrisui commented 4 years ago

Ie. Render in a child canvas but bind events to parent div.

This would allow for easily overlaying the canvas with interactive dom elements.

chrisui commented 4 years ago

Note I've also tried passing the parent element to quickStart which means Pts creates and manages the canvas element for me. However it seems that the event bindings are still made on the canvas rather than the parent element (a div in this case).

chrisui commented 4 years ago

See http://chrispearce.co/ for my example of the issue :) And code here: https://github.com/chrisui/chrispearce.co/blob/master/src/index.html#L214

williamngan commented 4 years ago

Hi Chris, sorry I don't think I understand fully. What are the events you want to pass to other elements?

Do you mean using a custom event? For example: https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events

Or do you mean that the overlay elements are blocking the events that should pass through to canvas underneath? In these cases, if the overlay is non-interactive, you can fix it by css pointer-events: none on the overlay.

chrisui commented 4 years ago

The latter. Sadly I can't use pointer-events because there are buttons in the overlay and ideally the text is selectable still.

Ideally you would be able to pass through two elements when initialising. First the canvas to render to and secondly, optionally otherwise default to canvas, an element to bind events to which would avoid these issues. Ie. here you would not always bind to the "canvas" but to the "event target element" (which would default to canvas)

I also reckon when you pass through a non-canvas element and Pts creates a canvas node for you, the events should be bound to the element I chose.

Given html

<div id="container"></div>

And initialised with

Pts.quickStart("#container", "rgb(22, 22, 22)");

I'd expect Pts to create canvas and construct the tree

<div id="container">
  <canvas>
</div>

And for events to be bound to div#container (the element I was explicit about).

Alternatively without changing this behaviour just add a new parameter for setting which element to bind to.

Pts.quickStart("#container", "rgb(22, 22, 22)", "#container"); // 3rd parameter is the event target

And finally another possibility would be to add a parameter to the bindMouse (etc) methods to accept an element to bind to.

space.bindMouse(true, '#container');
williamngan commented 4 years ago

Thanks @chrisui - let me take a look into this.

williamngan commented 4 years ago

One quick idea @chrisui -- if you hack it by extending the CanvasSpace class and hardcode your container div like this, I wonder if it would work?

class AnotherSpace extends CanvasSpace {

  bindCanvas(evt, callback) {
    document.querySelector("#containerID").addEventListener( evt, callback );
  }

  unbindCanvas(evt, callback) {
    document.querySelector("#containerID").removeEventListener( evt, callback );
  }
}
chrisui commented 4 years ago

This should work well enough for my use case, though worried that there may be other internals which get confused due to event element being different to that which the space has reference to. Ie. calculating absolute mouse position using scroll+element offset etc.

williamngan commented 4 years ago

Agreed. Will think about how to support this in the API.