From b02ac65e68f37a983c83e28625957a084d929fd7 Mon Sep 17 00:00:00 2001 From: Anton Lavrenov Date: Tue, 5 Feb 2019 16:43:43 -0500 Subject: [PATCH] fix docs, Better implementation of `mouseover` event for stage --- CHANGELOG.md | 3 +- konva.js | 46 ++++++++++++++++++++++++----- src/Node.ts | 17 +++++++++-- src/Shape.ts | 8 +++++ src/Stage.ts | 16 +++++++--- src/Tween.ts | 7 +++-- src/shapes/Transformer.ts | 1 + test/unit/Stage-test.js | 61 +++++++++++++++++++++++++++++++++++++++ 8 files changed, 142 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f9b0c5ba..579bb8f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). * Full rewrite to Typescript with tons of refactoring and small optimizations. The public API should be 100% the same * Fixed `patternImage` and `radialGradient` for `Konva.Text` * `Konva.Util._isObject` is renamed to `Konva.Util._isPlainObject`. -* A bit changed behavior of `removeId` (private method), not it doesn't clear node ref, of id is changed. +* A bit changed behavior of `removeId` (private method), now it doesn't clear node ref, if object is changed. * simplified `batchDraw` method (it doesn't use `Konva.Animation`) now. * `id` and `name` properties defaults are empty strings, not `undefined` @@ -30,6 +30,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Fixed * Better mouse support on mobile devices (yes, that is possible to connect mouse to mobile) +* Better implementation of `mouseover` event for stage ## [2.6.0][2018-12-14] diff --git a/konva.js b/konva.js index 4f53a3c6..91d8e72a 100644 --- a/konva.js +++ b/konva.js @@ -3433,6 +3433,8 @@ } return false; }; + // TODO: validate z index + // it should be >= 0 and < length Node.prototype.setZIndex = function (zIndex) { if (!this.parent) { Util.warn('Node has no parent. zIndex parameter is ignored.'); @@ -3845,7 +3847,10 @@ * @param {Number} [config.y] y position of canvas section * @param {Number} [config.width] width of canvas section * @param {Number} [config.height] height of canvas section - * @paremt {Number} [config.pixelRatio] pixelRatio of ouput image. Default is 1. + * @param {Number} [config.pixelRatio] pixelRatio of output canvas. Default is 1. + * You can use that property to increase quality of the image, for example for super hight quality exports + * or usage on retina (or similar) displays. pixelRatio will be used to multiply the size of exported image. + * If you export to 500x500 size with pixelRatio = 2, then produced image will have size 1000x1000. * @example * var canvas = node.toCanvas(); */ @@ -3868,7 +3873,10 @@ * @param {Number} [config.quality] jpeg quality. If using an "image/jpeg" mimeType, * you can specify the quality from 0 to 1, where 0 is very poor quality and 1 * is very high quality - * @param {Number} [config.pixelRatio] pixelRatio of output image url. Default is 1 + * @param {Number} [config.pixelRatio] pixelRatio of output image url. Default is 1. + * You can use that property to increase quality of the image, for example for super hight quality exports + * or usage on retina (or similar) displays. pixelRatio will be used to multiply the size of exported image. + * If you export to 500x500 size with pixelRatio = 2, then produced image will have size 1000x1000. * @returns {String} */ Node.prototype.toDataURL = function (config) { @@ -3897,7 +3905,10 @@ * @param {Number} [config.quality] jpeg quality. If using an "image/jpeg" mimeType, * you can specify the quality from 0 to 1, where 0 is very poor quality and 1 * is very high quality - * @paremt {Number} [config.pixelRatio] pixelRatio of ouput image. Default is 1. + * @param {Number} [config.pixelRatio] pixelRatio of output image. Default is 1. + * You can use that property to increase quality of the image, for example for super hight quality exports + * or usage on retina (or similar) displays. pixelRatio will be used to multiply the size of exported image. + * If you export to 500x500 size with pixelRatio = 2, then produced image will have size 1000x1000. * @example * var image = node.toImage({ * callback(img) { @@ -5716,7 +5727,12 @@ }; Stage.prototype._mouseover = function (evt) { this._setPointerPosition(evt); + // TODO: add test on mouseover + // I guess it should fire on: + // 1. mouseenter + // 2. leave or enter any shape this._fire(CONTENT_MOUSEOVER, { evt: evt }); + this._fire(MOUSEOVER, { evt: evt, target: this, currentTarget: this }); }; Stage.prototype._mouseout = function (evt) { this._setPointerPosition(evt); @@ -5739,8 +5755,8 @@ if (!getGlobalKonva().isDragging()) { shape = this.getIntersection(this.getPointerPosition()); if (shape && shape.isListening()) { - if (!getGlobalKonva().isDragging() && - (!this.targetShape || this.targetShape._id !== shape._id)) { + var differentTarget = !this.targetShape || this.targetShape !== shape; + if (!getGlobalKonva().isDragging() && differentTarget) { if (this.targetShape) { this.targetShape._fireAndBubble(MOUSEOUT, { evt: evt }, shape); this.targetShape._fireAndBubble(MOUSELEAVE$1, { evt: evt }, shape); @@ -5761,6 +5777,11 @@ if (this.targetShape && !getGlobalKonva().isDragging()) { this.targetShape._fireAndBubble(MOUSEOUT, { evt: evt }); this.targetShape._fireAndBubble(MOUSELEAVE$1, { evt: evt }); + this._fire(MOUSEOVER, { + evt: evt, + target: this, + currentTarget: this + }); this.targetShape = null; } this._fire(MOUSEMOVE, { @@ -6670,6 +6691,13 @@ var HAS_SHADOW = 'hasShadow'; var SHADOW_RGBA = 'shadowRGBA'; // TODO: cache gradient from context + // TODO: write a test for adding destroyed shape into the layer + // will it draw? + // will it pass hit test? + // show warning on adding destroyed shape? + // TODO: idea - use only "remove" (or destroy method) + // how? on add, check that every inner shape has reference in konva store with color + // on remove - clear that reference function _fillFunc(context) { context.fill(); } @@ -8489,8 +8517,11 @@ * @example * * circle.to({ - * x : 50, - * duration : 0.5 + * x : 50, + * duration : 0.5, + * onFinish: () => { + * console.log('finished'); + * } * }); */ Node.prototype.to = function (params) { @@ -12584,6 +12615,7 @@ } return this; }; + // TODO: add docs, use overloaded setter/getter Transformer.prototype.getNode = function () { return this._node; }; diff --git a/src/Node.ts b/src/Node.ts index d2a348ff..5b6fc1f0 100644 --- a/src/Node.ts +++ b/src/Node.ts @@ -1242,6 +1242,8 @@ export abstract class Node { } return false; } + // TODO: validate z index + // it should be >= 0 and < length setZIndex(zIndex) { if (!this.parent) { Util.warn('Node has no parent. zIndex parameter is ignored.'); @@ -1701,7 +1703,10 @@ export abstract class Node { * @param {Number} [config.y] y position of canvas section * @param {Number} [config.width] width of canvas section * @param {Number} [config.height] height of canvas section - * @paremt {Number} [config.pixelRatio] pixelRatio of ouput image. Default is 1. + * @param {Number} [config.pixelRatio] pixelRatio of output canvas. Default is 1. + * You can use that property to increase quality of the image, for example for super hight quality exports + * or usage on retina (or similar) displays. pixelRatio will be used to multiply the size of exported image. + * If you export to 500x500 size with pixelRatio = 2, then produced image will have size 1000x1000. * @example * var canvas = node.toCanvas(); */ @@ -1724,7 +1729,10 @@ export abstract class Node { * @param {Number} [config.quality] jpeg quality. If using an "image/jpeg" mimeType, * you can specify the quality from 0 to 1, where 0 is very poor quality and 1 * is very high quality - * @param {Number} [config.pixelRatio] pixelRatio of output image url. Default is 1 + * @param {Number} [config.pixelRatio] pixelRatio of output image url. Default is 1. + * You can use that property to increase quality of the image, for example for super hight quality exports + * or usage on retina (or similar) displays. pixelRatio will be used to multiply the size of exported image. + * If you export to 500x500 size with pixelRatio = 2, then produced image will have size 1000x1000. * @returns {String} */ toDataURL(config) { @@ -1754,7 +1762,10 @@ export abstract class Node { * @param {Number} [config.quality] jpeg quality. If using an "image/jpeg" mimeType, * you can specify the quality from 0 to 1, where 0 is very poor quality and 1 * is very high quality - * @paremt {Number} [config.pixelRatio] pixelRatio of ouput image. Default is 1. + * @param {Number} [config.pixelRatio] pixelRatio of output image. Default is 1. + * You can use that property to increase quality of the image, for example for super hight quality exports + * or usage on retina (or similar) displays. pixelRatio will be used to multiply the size of exported image. + * If you export to 500x500 size with pixelRatio = 2, then produced image will have size 1000x1000. * @example * var image = node.toImage({ * callback(img) { diff --git a/src/Shape.ts b/src/Shape.ts index 844261b2..4e985bec 100644 --- a/src/Shape.ts +++ b/src/Shape.ts @@ -10,6 +10,14 @@ var HAS_SHADOW = 'hasShadow'; var SHADOW_RGBA = 'shadowRGBA'; // TODO: cache gradient from context +// TODO: write a test for adding destroyed shape into the layer +// will it draw? +// will it pass hit test? +// show warning on adding destroyed shape? + +// TODO: idea - use only "remove" (or destroy method) +// how? on add, check that every inner shape has reference in konva store with color +// on remove - clear that reference function _fillFunc(context) { context.fill(); diff --git a/src/Stage.ts b/src/Stage.ts index efe324c8..32a49dc6 100644 --- a/src/Stage.ts +++ b/src/Stage.ts @@ -345,7 +345,12 @@ export class Stage extends Container { } _mouseover(evt) { this._setPointerPosition(evt); + // TODO: add test on mouseover + // I guess it should fire on: + // 1. mouseenter + // 2. leave or enter any shape this._fire(CONTENT_MOUSEOVER, { evt: evt }); + this._fire(MOUSEOVER, { evt: evt, target: this, currentTarget: this }); } _mouseout(evt) { this._setPointerPosition(evt); @@ -371,10 +376,8 @@ export class Stage extends Container { if (!getGlobalKonva().isDragging()) { shape = this.getIntersection(this.getPointerPosition()); if (shape && shape.isListening()) { - if ( - !getGlobalKonva().isDragging() && - (!this.targetShape || this.targetShape._id !== shape._id) - ) { + var differentTarget = !this.targetShape || this.targetShape !== shape; + if (!getGlobalKonva().isDragging() && differentTarget) { if (this.targetShape) { this.targetShape._fireAndBubble(MOUSEOUT, { evt: evt }, shape); this.targetShape._fireAndBubble(MOUSELEAVE, { evt: evt }, shape); @@ -393,6 +396,11 @@ export class Stage extends Container { if (this.targetShape && !getGlobalKonva().isDragging()) { this.targetShape._fireAndBubble(MOUSEOUT, { evt: evt }); this.targetShape._fireAndBubble(MOUSELEAVE, { evt: evt }); + this._fire(MOUSEOVER, { + evt: evt, + target: this, + currentTarget: this + }); this.targetShape = null; } this._fire(MOUSEMOVE, { diff --git a/src/Tween.ts b/src/Tween.ts index b7374aad..e7a275e7 100644 --- a/src/Tween.ts +++ b/src/Tween.ts @@ -525,8 +525,11 @@ export class Tween { * @example * * circle.to({ - * x : 50, - * duration : 0.5 + * x : 50, + * duration : 0.5, + * onFinish: () => { + * console.log('finished'); + * } * }); */ Node.prototype.to = function(params) { diff --git a/src/shapes/Transformer.ts b/src/shapes/Transformer.ts index d0f070c6..638d91ac 100644 --- a/src/shapes/Transformer.ts +++ b/src/shapes/Transformer.ts @@ -223,6 +223,7 @@ export class Transformer extends Group { } return this; } + // TODO: add docs, use overloaded setter/getter getNode() { return this._node; } diff --git a/test/unit/Stage-test.js b/test/unit/Stage-test.js index 7569e190..ee1e776f 100644 --- a/test/unit/Stage-test.js +++ b/test/unit/Stage-test.js @@ -1092,6 +1092,67 @@ suite('Stage', function() { assert.equal(dblicks, 1, 'first dbclick registered'); }); + test.only('test mouseover event on stage', function() { + var stage = addStage(); + var layer = new Konva.Layer(); + stage.add(layer); + var rect = new Konva.Rect({ + x: 50, + y: 50, + width: stage.width(), + height: stage.height(), + fill: 'red' + }); + layer.add(rect); + layer.draw(); + + var mouseover = 0; + + stage.on('mouseover', function(e) { + mouseover += 1; + + if (mouseover === 1) { + assert.equal(e.target, stage); + assert.equal(e.currentTarget, stage); + } + if (mouseover === 2) { + assert.equal(e.target, rect); + assert.equal(e.currentTarget, stage); + } + }); + + stage._mouseover({ + clientX: 0, + clientY: 0 + }); + + assert.equal(mouseover, 1, 'initial over'); + stage.simulateMouseMove({ + x: 10, + y: 10 + }); + + assert.equal(mouseover, 1, 'moved inside stage - no new over events'); + + stage.simulateMouseMove({ + x: 60, + y: 60 + }); + + assert.equal(mouseover, 2, 'moved into inner shape, trigger new mouseover'); + + stage.simulateMouseMove({ + x: 10, + y: 10 + }); + + assert.equal( + mouseover, + 3, + 'moved out of inner shape, trigger new mouseover' + ); + }); + test('toCanvas in sync way', function() { var stage = addStage(); var layer = new Konva.Layer();