mirror of
https://github.com/konvajs/konva.git
synced 2025-12-17 18:01:15 +08:00
event delegation support
This commit is contained in:
@@ -8,6 +8,9 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
- correct `Konva.Arrow` drawing. Now it works better.
|
- correct `Konva.Arrow` drawing. Now it works better.
|
||||||
- Better support for dragging when mouse out of stage
|
- Better support for dragging when mouse out of stage
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- event delegation. You can use it in this way: `layer.on('click', 'Circle', handler);`
|
||||||
|
|
||||||
## [0.10.0][2015-10-27]
|
## [0.10.0][2015-10-27]
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
88
konva.js
88
konva.js
@@ -3,7 +3,7 @@
|
|||||||
* Konva JavaScript Framework v0.11.0
|
* Konva JavaScript Framework v0.11.0
|
||||||
* http://konvajs.github.io/
|
* http://konvajs.github.io/
|
||||||
* Licensed under the MIT or GPL Version 2 licenses.
|
* Licensed under the MIT or GPL Version 2 licenses.
|
||||||
* Date: Sat Nov 21 2015
|
* Date: Sun Nov 22 2015
|
||||||
*
|
*
|
||||||
* Original work Copyright (C) 2011 - 2013 by Eric Rowell (KineticJS)
|
* Original work Copyright (C) 2011 - 2013 by Eric Rowell (KineticJS)
|
||||||
* Modified work Copyright (C) 2014 - 2015 by Anton Lavrenov (Konva)
|
* Modified work Copyright (C) 2014 - 2015 by Anton Lavrenov (Konva)
|
||||||
@@ -800,6 +800,13 @@ var Konva = {};
|
|||||||
}
|
}
|
||||||
return names.length > 0;
|
return names.length > 0;
|
||||||
},
|
},
|
||||||
|
isValidSelector: function(selector) {
|
||||||
|
if (typeof selector !== 'string') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var firstChar = selector[0];
|
||||||
|
return firstChar === '#' || firstChar === '.' || firstChar === firstChar.toUpperCase();
|
||||||
|
},
|
||||||
createCanvasElement: function() {
|
createCanvasElement: function() {
|
||||||
var canvas = Konva.document.createElement('canvas');
|
var canvas = Konva.document.createElement('canvas');
|
||||||
// on some environments canvas.style is readonly
|
// on some environments canvas.style is readonly
|
||||||
@@ -2658,8 +2665,17 @@ var Konva = {};
|
|||||||
* var oldVal = evt.oldVal;
|
* var oldVal = evt.oldVal;
|
||||||
* var newVal = evt.newVal;
|
* var newVal = evt.newVal;
|
||||||
* });
|
* });
|
||||||
|
*
|
||||||
|
* // also event delegation works
|
||||||
|
* layer.on('click', 'Group', function(evt) {
|
||||||
|
* var shape = evt.target;
|
||||||
|
* var group = evtn.currentTarger;
|
||||||
|
* });
|
||||||
*/
|
*/
|
||||||
on: function(evtStr, handler) {
|
on: function(evtStr, handler) {
|
||||||
|
if (arguments.length === 3) {
|
||||||
|
return this._delegate.apply(this, arguments);
|
||||||
|
}
|
||||||
var events = evtStr.split(SPACE),
|
var events = evtStr.split(SPACE),
|
||||||
len = events.length,
|
len = events.length,
|
||||||
n, event, parts, baseEvent, name;
|
n, event, parts, baseEvent, name;
|
||||||
@@ -2757,6 +2773,17 @@ var Konva = {};
|
|||||||
removeEventListener: function(type) {
|
removeEventListener: function(type) {
|
||||||
this.off(type);
|
this.off(type);
|
||||||
},
|
},
|
||||||
|
_delegate: function(event, selector, handler) {
|
||||||
|
var stopNode = this;
|
||||||
|
this.on(event, function(evt) {
|
||||||
|
var targets = evt.target._findMatchers(selector, stopNode);
|
||||||
|
for(var i = 0; i < targets.length; i++) {
|
||||||
|
evt = Konva.Util.cloneObject(evt);
|
||||||
|
evt.currentTarget = targets[i];
|
||||||
|
handler.call(targets[i], evt);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
/**
|
/**
|
||||||
* remove self from parent, but don't destroy
|
* remove self from parent, but don't destroy
|
||||||
* @method
|
* @method
|
||||||
@@ -3408,6 +3435,49 @@ var Konva = {};
|
|||||||
getParent: function() {
|
getParent: function() {
|
||||||
return this.parent;
|
return this.parent;
|
||||||
},
|
},
|
||||||
|
_findMatchers: function(selector, stopNode) {
|
||||||
|
var res = [];
|
||||||
|
if (this._isMatch(selector)) {
|
||||||
|
res.push(this);
|
||||||
|
}
|
||||||
|
var parent = this.parent;
|
||||||
|
if (!parent) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
if (parent === stopNode) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
return res.concat(parent._findMatchers(selector, stopNode));
|
||||||
|
},
|
||||||
|
_isMatch: function(selector) {
|
||||||
|
var selectorArr = selector.replace(/ /g, '').split(','),
|
||||||
|
len = selectorArr.length,
|
||||||
|
n, sel;
|
||||||
|
|
||||||
|
for (n = 0; n < len; n++) {
|
||||||
|
sel = selectorArr[n];
|
||||||
|
if (!Konva.Util.isValidSelector(sel)) {
|
||||||
|
Konva.Util.warn('Selector "' + sel + '" is invalid. Allowed selectors examples are "#foo", ".bar" or "Group".');
|
||||||
|
Konva.Util.warn('If you have a custom shape with such className, please change it to start with upper letter like "Triangle".');
|
||||||
|
Konva.Util.warn('Konva is awesome, right?');
|
||||||
|
}
|
||||||
|
// id selector
|
||||||
|
if(sel.charAt(0) === '#') {
|
||||||
|
if (this.id() === sel.slice(1)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// name selector
|
||||||
|
else if(sel.charAt(0) === '.') {
|
||||||
|
if (this.hasName(sel.slice(1))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (this._get(sel).length !== 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
/**
|
/**
|
||||||
* get layer ancestor
|
* get layer ancestor
|
||||||
* @method
|
* @method
|
||||||
@@ -3462,12 +3532,14 @@ var Konva = {};
|
|||||||
*/
|
*/
|
||||||
fire: function(eventType, evt, bubble) {
|
fire: function(eventType, evt, bubble) {
|
||||||
// bubble
|
// bubble
|
||||||
|
evt = Konva.Util.cloneObject(evt || {});
|
||||||
|
evt.currentTarget = this;
|
||||||
if (bubble) {
|
if (bubble) {
|
||||||
this._fireAndBubble(eventType, evt || {});
|
this._fireAndBubble(eventType, evt);
|
||||||
}
|
}
|
||||||
// no bubble
|
// no bubble
|
||||||
else {
|
else {
|
||||||
this._fire(eventType, evt || {});
|
this._fire(eventType, evt);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
@@ -6377,14 +6449,6 @@ var Konva = {};
|
|||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
function isValidSelector(selector) {
|
|
||||||
if (typeof selector !== 'string') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
var firstChar = selector[0];
|
|
||||||
return firstChar === '#' || firstChar === '.' || firstChar === firstChar.toUpperCase();
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* Container constructor. Containers are used to contain nodes or other containers
|
* Container constructor. Containers are used to contain nodes or other containers
|
||||||
* @constructor
|
* @constructor
|
||||||
@@ -6583,7 +6647,7 @@ var Konva = {};
|
|||||||
|
|
||||||
for (n = 0; n < len; n++) {
|
for (n = 0; n < len; n++) {
|
||||||
sel = selectorArr[n];
|
sel = selectorArr[n];
|
||||||
if (!isValidSelector(sel)) {
|
if (!Konva.Util.isValidSelector(sel)) {
|
||||||
Konva.Util.warn('Selector "' + sel + '" is invalid. Allowed selectors examples are "#foo", ".bar" or "Group".');
|
Konva.Util.warn('Selector "' + sel + '" is invalid. Allowed selectors examples are "#foo", ".bar" or "Group".');
|
||||||
Konva.Util.warn('If you have a custom shape with such className, please change it to start with upper letter like "Triangle".');
|
Konva.Util.warn('If you have a custom shape with such className, please change it to start with upper letter like "Triangle".');
|
||||||
Konva.Util.warn('Konva is awesome, right?');
|
Konva.Util.warn('Konva is awesome, right?');
|
||||||
|
|||||||
12
konva.min.js
vendored
12
konva.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -1,13 +1,5 @@
|
|||||||
(function() {
|
(function() {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
function isValidSelector(selector) {
|
|
||||||
if (typeof selector !== 'string') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
var firstChar = selector[0];
|
|
||||||
return firstChar === '#' || firstChar === '.' || firstChar === firstChar.toUpperCase();
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* Container constructor. Containers are used to contain nodes or other containers
|
* Container constructor. Containers are used to contain nodes or other containers
|
||||||
* @constructor
|
* @constructor
|
||||||
@@ -182,7 +174,7 @@
|
|||||||
|
|
||||||
for (n = 0; n < len; n++) {
|
for (n = 0; n < len; n++) {
|
||||||
sel = selectorArr[n];
|
sel = selectorArr[n];
|
||||||
if (!isValidSelector(sel)) {
|
if (!Konva.Util.isValidSelector(sel)) {
|
||||||
Konva.Util.warn('Selector "' + sel + '" is invalid. Allowed selectors examples are "#foo", ".bar" or "Group".');
|
Konva.Util.warn('Selector "' + sel + '" is invalid. Allowed selectors examples are "#foo", ".bar" or "Group".');
|
||||||
Konva.Util.warn('If you have a custom shape with such className, please change it to start with upper letter like "Triangle".');
|
Konva.Util.warn('If you have a custom shape with such className, please change it to start with upper letter like "Triangle".');
|
||||||
Konva.Util.warn('Konva is awesome, right?');
|
Konva.Util.warn('Konva is awesome, right?');
|
||||||
|
|||||||
69
src/Node.js
69
src/Node.js
@@ -417,8 +417,17 @@
|
|||||||
* var oldVal = evt.oldVal;
|
* var oldVal = evt.oldVal;
|
||||||
* var newVal = evt.newVal;
|
* var newVal = evt.newVal;
|
||||||
* });
|
* });
|
||||||
|
*
|
||||||
|
* // also event delegation works
|
||||||
|
* layer.on('click', 'Group', function(evt) {
|
||||||
|
* var shape = evt.target;
|
||||||
|
* var group = evtn.currentTarger;
|
||||||
|
* });
|
||||||
*/
|
*/
|
||||||
on: function(evtStr, handler) {
|
on: function(evtStr, handler) {
|
||||||
|
if (arguments.length === 3) {
|
||||||
|
return this._delegate.apply(this, arguments);
|
||||||
|
}
|
||||||
var events = evtStr.split(SPACE),
|
var events = evtStr.split(SPACE),
|
||||||
len = events.length,
|
len = events.length,
|
||||||
n, event, parts, baseEvent, name;
|
n, event, parts, baseEvent, name;
|
||||||
@@ -516,6 +525,17 @@
|
|||||||
removeEventListener: function(type) {
|
removeEventListener: function(type) {
|
||||||
this.off(type);
|
this.off(type);
|
||||||
},
|
},
|
||||||
|
_delegate: function(event, selector, handler) {
|
||||||
|
var stopNode = this;
|
||||||
|
this.on(event, function(evt) {
|
||||||
|
var targets = evt.target._findMatchers(selector, stopNode);
|
||||||
|
for(var i = 0; i < targets.length; i++) {
|
||||||
|
evt = Konva.Util.cloneObject(evt);
|
||||||
|
evt.currentTarget = targets[i];
|
||||||
|
handler.call(targets[i], evt);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
/**
|
/**
|
||||||
* remove self from parent, but don't destroy
|
* remove self from parent, but don't destroy
|
||||||
* @method
|
* @method
|
||||||
@@ -1167,6 +1187,49 @@
|
|||||||
getParent: function() {
|
getParent: function() {
|
||||||
return this.parent;
|
return this.parent;
|
||||||
},
|
},
|
||||||
|
_findMatchers: function(selector, stopNode) {
|
||||||
|
var res = [];
|
||||||
|
if (this._isMatch(selector)) {
|
||||||
|
res.push(this);
|
||||||
|
}
|
||||||
|
var parent = this.parent;
|
||||||
|
if (!parent) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
if (parent === stopNode) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
return res.concat(parent._findMatchers(selector, stopNode));
|
||||||
|
},
|
||||||
|
_isMatch: function(selector) {
|
||||||
|
var selectorArr = selector.replace(/ /g, '').split(','),
|
||||||
|
len = selectorArr.length,
|
||||||
|
n, sel;
|
||||||
|
|
||||||
|
for (n = 0; n < len; n++) {
|
||||||
|
sel = selectorArr[n];
|
||||||
|
if (!Konva.Util.isValidSelector(sel)) {
|
||||||
|
Konva.Util.warn('Selector "' + sel + '" is invalid. Allowed selectors examples are "#foo", ".bar" or "Group".');
|
||||||
|
Konva.Util.warn('If you have a custom shape with such className, please change it to start with upper letter like "Triangle".');
|
||||||
|
Konva.Util.warn('Konva is awesome, right?');
|
||||||
|
}
|
||||||
|
// id selector
|
||||||
|
if(sel.charAt(0) === '#') {
|
||||||
|
if (this.id() === sel.slice(1)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// name selector
|
||||||
|
else if(sel.charAt(0) === '.') {
|
||||||
|
if (this.hasName(sel.slice(1))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (this._get(sel).length !== 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
/**
|
/**
|
||||||
* get layer ancestor
|
* get layer ancestor
|
||||||
* @method
|
* @method
|
||||||
@@ -1221,12 +1284,14 @@
|
|||||||
*/
|
*/
|
||||||
fire: function(eventType, evt, bubble) {
|
fire: function(eventType, evt, bubble) {
|
||||||
// bubble
|
// bubble
|
||||||
|
evt = Konva.Util.cloneObject(evt || {});
|
||||||
|
evt.currentTarget = this;
|
||||||
if (bubble) {
|
if (bubble) {
|
||||||
this._fireAndBubble(eventType, evt || {});
|
this._fireAndBubble(eventType, evt);
|
||||||
}
|
}
|
||||||
// no bubble
|
// no bubble
|
||||||
else {
|
else {
|
||||||
this._fire(eventType, evt || {});
|
this._fire(eventType, evt);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -539,6 +539,13 @@
|
|||||||
}
|
}
|
||||||
return names.length > 0;
|
return names.length > 0;
|
||||||
},
|
},
|
||||||
|
isValidSelector: function(selector) {
|
||||||
|
if (typeof selector !== 'string') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var firstChar = selector[0];
|
||||||
|
return firstChar === '#' || firstChar === '.' || firstChar === firstChar.toUpperCase();
|
||||||
|
},
|
||||||
createCanvasElement: function() {
|
createCanvasElement: function() {
|
||||||
var canvas = Konva.document.createElement('canvas');
|
var canvas = Konva.document.createElement('canvas');
|
||||||
// on some environments canvas.style is readonly
|
// on some environments canvas.style is readonly
|
||||||
|
|||||||
@@ -1846,6 +1846,63 @@ suite('Node', function() {
|
|||||||
assert.equal(clicks[1], 'layer');
|
assert.equal(clicks[1], 'layer');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('simple event delegation', function() {
|
||||||
|
var stage = addStage();
|
||||||
|
var layer = new Konva.Layer();
|
||||||
|
var circle = new Konva.Circle({
|
||||||
|
x: stage.getWidth() / 2,
|
||||||
|
y: stage.getHeight() / 2,
|
||||||
|
radius: 70,
|
||||||
|
fill: 'green',
|
||||||
|
stroke: 'black',
|
||||||
|
strokeWidth: 4,
|
||||||
|
name: 'myCircle'
|
||||||
|
});
|
||||||
|
|
||||||
|
stage.add(layer);
|
||||||
|
layer.add(circle);
|
||||||
|
layer.draw();
|
||||||
|
|
||||||
|
var fired = false;
|
||||||
|
layer.on('click', 'Circle', function(e) {
|
||||||
|
assert.equal(this, circle);
|
||||||
|
assert.equal(e.currentTarget, circle);
|
||||||
|
fired = true;
|
||||||
|
});
|
||||||
|
circle.fire('click', null, true);
|
||||||
|
assert(fired, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('complex event delegation', function() {
|
||||||
|
var stage = addStage();
|
||||||
|
var layer = new Konva.Layer();
|
||||||
|
stage.add(layer);
|
||||||
|
var circle = new Konva.Circle({
|
||||||
|
x: stage.getWidth() / 2,
|
||||||
|
y: stage.getHeight() / 2,
|
||||||
|
radius: 70,
|
||||||
|
fill: 'green',
|
||||||
|
stroke: 'black',
|
||||||
|
strokeWidth: 4,
|
||||||
|
name: 'myCircle'
|
||||||
|
});
|
||||||
|
var group = new Konva.Group({
|
||||||
|
name: 'group1 group2'
|
||||||
|
});
|
||||||
|
group.add(circle);
|
||||||
|
layer.add(group);
|
||||||
|
|
||||||
|
layer.draw();
|
||||||
|
|
||||||
|
var fired = false;
|
||||||
|
layer.on('click', '.group1', function(e) {
|
||||||
|
assert.equal(this, group);
|
||||||
|
assert.equal(e.currentTarget, group);
|
||||||
|
fired = true;
|
||||||
|
});
|
||||||
|
circle.fire('click', null, true);
|
||||||
|
assert(fired, true);
|
||||||
|
});
|
||||||
// ======================================================
|
// ======================================================
|
||||||
test('move shape, group, and layer, and then get absolute position', function() {
|
test('move shape, group, and layer, and then get absolute position', function() {
|
||||||
var stage = addStage();
|
var stage = addStage();
|
||||||
|
|||||||
Reference in New Issue
Block a user