diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js index a0956a4..7d84580 100644 --- a/src/interaction/InteractionManager.js +++ b/src/interaction/InteractionManager.js @@ -13,6 +13,14 @@ const MOUSE_POINTER_ID = 'MOUSE'; +// helpers for hitTest() - only used inside hitTest() +const hitTestEvent = { + target: null, + data: { + global: null, + }, +}; + /** * The interaction manager deals with mouse, touch and pointer events. Any DisplayObject can be interactive * if its interactive parameter is set to true @@ -456,6 +464,32 @@ } /** + * Hit tests a point against the display tree, returning the first interactive object that is hit. + * + * @param {PIXI.Point} globalPoint - A point to hit test with, in global space. + * @param {PIXI.Container} [root] - The root display object to start from. If omitted, defaults + * to the last rendered root of the associated renderer. + * @return {PIXI.DisplayObject} The hit display object, if any. + */ + hitTest(globalPoint, root) + { + // clear the target for our hit test + hitTestEvent.target = null; + // assign the global point + hitTestEvent.data.global = globalPoint; + // ensure safety of the root + if (!root) + { + root = this.renderer._lastObjectRendered; + } + // run the hit test + this.processInteractive(hitTestEvent, root, null, true); + // return our found object - it'll be null if we didn't hit anything + + return hitTestEvent.target; + } + + /** * Sets the DOM element which will receive mouse/touch events. This is useful for when you have * other DOM elements on top of the renderers Canvas element. With this you'll be bale to deletegate * another DOM element to receive those events. @@ -881,7 +915,10 @@ interactionEvent.target = displayObject; } - func(interactionEvent, displayObject, !!hit); + if (func) + { + func(interactionEvent, displayObject, !!hit); + } } } diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js index a0956a4..7d84580 100644 --- a/src/interaction/InteractionManager.js +++ b/src/interaction/InteractionManager.js @@ -13,6 +13,14 @@ const MOUSE_POINTER_ID = 'MOUSE'; +// helpers for hitTest() - only used inside hitTest() +const hitTestEvent = { + target: null, + data: { + global: null, + }, +}; + /** * The interaction manager deals with mouse, touch and pointer events. Any DisplayObject can be interactive * if its interactive parameter is set to true @@ -456,6 +464,32 @@ } /** + * Hit tests a point against the display tree, returning the first interactive object that is hit. + * + * @param {PIXI.Point} globalPoint - A point to hit test with, in global space. + * @param {PIXI.Container} [root] - The root display object to start from. If omitted, defaults + * to the last rendered root of the associated renderer. + * @return {PIXI.DisplayObject} The hit display object, if any. + */ + hitTest(globalPoint, root) + { + // clear the target for our hit test + hitTestEvent.target = null; + // assign the global point + hitTestEvent.data.global = globalPoint; + // ensure safety of the root + if (!root) + { + root = this.renderer._lastObjectRendered; + } + // run the hit test + this.processInteractive(hitTestEvent, root, null, true); + // return our found object - it'll be null if we didn't hit anything + + return hitTestEvent.target; + } + + /** * Sets the DOM element which will receive mouse/touch events. This is useful for when you have * other DOM elements on top of the renderers Canvas element. With this you'll be bale to deletegate * another DOM element to receive those events. @@ -881,7 +915,10 @@ interactionEvent.target = displayObject; } - func(interactionEvent, displayObject, !!hit); + if (func) + { + func(interactionEvent, displayObject, !!hit); + } } } diff --git a/test/interaction/InteractionManager.js b/test/interaction/InteractionManager.js index c87fa5c..31385b9 100644 --- a/test/interaction/InteractionManager.js +++ b/test/interaction/InteractionManager.js @@ -1198,4 +1198,85 @@ expect(pointer.interaction.activeInteractionData[42]).to.be.undefined; }); }); + + describe('hitTest()', function () + { + it('should return hit', function () + { + const stage = new PIXI.Container(); + const graphics = new PIXI.Graphics(); + const pointer = this.pointer = new MockPointer(stage); + + stage.addChild(graphics); + graphics.beginFill(0xFFFFFF); + graphics.drawRect(0, 0, 50, 50); + graphics.interactive = true; + + pointer.render(); + const hit = pointer.interaction.hitTest(new PIXI.Point(10, 10)); + + expect(hit).to.equal(graphics); + }); + + it('should return null if not hit', function () + { + const stage = new PIXI.Container(); + const graphics = new PIXI.Graphics(); + const pointer = this.pointer = new MockPointer(stage); + + stage.addChild(graphics); + graphics.beginFill(0xFFFFFF); + graphics.drawRect(0, 0, 50, 50); + graphics.interactive = true; + + pointer.render(); + const hit = pointer.interaction.hitTest(new PIXI.Point(60, 60)); + + expect(hit).to.be.null; + }); + + it('should return top thing that was hit', function () + { + const stage = new PIXI.Container(); + const graphics = new PIXI.Graphics(); + const behind = new PIXI.Graphics(); + const pointer = this.pointer = new MockPointer(stage); + + stage.addChild(behind); + stage.addChild(graphics); + graphics.beginFill(0xFFFFFF); + graphics.drawRect(0, 0, 50, 50); + graphics.interactive = true; + behind.beginFill(0xFFFFFF); + behind.drawRect(0, 0, 50, 50); + behind.interactive = true; + + pointer.render(); + const hit = pointer.interaction.hitTest(new PIXI.Point(10, 10)); + + expect(hit).to.equal(graphics); + }); + + it('should return hit when passing in root', function () + { + const stage = new PIXI.Container(); + const graphics = new PIXI.Graphics(); + const behind = new PIXI.Graphics(); + const pointer = this.pointer = new MockPointer(stage); + + stage.addChild(behind); + stage.addChild(graphics); + graphics.beginFill(0xFFFFFF); + graphics.drawRect(0, 0, 50, 50); + graphics.interactive = true; + behind.beginFill(0xFFFFFF); + behind.drawRect(0, 0, 50, 50); + behind.interactive = true; + + pointer.render(); + const hit = pointer.interaction.hitTest(new PIXI.Point(10, 10), behind); + + expect(hit).to.equal(behind); + }); + }); });