paperjs / paper.js

The Swiss Army Knife of Vector Graphics Scripting – Scriptographer ported to JavaScript and the browser, using HTML5 Canvas. Created by @lehni & @puckey
http://paperjs.org
Other
14.43k stars 1.22k forks source link

items without listeners should not hitTest – onMouseEnter, onMouseLeave #1309

Open dmccuskey opened 7 years ago

dmccuskey commented 7 years ago

i'm creating a charting application. i have display bars, etc on a background. the background is watching for events : mouseEnter/Leave/Move.

Observed Behavior

when the mouse moves over one of the chart items, that item captures/interrupts mouse events, even though those items have no attached listeners.

Expected Behavior

i would expect objects that DO NOT have attached event listeners to essentially be "invisible" to mouse activity.

here's a sketch of the issue

Otherwise...

Option 1

from what i've read elsewhere, i guess all items are getting hit-tested. i see in Item.js that the following are in _hitTest():

        if (this._locked || !this._visible || this._guide && !options.guides
                || this.isEmpty()) {
            return null;
        }

so it appears that i could set ._locked, but i can't find an "official" way in the code to do so.

Option 2

i could attach listeners to all of my chart items and re-emit the events, but i could have hundreds of items on the graph. this option seems memory and performance inefficient.

Option 3

i'm open to suggestions...

ubwhole commented 7 years ago

@dmccuskey When I log the ID of the shape, it logs the ID of the blue rectangle. But when I assign the blue rectangle shape to a new variable, it logs the ID of the yellow rectangle.

...
console.log("Created new yellow rectangle shape " + o.id)
grp.addChild( o )
o.onMouseEnter = function( event ) {
    console.log("Enter " + o.id )
}
...

Is assigning to a new variable the correct thing to do?

dmccuskey commented 7 years ago

@ubwhole i've found that inside of the event-handling function,this is set to the proper object. in my example i'm just using o as a temporary variable for setup.

o.onMouseLeave = function( event ) {
    console.log("Leave", this.type, this.fillColor )
}

here is an updated sketch

as you'll see, i have commented out the event handlers for the blue rectangle.

ubwhole commented 7 years ago

@dmccuskey Using the this object makes more sense. So the problem lies with crossing the border between the blue and yellow box. Correct?

dmccuskey commented 7 years ago

@ubwhole yes the issue is when you cross over into the blue box. if there isn't an event handler on that object, then that object should be "invisible" to interaction, and we shouldn't see events for enter/leave on the yellow box.

take this image for example:

Image of cute frog

say that we have created this image with a single green background and various other basic shapes – circles, paths, rectangles, etc – gathered by a group. to allow interaction with the frog (shape collection), i may put MouseEnter/Leave handlers just on the green background – since that is the basic area of the image which defines all of the shapes. thus i can simply use the background to control interaction, perhaps to fade in/out the entire group.

what my simple example is demonstrating is that, if i mouse over any other shape object then that action will cycle enter/leave events, even if i'm not interested in those shapes (ie, no handlers on them).

to allow for this, any objects which don't have handlers should allow interaction to "fall through", as if they don't exist.

in a way, i see this similar to event bubbling. if you 1. don't have a handler or 2. don't stop even propagation, then the event will continue up the parent chain. in this case, if an object isn't listening for mouse events then it shouldn't be an active target for them, allowing other objects under them to receive them.

sasensi commented 5 years ago

You can indeed workaround this issue by locking (this property is now documented) blue rectangle. Here is a sketch demonstrating the solution.

// create overlapping rectangles
var yellowRectangle = Shape.Rectangle({
    rectangle: new Rectangle(0, 0, 150, 150),
    fillColor: 'yellow'
});
var blueRectangle = Shape.Rectangle({
    rectangle: new Rectangle(50, 50, 50, 50),
    fillColor: 'blue'
});

// attach event listeners
yellowRectangle.onMouseEnter = function(event) {
    console.log('Enter');
};
yellowRectangle.onMouseLeave = function(event) {
    console.log('Leave');
};

// lock blue rectangle so it does not interupt yellow rectangle hovering
blueRectangle.locked = true;