mirror of
https://github.com/konvajs/konva.git
synced 2025-06-27 22:51:29 +08:00
improved mouseover and mouseout event handling by ignoring parent handlers if mouse moves from one child to another
This commit is contained in:
parent
9a61e149df
commit
84e7e71461
94
dist/kinetic-core.js
vendored
94
dist/kinetic-core.js
vendored
@ -3,7 +3,7 @@
|
|||||||
* http://www.kineticjs.com/
|
* http://www.kineticjs.com/
|
||||||
* Copyright 2012, Eric Rowell
|
* Copyright 2012, Eric Rowell
|
||||||
* Licensed under the MIT or GPL Version 2 licenses.
|
* Licensed under the MIT or GPL Version 2 licenses.
|
||||||
* Date: Mar 17 2012
|
* Date: Mar 18 2012
|
||||||
*
|
*
|
||||||
* Copyright (C) 2011 - 2012 by Eric Rowell
|
* Copyright (C) 2011 - 2012 by Eric Rowell
|
||||||
*
|
*
|
||||||
@ -733,23 +733,42 @@ Kinetic.Node.prototype = {
|
|||||||
* @param {Event} evt
|
* @param {Event} evt
|
||||||
*/
|
*/
|
||||||
_handleEvents: function(eventType, evt) {
|
_handleEvents: function(eventType, evt) {
|
||||||
// generic events handler
|
var stage = this.getStage();
|
||||||
function handle(obj) {
|
this._handleEvent(this, stage.mouseoverShape, stage.mouseoutShape, eventType, evt);
|
||||||
var el = obj.eventListeners;
|
},
|
||||||
if(el[eventType]) {
|
/**
|
||||||
|
* handle node event
|
||||||
|
*/
|
||||||
|
_handleEvent: function(node, mouseoverNode, mouseoutNode, eventType, evt) {
|
||||||
|
var el = node.eventListeners;
|
||||||
|
var okayToRun = true;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* determine if event handler should be skipped by comparing
|
||||||
|
* parent nodes
|
||||||
|
*/
|
||||||
|
if(eventType === 'onmouseover' && mouseoutNode && mouseoutNode.id === node.id) {
|
||||||
|
okayToRun = false;
|
||||||
|
}
|
||||||
|
else if(eventType === 'onmouseout' && mouseoverNode && mouseoverNode.id === node.id) {
|
||||||
|
okayToRun = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(el[eventType] && okayToRun) {
|
||||||
var events = el[eventType];
|
var events = el[eventType];
|
||||||
for(var i = 0; i < events.length; i++) {
|
for(var i = 0; i < events.length; i++) {
|
||||||
events[i].handler.apply(obj, [evt]);
|
events[i].handler.apply(node, [evt]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var mouseoverParent = mouseoverNode ? mouseoverNode.parent : undefined;
|
||||||
|
var mouseoutParent = mouseoutNode ? mouseoutNode.parent : undefined;
|
||||||
|
|
||||||
// simulate event bubbling
|
// simulate event bubbling
|
||||||
if(!evt.cancelBubble && obj.parent.className !== 'Stage') {
|
if(!evt.cancelBubble && node.parent.className !== 'Stage') {
|
||||||
handle(obj.parent);
|
this._handleEvent(node.parent, mouseoverParent, mouseoutParent, eventType, evt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
handle(this);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////
|
||||||
@ -882,9 +901,11 @@ Kinetic.Stage = function(cont, width, height) {
|
|||||||
y: 1
|
y: 1
|
||||||
};
|
};
|
||||||
this.dblClickWindow = 400;
|
this.dblClickWindow = 400;
|
||||||
this.targetShape = undefined;
|
|
||||||
this.clickStart = false;
|
this.clickStart = false;
|
||||||
|
this.targetShape = undefined;
|
||||||
this.targetFound = false;
|
this.targetFound = false;
|
||||||
|
this.mouseoverShape = undefined;
|
||||||
|
this.mouseoutShape = undefined;
|
||||||
|
|
||||||
// desktop flags
|
// desktop flags
|
||||||
this.mousePos = undefined;
|
this.mousePos = undefined;
|
||||||
@ -1202,9 +1223,22 @@ Kinetic.Stage.prototype = {
|
|||||||
* NOTE: these event handlers require target shape
|
* NOTE: these event handlers require target shape
|
||||||
* handling
|
* handling
|
||||||
*/
|
*/
|
||||||
else if(!isDragging && this._isNewTarget(shape, evt)) {
|
|
||||||
// handle onmouseover
|
// handle onmouseover
|
||||||
|
else if(!isDragging && this._isNewTarget(shape, evt)) {
|
||||||
|
/*
|
||||||
|
* check to see if there are stored mouseout events first.
|
||||||
|
* if there are, run those before running the onmouseover
|
||||||
|
* events
|
||||||
|
*/
|
||||||
|
if(this.mouseoutShape) {
|
||||||
|
this.mouseoverShape = shape;
|
||||||
|
this.mouseoutShape._handleEvents('onmouseout', evt);
|
||||||
|
this.mouseoverShape = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
shape._handleEvents('onmouseover', evt);
|
shape._handleEvents('onmouseover', evt);
|
||||||
|
this._setTarget(shape);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1217,13 +1251,24 @@ Kinetic.Stage.prototype = {
|
|||||||
}
|
}
|
||||||
// handle mouseout condition
|
// handle mouseout condition
|
||||||
else if(!isDragging && this.targetShape && this.targetShape.id === shape.id) {
|
else if(!isDragging && this.targetShape && this.targetShape.id === shape.id) {
|
||||||
this.targetShape = undefined;
|
this._setTarget(undefined);
|
||||||
shape._handleEvents('onmouseout', evt);
|
this.mouseoutShape = shape;
|
||||||
|
//shape._handleEvents('onmouseout', evt);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* set new target
|
||||||
|
*/
|
||||||
|
_setTarget: function(shape) {
|
||||||
|
this.targetShape = shape;
|
||||||
|
this.targetFound = true;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* check if shape should be a new target
|
||||||
|
*/
|
||||||
_isNewTarget: function(shape, evt) {
|
_isNewTarget: function(shape, evt) {
|
||||||
if(!this.targetShape || (!this.targetFound && shape.id !== this.targetShape.id)) {
|
if(!this.targetShape || (!this.targetFound && shape.id !== this.targetShape.id)) {
|
||||||
/*
|
/*
|
||||||
@ -1232,14 +1277,10 @@ Kinetic.Stage.prototype = {
|
|||||||
if(this.targetShape) {
|
if(this.targetShape) {
|
||||||
var oldEl = this.targetShape.eventListeners;
|
var oldEl = this.targetShape.eventListeners;
|
||||||
if(oldEl) {
|
if(oldEl) {
|
||||||
this.targetShape._handleEvents('onmouseout', evt);
|
this.mouseoutShape = this.targetShape;
|
||||||
|
//this.targetShape._handleEvents('onmouseout', evt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// set new target shape
|
|
||||||
this.targetShape = shape;
|
|
||||||
this.targetFound = true;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -1293,15 +1334,26 @@ Kinetic.Stage.prototype = {
|
|||||||
* three nested loops
|
* three nested loops
|
||||||
*/
|
*/
|
||||||
this.targetFound = false;
|
this.targetFound = false;
|
||||||
|
var shapeDetected = false;
|
||||||
for(var n = this.children.length - 1; n >= 0; n--) {
|
for(var n = this.children.length - 1; n >= 0; n--) {
|
||||||
var layer = this.children[n];
|
var layer = this.children[n];
|
||||||
if(layer.visible && n >= 0 && layer.isListening) {
|
if(layer.visible && n >= 0 && layer.isListening) {
|
||||||
if(this._traverseChildren(layer, evt)) {
|
if(this._traverseChildren(layer, evt)) {
|
||||||
n = -1;
|
n = -1;
|
||||||
|
shapeDetected = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* if no shape was detected and a mouseout shape has been stored,
|
||||||
|
* then run the onmouseout event handlers
|
||||||
|
*/
|
||||||
|
if(!shapeDetected && this.mouseoutShape) {
|
||||||
|
this.mouseoutShape._handleEvents('onmouseout', evt);
|
||||||
|
this.mouseoutShape = undefined;
|
||||||
|
|
||||||
|
}
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* begin listening for events by adding event handlers
|
* begin listening for events by adding event handlers
|
||||||
|
4
dist/kinetic-core.min.js
vendored
4
dist/kinetic-core.min.js
vendored
File diff suppressed because one or more lines are too long
37
src/Node.js
37
src/Node.js
@ -531,21 +531,40 @@ Kinetic.Node.prototype = {
|
|||||||
* @param {Event} evt
|
* @param {Event} evt
|
||||||
*/
|
*/
|
||||||
_handleEvents: function(eventType, evt) {
|
_handleEvents: function(eventType, evt) {
|
||||||
// generic events handler
|
var stage = this.getStage();
|
||||||
function handle(obj) {
|
this._handleEvent(this, stage.mouseoverShape, stage.mouseoutShape, eventType, evt);
|
||||||
var el = obj.eventListeners;
|
},
|
||||||
if(el[eventType]) {
|
/**
|
||||||
|
* handle node event
|
||||||
|
*/
|
||||||
|
_handleEvent: function(node, mouseoverNode, mouseoutNode, eventType, evt) {
|
||||||
|
var el = node.eventListeners;
|
||||||
|
var okayToRun = true;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* determine if event handler should be skipped by comparing
|
||||||
|
* parent nodes
|
||||||
|
*/
|
||||||
|
if(eventType === 'onmouseover' && mouseoutNode && mouseoutNode.id === node.id) {
|
||||||
|
okayToRun = false;
|
||||||
|
}
|
||||||
|
else if(eventType === 'onmouseout' && mouseoverNode && mouseoverNode.id === node.id) {
|
||||||
|
okayToRun = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(el[eventType] && okayToRun) {
|
||||||
var events = el[eventType];
|
var events = el[eventType];
|
||||||
for(var i = 0; i < events.length; i++) {
|
for(var i = 0; i < events.length; i++) {
|
||||||
events[i].handler.apply(obj, [evt]);
|
events[i].handler.apply(node, [evt]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var mouseoverParent = mouseoverNode ? mouseoverNode.parent : undefined;
|
||||||
|
var mouseoutParent = mouseoutNode ? mouseoutNode.parent : undefined;
|
||||||
|
|
||||||
// simulate event bubbling
|
// simulate event bubbling
|
||||||
if(!evt.cancelBubble && obj.parent.className !== 'Stage') {
|
if(!evt.cancelBubble && node.parent.className !== 'Stage') {
|
||||||
handle(obj.parent);
|
this._handleEvent(node.parent, mouseoverParent, mouseoutParent, eventType, evt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
handle(this);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
55
src/Stage.js
55
src/Stage.js
@ -21,9 +21,11 @@ Kinetic.Stage = function(cont, width, height) {
|
|||||||
y: 1
|
y: 1
|
||||||
};
|
};
|
||||||
this.dblClickWindow = 400;
|
this.dblClickWindow = 400;
|
||||||
this.targetShape = undefined;
|
|
||||||
this.clickStart = false;
|
this.clickStart = false;
|
||||||
|
this.targetShape = undefined;
|
||||||
this.targetFound = false;
|
this.targetFound = false;
|
||||||
|
this.mouseoverShape = undefined;
|
||||||
|
this.mouseoutShape = undefined;
|
||||||
|
|
||||||
// desktop flags
|
// desktop flags
|
||||||
this.mousePos = undefined;
|
this.mousePos = undefined;
|
||||||
@ -341,9 +343,22 @@ Kinetic.Stage.prototype = {
|
|||||||
* NOTE: these event handlers require target shape
|
* NOTE: these event handlers require target shape
|
||||||
* handling
|
* handling
|
||||||
*/
|
*/
|
||||||
else if(!isDragging && this._isNewTarget(shape, evt)) {
|
|
||||||
// handle onmouseover
|
// handle onmouseover
|
||||||
|
else if(!isDragging && this._isNewTarget(shape, evt)) {
|
||||||
|
/*
|
||||||
|
* check to see if there are stored mouseout events first.
|
||||||
|
* if there are, run those before running the onmouseover
|
||||||
|
* events
|
||||||
|
*/
|
||||||
|
if(this.mouseoutShape) {
|
||||||
|
this.mouseoverShape = shape;
|
||||||
|
this.mouseoutShape._handleEvents('onmouseout', evt);
|
||||||
|
this.mouseoverShape = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
shape._handleEvents('onmouseover', evt);
|
shape._handleEvents('onmouseover', evt);
|
||||||
|
this._setTarget(shape);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -356,13 +371,24 @@ Kinetic.Stage.prototype = {
|
|||||||
}
|
}
|
||||||
// handle mouseout condition
|
// handle mouseout condition
|
||||||
else if(!isDragging && this.targetShape && this.targetShape.id === shape.id) {
|
else if(!isDragging && this.targetShape && this.targetShape.id === shape.id) {
|
||||||
this.targetShape = undefined;
|
this._setTarget(undefined);
|
||||||
shape._handleEvents('onmouseout', evt);
|
this.mouseoutShape = shape;
|
||||||
|
//shape._handleEvents('onmouseout', evt);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* set new target
|
||||||
|
*/
|
||||||
|
_setTarget: function(shape) {
|
||||||
|
this.targetShape = shape;
|
||||||
|
this.targetFound = true;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* check if shape should be a new target
|
||||||
|
*/
|
||||||
_isNewTarget: function(shape, evt) {
|
_isNewTarget: function(shape, evt) {
|
||||||
if(!this.targetShape || (!this.targetFound && shape.id !== this.targetShape.id)) {
|
if(!this.targetShape || (!this.targetFound && shape.id !== this.targetShape.id)) {
|
||||||
/*
|
/*
|
||||||
@ -371,14 +397,10 @@ Kinetic.Stage.prototype = {
|
|||||||
if(this.targetShape) {
|
if(this.targetShape) {
|
||||||
var oldEl = this.targetShape.eventListeners;
|
var oldEl = this.targetShape.eventListeners;
|
||||||
if(oldEl) {
|
if(oldEl) {
|
||||||
this.targetShape._handleEvents('onmouseout', evt);
|
this.mouseoutShape = this.targetShape;
|
||||||
|
//this.targetShape._handleEvents('onmouseout', evt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// set new target shape
|
|
||||||
this.targetShape = shape;
|
|
||||||
this.targetFound = true;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -432,15 +454,26 @@ Kinetic.Stage.prototype = {
|
|||||||
* three nested loops
|
* three nested loops
|
||||||
*/
|
*/
|
||||||
this.targetFound = false;
|
this.targetFound = false;
|
||||||
|
var shapeDetected = false;
|
||||||
for(var n = this.children.length - 1; n >= 0; n--) {
|
for(var n = this.children.length - 1; n >= 0; n--) {
|
||||||
var layer = this.children[n];
|
var layer = this.children[n];
|
||||||
if(layer.visible && n >= 0 && layer.isListening) {
|
if(layer.visible && n >= 0 && layer.isListening) {
|
||||||
if(this._traverseChildren(layer, evt)) {
|
if(this._traverseChildren(layer, evt)) {
|
||||||
n = -1;
|
n = -1;
|
||||||
|
shapeDetected = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* if no shape was detected and a mouseout shape has been stored,
|
||||||
|
* then run the onmouseout event handlers
|
||||||
|
*/
|
||||||
|
if(!shapeDetected && this.mouseoutShape) {
|
||||||
|
this.mouseoutShape._handleEvents('onmouseout', evt);
|
||||||
|
this.mouseoutShape = undefined;
|
||||||
|
|
||||||
|
}
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* begin listening for events by adding event handlers
|
* begin listening for events by adding event handlers
|
||||||
|
@ -458,28 +458,54 @@ Test.prototype.tests = {
|
|||||||
|
|
||||||
stage.add(layer);
|
stage.add(layer);
|
||||||
},
|
},
|
||||||
'EVENTS - event bubbling': function(containerId) {
|
'EVENTS - group click events': function(containerId) {
|
||||||
var stage = new Kinetic.Stage(containerId, 578, 200);
|
var stage = new Kinetic.Stage(containerId, 578, 200);
|
||||||
var layer = new Kinetic.Layer();
|
var layer = new Kinetic.Layer();
|
||||||
var group = new Kinetic.Group();
|
var group = new Kinetic.Group();
|
||||||
|
|
||||||
layer.on('mouseover', function() {
|
|
||||||
log('mouseover layer');
|
|
||||||
//console.log(this);
|
group.on('click', function() {
|
||||||
});
|
log('click group');
|
||||||
layer.on('mouseout', function() {
|
|
||||||
log('mouseout layer');
|
|
||||||
//console.log(this);
|
//console.log(this);
|
||||||
});
|
});
|
||||||
|
|
||||||
group.on('mouseover', function() {
|
var redCircle = new Kinetic.Circle({
|
||||||
log('mouseover group');
|
x: stage.width / 2,
|
||||||
//console.log(this);
|
y: stage.height / 2,
|
||||||
});
|
radius: 80,
|
||||||
group.on('mouseout', function() {
|
strokeWidth: 4,
|
||||||
log('mouseout group');
|
fill: 'red',
|
||||||
|
stroke: 'black',
|
||||||
|
name: 'red'
|
||||||
|
});
|
||||||
|
|
||||||
|
var greenCircle = new Kinetic.Circle({
|
||||||
|
x: stage.width / 2,
|
||||||
|
y: stage.height / 2,
|
||||||
|
radius: 40,
|
||||||
|
strokeWidth: 4,
|
||||||
|
fill: 'green',
|
||||||
|
stroke: 'black',
|
||||||
|
name: 'green'
|
||||||
|
});
|
||||||
|
|
||||||
|
group.add(redCircle);
|
||||||
|
group.add(greenCircle);
|
||||||
|
|
||||||
|
layer.add(group);
|
||||||
|
stage.add(layer);
|
||||||
|
},
|
||||||
|
'EVENTS - group mousemove events': function(containerId) {
|
||||||
|
var stage = new Kinetic.Stage(containerId, 578, 200);
|
||||||
|
var layer = new Kinetic.Layer();
|
||||||
|
var group = new Kinetic.Group();
|
||||||
|
|
||||||
|
group.on('mousemove', function() {
|
||||||
|
log('mousemove group');
|
||||||
//console.log(this);
|
//console.log(this);
|
||||||
});
|
});
|
||||||
|
|
||||||
var redCircle = new Kinetic.Circle({
|
var redCircle = new Kinetic.Circle({
|
||||||
x: stage.width / 2,
|
x: stage.width / 2,
|
||||||
y: stage.height / 2,
|
y: stage.height / 2,
|
||||||
@ -489,14 +515,6 @@ Test.prototype.tests = {
|
|||||||
stroke: 'black'
|
stroke: 'black'
|
||||||
});
|
});
|
||||||
|
|
||||||
redCircle.on('mouseover', function() {
|
|
||||||
log('mouseover red circle');
|
|
||||||
//console.log(this);
|
|
||||||
});
|
|
||||||
redCircle.on('mouseout', function() {
|
|
||||||
log('mouseout red circle');
|
|
||||||
//console.log(this);
|
|
||||||
});
|
|
||||||
var greenCircle = new Kinetic.Circle({
|
var greenCircle = new Kinetic.Circle({
|
||||||
x: stage.width / 2,
|
x: stage.width / 2,
|
||||||
y: stage.height / 2,
|
y: stage.height / 2,
|
||||||
@ -506,13 +524,45 @@ Test.prototype.tests = {
|
|||||||
stroke: 'black'
|
stroke: 'black'
|
||||||
});
|
});
|
||||||
|
|
||||||
greenCircle.on('mouseover', function() {
|
group.add(redCircle);
|
||||||
log('mouseover green circle');
|
group.add(greenCircle);
|
||||||
//console.log(this);
|
|
||||||
|
layer.add(group);
|
||||||
|
stage.add(layer);
|
||||||
|
},
|
||||||
|
'EVENTS - group mouseover events': function(containerId) {
|
||||||
|
var stage = new Kinetic.Stage(containerId, 578, 200);
|
||||||
|
var layer = new Kinetic.Layer();
|
||||||
|
var group = new Kinetic.Group({
|
||||||
|
name: 'group'
|
||||||
});
|
});
|
||||||
greenCircle.on('mouseout', function() {
|
|
||||||
log('mouseout green circle');
|
group.on('mouseover', function() {
|
||||||
//console.log(this);
|
log('mouseover group');
|
||||||
|
});
|
||||||
|
|
||||||
|
group.on('mouseout', function() {
|
||||||
|
log('mouseout group');
|
||||||
|
});
|
||||||
|
|
||||||
|
var redCircle = new Kinetic.Circle({
|
||||||
|
x: stage.width / 2,
|
||||||
|
y: stage.height / 2,
|
||||||
|
radius: 80,
|
||||||
|
strokeWidth: 4,
|
||||||
|
fill: 'red',
|
||||||
|
stroke: 'black',
|
||||||
|
name: 'red'
|
||||||
|
});
|
||||||
|
|
||||||
|
var greenCircle = new Kinetic.Circle({
|
||||||
|
x: stage.width / 2,
|
||||||
|
y: stage.height / 2,
|
||||||
|
radius: 40,
|
||||||
|
strokeWidth: 4,
|
||||||
|
fill: 'green',
|
||||||
|
stroke: 'black',
|
||||||
|
name: 'green'
|
||||||
});
|
});
|
||||||
|
|
||||||
group.add(redCircle);
|
group.add(redCircle);
|
||||||
|
Loading…
Reference in New Issue
Block a user