fixed mousemove event bug, and added a very rigorous functional test to test all of the basic shape events for both desktop and mobile

This commit is contained in:
Eric Rowell
2012-06-18 17:56:12 -07:00
parent dc51d95eb1
commit bf616d0d4d
6 changed files with 402 additions and 152 deletions

57
dist/kinetic-core.js vendored
View File

@@ -3,7 +3,7 @@
* http://www.kineticjs.com/
* Copyright 2012, Eric Rowell
* Licensed under the MIT or GPL Version 2 licenses.
* Date: Jun 17 2012
* Date: Jun 18 2012
*
* Copyright (C) 2011 - 2012 by Eric Rowell
*
@@ -1945,6 +1945,7 @@ Kinetic.Stage.prototype = {
var go = Kinetic.GlobalObject;
var pos = this.getUserPosition();
var el = shape.eventListeners;
var that = this;
if(this.targetShape && shape._id === this.targetShape._id) {
this.targetFound = true;
@@ -1972,12 +1973,12 @@ Kinetic.Stage.prototype = {
if((!go.drag.moving) || !go.drag.node) {
shape._handleEvent('click', evt);
if(shape.inDoubleClickWindow) {
if(this.inDoubleClickWindow) {
shape._handleEvent('dblclick', evt);
}
shape.inDoubleClickWindow = true;
this.inDoubleClickWindow = true;
setTimeout(function() {
shape.inDoubleClickWindow = false;
that.inDoubleClickWindow = false;
}, this.dblClickWindow);
}
}
@@ -2005,23 +2006,25 @@ Kinetic.Stage.prototype = {
if((!go.drag.moving) || !go.drag.node) {
shape._handleEvent('tap', evt);
if(shape.inDoubleClickWindow) {
if(this.inDoubleClickWindow) {
shape._handleEvent('dbltap', evt);
}
shape.inDoubleClickWindow = true;
this.inDoubleClickWindow = true;
setTimeout(function() {
shape.inDoubleClickWindow = false;
that.inDoubleClickWindow = false;
}, this.dblClickWindow);
}
}
return true;
}
else if(!isDragging && this.touchMove) {
shape._handleEvent('touchmove', evt);
return true;
}
/*
* NOTE: these event handlers require target shape
* handling
*/
// handle onmouseover
else if(!isDragging && this._isNewTarget(shape, evt)) {
/*
@@ -2039,18 +2042,12 @@ Kinetic.Stage.prototype = {
this._setTarget(shape);
return true;
}
// handle mousemove and touchmove
else if(!isDragging && this.mouseMove) {
shape._handleEvent('mousemove', evt);
return true;
}
else if(!isDragging && this.touchMove) {
shape._handleEvent('touchmove', evt);
return true;
}
}
// handle mouseout condition
@@ -2191,7 +2188,7 @@ else if(!isDragging && this.touchMove) {
this.targetShape = undefined;
}
this.mousePos = undefined;
// end drag and drop
this._endDrag(evt);
},
@@ -2203,9 +2200,10 @@ else if(!isDragging && this.touchMove) {
var timeDiff = time - this.lastEventTime;
var tt = 1000 / throttle;
if(timeDiff >= tt) {
if(timeDiff >= tt || throttle > 200) {
this.mouseDown = false;
this.mouseUp = false;
this.mouseMove = true;
this._handleStageEvent(evt);
}
@@ -2229,7 +2227,7 @@ else if(!isDragging && this.touchMove) {
this.mouseMove = false;
this._handleStageEvent(evt);
this.clickStart = false;
// end drag and drop
this._endDrag(evt);
},
@@ -2252,7 +2250,7 @@ else if(!isDragging && this.touchMove) {
this.touchMove = false;
this._handleStageEvent(evt);
this.tapStart = false;
// end drag and drop
this._endDrag(evt);
},
@@ -2265,20 +2263,11 @@ else if(!isDragging && this.touchMove) {
var timeDiff = time - this.lastEventTime;
var tt = 1000 / throttle;
if(timeDiff >= tt) {
/*
* need a setTimeout here because iOS
* sometimes triggers touchStart and touchMove
* simultaenously which causes event detection issues.
* The timeout ensures that touchstart events
* are handled first followed by touchmove
*/
setTimeout(function() {
evt.preventDefault();
that.touchEnd = false;
that.touchMove = true;
that._handleStageEvent(evt);
}, 5);
if(timeDiff >= tt || throttle > 200) {
evt.preventDefault();
that.touchEnd = false;
that.touchMove = true;
that._handleStageEvent(evt);
}
// start drag and drop
@@ -2648,7 +2637,7 @@ Kinetic.Layer.prototype = {
var timeDiff = time - this.lastDrawTime;
var tt = 1000 / throttle;
if(timeDiff >= tt) {
if(timeDiff >= tt || throttle > 200) {
this._draw();
if(this.drawTimeout !== undefined) {

File diff suppressed because one or more lines are too long

View File

@@ -42,7 +42,7 @@ Kinetic.Layer.prototype = {
var timeDiff = time - this.lastDrawTime;
var tt = 1000 / throttle;
if(timeDiff >= tt) {
if(timeDiff >= tt || throttle > 200) {
this._draw();
if(this.drawTimeout !== undefined) {

View File

@@ -381,6 +381,7 @@ Kinetic.Stage.prototype = {
var go = Kinetic.GlobalObject;
var pos = this.getUserPosition();
var el = shape.eventListeners;
var that = this;
if(this.targetShape && shape._id === this.targetShape._id) {
this.targetFound = true;
@@ -408,12 +409,12 @@ Kinetic.Stage.prototype = {
if((!go.drag.moving) || !go.drag.node) {
shape._handleEvent('click', evt);
if(shape.inDoubleClickWindow) {
if(this.inDoubleClickWindow) {
shape._handleEvent('dblclick', evt);
}
shape.inDoubleClickWindow = true;
this.inDoubleClickWindow = true;
setTimeout(function() {
shape.inDoubleClickWindow = false;
that.inDoubleClickWindow = false;
}, this.dblClickWindow);
}
}
@@ -441,23 +442,25 @@ Kinetic.Stage.prototype = {
if((!go.drag.moving) || !go.drag.node) {
shape._handleEvent('tap', evt);
if(shape.inDoubleClickWindow) {
if(this.inDoubleClickWindow) {
shape._handleEvent('dbltap', evt);
}
shape.inDoubleClickWindow = true;
this.inDoubleClickWindow = true;
setTimeout(function() {
shape.inDoubleClickWindow = false;
that.inDoubleClickWindow = false;
}, this.dblClickWindow);
}
}
return true;
}
else if(!isDragging && this.touchMove) {
shape._handleEvent('touchmove', evt);
return true;
}
/*
* NOTE: these event handlers require target shape
* handling
*/
// handle onmouseover
else if(!isDragging && this._isNewTarget(shape, evt)) {
/*
@@ -475,18 +478,12 @@ Kinetic.Stage.prototype = {
this._setTarget(shape);
return true;
}
// handle mousemove and touchmove
else if(!isDragging && this.mouseMove) {
shape._handleEvent('mousemove', evt);
return true;
}
else if(!isDragging && this.touchMove) {
shape._handleEvent('touchmove', evt);
return true;
}
}
// handle mouseout condition
@@ -627,7 +624,7 @@ else if(!isDragging && this.touchMove) {
this.targetShape = undefined;
}
this.mousePos = undefined;
// end drag and drop
this._endDrag(evt);
},
@@ -639,9 +636,10 @@ else if(!isDragging && this.touchMove) {
var timeDiff = time - this.lastEventTime;
var tt = 1000 / throttle;
if(timeDiff >= tt) {
if(timeDiff >= tt || throttle > 200) {
this.mouseDown = false;
this.mouseUp = false;
this.mouseMove = true;
this._handleStageEvent(evt);
}
@@ -665,7 +663,7 @@ else if(!isDragging && this.touchMove) {
this.mouseMove = false;
this._handleStageEvent(evt);
this.clickStart = false;
// end drag and drop
this._endDrag(evt);
},
@@ -688,7 +686,7 @@ else if(!isDragging && this.touchMove) {
this.touchMove = false;
this._handleStageEvent(evt);
this.tapStart = false;
// end drag and drop
this._endDrag(evt);
},
@@ -701,20 +699,11 @@ else if(!isDragging && this.touchMove) {
var timeDiff = time - this.lastEventTime;
var tt = 1000 / throttle;
if(timeDiff >= tt) {
/*
* need a setTimeout here because iOS
* sometimes triggers touchStart and touchMove
* simultaenously which causes event detection issues.
* The timeout ensures that touchstart events
* are handled first followed by touchmove
*/
setTimeout(function() {
evt.preventDefault();
that.touchEnd = false;
that.touchMove = true;
that._handleStageEvent(evt);
}, 5);
if(timeDiff >= tt || throttle > 200) {
evt.preventDefault();
that.touchEnd = false;
that.touchMove = true;
that._handleStageEvent(evt);
}
// start drag and drop

View File

@@ -8,7 +8,7 @@ Test.prototype.tests = {
height: 200
});
var layer = new Kinetic.Layer();
var Ellipse = new Kinetic.Ellipse({
var circle = new Kinetic.Ellipse({
x: 380,
y: stage.getHeight() / 2,
radius: 70,
@@ -17,28 +17,28 @@ Test.prototype.tests = {
stroke: 'black'
});
Ellipse.draggable(true);
circle.draggable(true);
layer.add(Ellipse);
layer.add(circle);
stage.add(layer);
var dragStart = false;
var dragMove = false;
var dragEnd = false;
Ellipse.on('dragstart', function() {
circle.on('dragstart', function() {
dragStart = true;
});
Ellipse.on('dragstart', function() {
circle.on('dragstart', function() {
dragStart = true;
});
Ellipse.on('dragmove', function() {
circle.on('dragmove', function() {
dragMove = true;
});
Ellipse.on('dragend', function() {
circle.on('dragend', function() {
dragEnd = true;
});
@@ -82,15 +82,15 @@ Test.prototype.tests = {
}, 100);
});
},
'EVENTS - modify fill stroke and stroke width on hover with Ellipse': function(containerId) {
var urls = dataUrls['EVENTS - modify fill stroke and stroke width on hover with Ellipse'];
'EVENTS - modify fill stroke and stroke width on hover with circle': function(containerId) {
var urls = dataUrls['EVENTS - modify fill stroke and stroke width on hover with circle'];
var stage = new Kinetic.Stage({
container: containerId,
width: 578,
height: 200
});
var layer = new Kinetic.Layer();
var Ellipse = new Kinetic.Ellipse({
var circle = new Kinetic.Ellipse({
x: 380,
y: stage.getHeight() / 2,
radius: 70,
@@ -99,21 +99,21 @@ Test.prototype.tests = {
stroke: 'black'
});
Ellipse.on('mouseover', function() {
circle.on('mouseover', function() {
this.setFill('yellow');
this.setStroke('purple');
this.setStrokeWidth(20);
layer.draw();
});
Ellipse.on('mouseout', function() {
circle.on('mouseout', function() {
this.setFill('red');
this.setStroke('black');
this.setStrokeWidth(4);
layer.draw();
});
layer.add(Ellipse);
layer.add(circle);
stage.add(layer);
stage.toDataURL(function(startDataUrl) {
@@ -128,5 +128,277 @@ Test.prototype.tests = {
//test(urls[1] === endDataUrl, 'end data url is incorrect');
});
});
},
'EVENTS - mousedown mouseup mouseover mouseout mousemove click dblclick / touchstart touchend touchmove tap dbltap': function(containerId) {
var stage = new Kinetic.Stage({
container: containerId,
width: 578,
height: 200,
throttle: 9999
});
var layer = new Kinetic.Layer();
var circle = new Kinetic.Ellipse({
x: stage.getWidth() / 2,
y: stage.getHeight() / 2,
radius: 70,
fill: 'red',
stroke: 'black',
strokeWidth: 4
});
// desktop events
var mousedown = false;
var mouseup = false;
var click = false;
var dblclick = false;
var mouseover = false;
var mouseout = false;
var mousemove = false;
// mobile events
var touchstart = false;
var touchend = false;
var tap = false;
var touchmove = false;
var dbltap = false;
circle.on('mousedown', function() {
mousedown = true;
//log('mousedown');
});
circle.on('mouseup', function() {
mouseup = true;
//log('mouseup');
});
circle.on('mouseover', function() {
mouseover = true;
//log('mouseover');
});
circle.on('mouseout', function() {
mouseout = true;
//log('mousedown');
});
circle.on('mousemove', function() {
mousemove = true;
//log('mousemove');
});
circle.on('click', function() {
click = true;
//log('click');
});
circle.on('dblclick', function() {
dblclick = true;
//log('dblclick');
});
/*
* mobile
*/
circle.on('touchstart', function() {
touchstart = true;
//log('touchstart');
});
circle.on('touchend', function() {
touchend = true;
//log('touchend');
});
circle.on('touchmove', function() {
touchmove = true;
//log('touchmove');
});
circle.on('tap', function(evt) {
tap = true;
//log('tap');
});
circle.on('dbltap', function() {
dbltap = true;
//log('dbltap');
});
layer.add(circle);
stage.add(layer);
// move mouse to center of circle to trigger mouseover
stage._mousemove({
clientX: 290,
clientY: 100
});
test(mouseover, '1) mouseover should be true');
test(!mousemove, '1) mousemove should be false');
test(!mousedown, '1) mousedown should be false');
test(!mouseup, '1) mouseup should be false');
test(!click, '1) click should be false');
test(!dblclick, '1) dblclick should be false');
test(!mouseout, '1) mouseout should be false');
// move mouse again inside circle to trigger mousemove
stage._mousemove({
clientX: 290,
clientY: 100
});
test(mouseover, '2) mouseover should be true');
test(mousemove, '2) mousemove should be true');
test(!mousedown, '2) mousedown should be false');
test(!mouseup, '2) mouseup should be false');
test(!click, '2) click should be false');
test(!dblclick, '2) dblclick should be false');
test(!mouseout, '2) mouseout should be false');
// mousedown inside circle
stage._mousedown({
clientX: 290,
clientY: 100
});
test(mouseover, '3) mouseover should be true');
test(mousemove, '3) mousemove should be true');
test(mousedown, '3) mousedown should be true');
test(!mouseup, '3) mouseup should be false');
test(!click, '3) click should be false');
test(!dblclick, '3) dblclick should be false');
test(!mouseout, '3) mouseout should be false');
// mouseup inside circle
stage._mouseup({
clientX: 290,
clientY: 100
});
test(mouseover, '4) mouseover should be true');
test(mousemove, '4) mousemove should be true');
test(mousedown, '4) mousedown should be true');
test(mouseup, '4) mouseup should be true');
test(click, '4) click should be true');
test(!dblclick, '4) dblclick should be false');
test(!mouseout, '4) mouseout should be false');
// mousedown inside circle
stage._mousedown({
clientX: 290,
clientY: 100
});
test(mouseover, '5) mouseover should be true');
test(mousemove, '5) mousemove should be true');
test(mousedown, '5) mousedown should be true');
test(mouseup, '5) mouseup should be true');
test(click, '5) click should be true');
test(!dblclick, '5) dblclick should be false');
test(!mouseout, '5) mouseout should be false');
// mouseup inside circle to trigger double click
stage._mouseup({
clientX: 290,
clientY: 100
});
test(mouseover, '6) mouseover should be true');
test(mousemove, '6) mousemove should be true');
test(mousedown, '6) mousedown should be true');
test(mouseup, '6) mouseup should be true');
test(click, '6) click should be true');
test(dblclick, '6) dblclick should be true');
test(!mouseout, '6) mouseout should be false');
// move mouse outside of circle to trigger mouseout
stage._mouseout({
clientX: 0,
clientY: 100
});
test(mouseover, '7) mouseover should be true');
test(mousemove, '7) mousemove should be true');
test(mousedown, '7) mousedown should be true');
test(mouseup, '7) mouseup should be true');
test(click, '7) click should be true');
test(dblclick, '7) dblclick should be true');
test(mouseout, '7) mouseout should be true');
/*
* mobile tests
*/
// reset inDoubleClickWindow
stage.inDoubleClickWindow = false;
// touchstart circle
stage._touchstart({
clientX: 289,
clientY: 100,
preventDefault: function() {
}
});
test(touchstart, '8) touchstart should be true');
test(!touchmove, '8) touchmove should be false');
test(!touchend, '8) touchend should be false');
test(!tap, '8) tap should be false');
test(!dbltap, '8) dbltap should be false');
// touchend circle
stage._touchend({
clientX: 289,
clientY: 100,
preventDefault: function() {
}
});
test(touchstart, '9) touchstart should be true');
test(!touchmove, '9) touchmove should be false');
test(touchend, '9) touchend should be true');
test(tap, '9) tap should be true');
test(!dbltap, '9) dbltap should be false');
// touchstart circle
stage._touchstart({
clientX: 289,
clientY: 100,
preventDefault: function() {
}
});
test(touchstart, '10) touchstart should be true');
test(!touchmove, '10) touchmove should be false');
test(touchend, '10) touchend should be true');
test(tap, '10) tap should be true');
test(!dbltap, '10) dbltap should be false');
// touchend circle to triger dbltap
stage._touchend({
clientX: 289,
clientY: 100,
preventDefault: function() {
}
});
test(touchstart, '11) touchstart should be true');
test(!touchmove, '11) touchmove should be false');
test(touchend, '11) touchend should be true');
test(tap, '11) tap should be true');
test(dbltap, '11) dbltap should be true');
// touchmove circle
stage._touchmove({
clientX: 290,
clientY: 100,
preventDefault: function() {
}
});
test(touchstart, '12) touchstart should be true');
test(touchmove, '12) touchmove should be true');
test(touchend, '12) touchend should be true');
test(tap, '12) tap should be true');
test(dbltap, '12) dbltap should be true');
}
};

View File

@@ -1,4 +1,73 @@
Test.prototype.tests = {
'EVENTS - mousedown mouseup mouseover mouseout mousemove click dblclick / touchstart touchend touchmove tap dbltap': function(containerId) {
var stage = new Kinetic.Stage({
container: containerId,
width: 578,
height: 200
});
var layer = new Kinetic.Layer();
var circle = new Kinetic.Ellipse({
x: stage.getWidth() / 2,
y: stage.getHeight() / 2,
radius: 70,
fill: 'red',
stroke: 'black',
strokeWidth: 4
});
circle.on('mousedown', function() {
log('mousedown');
});
circle.on('mouseup', function() {
log('mouseup');
});
circle.on('mouseover', function() {
log('mouseover');
});
circle.on('mouseout', function() {
log('mouseout');
});
circle.on('mousemove', function() {
log('mousemove');
});
circle.on('click', function() {
log('click');
});
circle.on('dblclick', function() {
log('dblclick');
});
/*
* mobile
*/
circle.on('touchstart', function() {
log('touchstart');
});
circle.on('touchend', function() {
log('touchend');
});
circle.on('touchmove', function() {
log('touchmove');
});
circle.on('tap', function(evt) {
log('tap');
});
circle.on('dbltap', function() {
log('dbltap');
});
layer.add(circle);
stage.add(layer);
},
'TRANSITION - transition position and rotation': function(containerId) {
var stage = new Kinetic.Stage({
container: containerId,
@@ -176,75 +245,6 @@ Test.prototype.tests = {
layer.add(redBox);
stage.add(layer);
},
'EVENTS - mousedown mouseup mouseover mouseout mousemove click dblclick / touchstart touchend touchmove tap dbltap': function(containerId) {
var stage = new Kinetic.Stage({
container: containerId,
width: 578,
height: 200
});
var layer = new Kinetic.Layer();
var Ellipse = new Kinetic.Ellipse({
x: stage.getWidth() / 2,
y: stage.getHeight() / 2,
radius: 70,
fill: 'red',
stroke: 'black',
strokeWidth: 4
});
Ellipse.on('mousedown', function() {
log('mousedown');
});
Ellipse.on('mouseup', function() {
log('mouseup');
});
Ellipse.on('mouseover', function() {
log('mouseover');
});
Ellipse.on('mouseout', function() {
log('mouseout');
});
Ellipse.on('mousemove', function() {
log('mousemove');
});
Ellipse.on('click', function() {
log('click');
});
Ellipse.on('dblclick', function() {
log('dblclick');
});
/*
* mobile
*/
Ellipse.on('touchstart', function() {
log('touchstart');
});
Ellipse.on('touchend', function() {
log('touchend');
});
Ellipse.on('touchmove', function() {
log('touchmove');
});
Ellipse.on('tap', function(evt) {
log('tap');
});
Ellipse.on('dbltap', function() {
log('dbltap');
});
layer.add(Ellipse);
stage.add(layer);
},
'EVENTS - modify fill stroke and stroke width on hover with star': function(containerId) {
var stage = new Kinetic.Stage({
container: containerId,