diff --git a/src/Node.ts b/src/Node.ts index 028eb812..e4af527d 100644 --- a/src/Node.ts +++ b/src/Node.ts @@ -2436,6 +2436,41 @@ export abstract class Node { this.off('touchstart.konva'); } + /** + * determine if node (at least partially) is currently in user-visible area + * @method + * @param {(Number | Object)} margin optional margin in pixels + * @param {Number} margin.x + * @param {Number} margin.y + * @returns {Boolean} + * @name Konva.Node#isClientRectOnScreen + */ + isClientRectOnScreen(margin?: number | {x: number; y: number;}): boolean { + const _margin = + margin === undefined + ? {x: 0, y: 0} + : isNaN(margin as any) + ? margin as {x: number; y: number;} + : {x: margin as number, y: margin as number} + ; + type Rect = {[k in 'x'|'y'|'width'|'height']: number}; + const haveIntersection = (r1: Rect, r2: Rect) => !( + r2.x > r1.x + r1.width || + r2.x + r2.width < r1.x || + r2.y > r1.y + r1.height || + r2.y + r2.height < r1.y + ); + const stage = this.getStage(); + if(!stage) return false; + const screenRect = { + x: (-stage.x() - _margin.x) / stage.scaleX(), + y: (-stage.y() - _margin.y) / stage.scaleY(), + width: (stage.width() + _margin.x) / stage.scaleX(), + height: (stage.height() + + _margin.y) / stage.scaleY() + }; + return haveIntersection(screenRect, this.getClientRect({relativeTo: stage as any})); + } + preventDefault: GetSet; // from filters diff --git a/test/unit/Node-test.ts b/test/unit/Node-test.ts index 1ae9fe2e..be2b4821 100644 --- a/test/unit/Node-test.ts +++ b/test/unit/Node-test.ts @@ -3774,4 +3774,30 @@ describe('Node', function () { assert.equal(text00.getClientRect().x, 90); assert.equal(text00.getClientRect().y, 90); }); + + // ====================================================== + test('isClientRectOnScreen() method', function () { + var stage = addStage(); + var layer = new Konva.Layer(); + var circle = new Konva.Circle({ + x: stage.getWidth() / 2, + y: stage.getHeight() / 2, + radius: 30, + fill: 'green', + stroke: 'black', + strokeWidth: 4, + }); + + layer.add(circle); + stage.add(layer); + + assert.equal(circle.isClientRectOnScreen(), true); + + circle.x(-circle.radius() - circle.strokeWidth()/2 - 1); // Move circle 1px outside of visible area + assert.equal(circle.isClientRectOnScreen(), false); + assert.equal(circle.isClientRectOnScreen(1), true); + assert.equal(circle.isClientRectOnScreen({x: 1, y: 0}), true); + assert.equal(circle.isClientRectOnScreen({x: 0, y: 1}), false); + + }); });