Skip to content
This repository has been archived by the owner on Dec 21, 2018. It is now read-only.

Commit

Permalink
Revise canvas event processing and tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
jheer committed Jul 10, 2015
1 parent 1fc765b commit e0c5e7a
Show file tree
Hide file tree
Showing 14 changed files with 114 additions and 89 deletions.
75 changes: 47 additions & 28 deletions src/render/canvas/CanvasHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ var DOM = require('../../util/dom'),
function CanvasHandler() {
Handler.call(this);
this._down = null;
this._touch = null;
this._first = true;
}

var base = Handler.prototype;
Expand All @@ -25,6 +27,7 @@ prototype.initialize = function(el, pad, obj) {
});
});

this._rect = this._canvas.getBoundingClientRect();
return base.initialize.call(this, el, pad, obj);
};

Expand All @@ -46,6 +49,7 @@ prototype.events = [
'mouseup',
'mousemove',
'mouseout',
'mouseover',
'click',
'dblclick',
'wheel',
Expand All @@ -55,34 +59,29 @@ prototype.events = [
'touchend'
];

// to keep firefox happy
prototype.DOMMouseScroll = function(evt) {
this.fire('mousewheel', evt);
};

prototype.mousemove = function(evt) {
var pad = this._padding,
b = evt.target.getBoundingClientRect(),
x = evt.clientX - b.left,
y = evt.clientY - b.top,
a = this._active,
p = this.pick(this._scene, x, y, x-pad.left, y-pad.top);
var a = this._active,
p = this.pickEvent(evt);

if (p === a) {
this.fire('mousemove', evt);
if (evt.type === 'touchmove') this.fire('touchmove', evt);
return;
} else if (a) {
this.fire('mouseout', evt);
if (evt.type === 'touchend') this.fire('touchend', evt);
}
this._active = p;
if (p) {
this.fire('mouseover', evt);
if (evt.type === 'touchstart') this.fire('touchstart', evt);
// active item and picked item are the same
this.fire('mousemove', evt); // fire move
} else {
// active item and picked item are different
this.fire('mouseout', evt); // fire out for prior active item
this._active = p; // set new active item
this.fire('mouseover', evt); // fire over for new active item
this.fire('mousemove', evt); // fire move for new active item
}
};

prototype.mouseout = function(evt) {
if (this._active) {
this.fire('mouseout', evt);
this.fire('touchend', evt);
}
this.fire('mouseout', evt);
this._active = null;
};

Expand All @@ -98,20 +97,32 @@ prototype.click = function(evt) {
}
};

// to keep firefox happy
prototype.DOMMouseScroll = function(evt) {
this.fire('mousewheel', evt);
prototype.touchstart = function(evt) {
this._touch = this.pickEvent(evt.changedTouches[0]);

if (this._first) {
this._active = this._touch;
this._first = false;
}

this.fire('touchstart', evt, true);
};

prototype.touchmove = prototype.mousemove;
prototype.touchmove = function(evt) {
this.fire('touchmove', evt, true);
};

prototype.touchend = prototype.mouseout;
prototype.touchend = function(evt) {
this.fire('touchend', evt, true);
this._touch = null;
};

// fire an event
prototype.fire = function(type, evt) {
var a = this._active,
prototype.fire = function(type, evt, touch) {
var a = touch ? this._touch : this._active,
h = this._handlers[type], i, len;
if (h) {
evt.vegaType = type;
for (i=0, len=h.length; i<len; ++i) {
h[i].handler.call(this._obj, evt, a);
}
Expand Down Expand Up @@ -141,6 +152,14 @@ prototype.off = function(type, handler) {
return this;
};

prototype.pickEvent = function(evt) {
var pad = this._padding, x, y;
return this.pick(this._scene,
x = (evt.clientX - this._rect.left),
y = (evt.clientY - this._rect.top),
x - pad.left, y - pad.top);
};

// find the scenegraph item at the current mouse position
// x, y -- the absolute x, y mouse coordinates on the canvas element
// gx, gy -- the relative coordinates within the current group
Expand Down
7 changes: 1 addition & 6 deletions src/render/canvas/marks/arc.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,7 @@ function path(g, o) {
g.closePath();
}

function hit(g, o, x, y) {
path(g, o);
return g.isPointInPath(x, y);
}

module.exports = {
draw: util.drawAll(path),
pick: util.pick(hit)
pick: util.pickPath(path)
};
15 changes: 6 additions & 9 deletions src/render/canvas/marks/area.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,21 @@ function path(g, items) {
}

function pick(g, scene, x, y, gx, gy) {
if (!scene.items || !scene.items.length) return false;

var items = scene.items,
b = scene.bounds;

if (b && !b.contains(gx, gy)) return false;
if (!items || !items.length || b && !b.contains(gx, gy)) {
return null;
}

if (g.pixelratio != null && g.pixelratio !== 1) {
x *= g.pixelratio;
y *= g.pixelratio;
}
if (!hit(g, items, x, y)) return false;
return items[0];
return hit(g, items, x, y) ? items[0] : null;
}

function hit(g, s, x, y) {
path(g, s);
return g.isPointInPath(x, y);
}
var hit = util.testPath(path);

module.exports = {
draw: util.drawOne(path),
Expand Down
4 changes: 2 additions & 2 deletions src/render/canvas/marks/group.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ function hit(g, o) {

function pick(g, scene, x, y, gx, gy) {
if (scene.bounds && !scene.bounds.contains(gx, gy)) {
return false;
return null;
}
var items = scene.items || [],
subscene, group, hits, dx, dy, i, j;
Expand All @@ -81,7 +81,7 @@ function pick(g, scene, x, y, gx, gy) {
g.restore();
}

return scene.interactive !== false ? pickSelf(g, scene, x, y, gx, gy) : false;
return scene.interactive !== false ? pickSelf(g, scene, x, y, gx, gy) : null;
}

var pickSelf = util.pick(hit);
Expand Down
24 changes: 5 additions & 19 deletions src/render/canvas/marks/line.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,36 +9,22 @@ function path(g, items) {
render(g, p);
}

function stroke(g, items) {
var o = items[0],
lw = o.strokeWidth,
lc = o.strokeCap;
g.lineWidth = lw != null ? lw : 1;
g.lineCap = lc != null ? lc : 'butt';
path(g, items);
}

function pick(g, scene, x, y, gx, gy) {
if (!scene.items || !scene.items.length) return false;

var items = scene.items,
b = scene.bounds;

if (b && !b.contains(gx, gy)) return false;
if (!items || !items.length || b && !b.contains(gx, gy)) {
return null;
}

if (g.pixelratio != null && g.pixelratio !== 1) {
x *= g.pixelratio;
y *= g.pixelratio;
}
if (!hit(g, items, x, y)) return false;
return items[0];
return hit(g, items, x, y) ? items[0] : null;
}

function hit(g, s, x, y) {
if (!g.isPointInStroke) return false;
stroke(g, s);
return g.isPointInStroke(x, y);
}
var hit = util.testPath(path, false);

module.exports = {
draw: util.drawOne(path),
Expand Down
6 changes: 1 addition & 5 deletions src/render/canvas/marks/path.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,7 @@ function path(g, o) {
render(g, p, o.x, o.y);
}

function hit(g, o, x, y) {
return path(g, o) ? false : g.isPointInPath(x, y);
}

module.exports = {
draw: util.drawAll(path),
pick: util.pick(hit)
pick: util.pickPath(path)
};
7 changes: 1 addition & 6 deletions src/render/canvas/marks/symbol.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,7 @@ function path(g, o) {
g.closePath();
}

function hit(g, o, x, y) {
path(g, o);
return g.isPointInPath(x, y);
}

module.exports = {
draw: util.drawAll(path),
pick: util.pick(hit)
pick: util.pickPath(path)
};
29 changes: 27 additions & 2 deletions src/render/canvas/marks/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ function pick(test) {
if (!test) test = function() { return true; };

return function(g, scene, x, y, gx, gy) {
if (!scene.items.length) return false;
if (!scene.items.length) return null;

var o, b, i;

Expand All @@ -53,10 +53,33 @@ function pick(test) {
// if in bounding box, perform more careful test
if (test(g, o, x, y, gx, gy)) return o;
}
return false;
return null;
};
}

function testPath(path, fill) {
return function(g, o, x, y) {
var item = Array.isArray(o) ? o[0] : o,
stroke = item.stroke && g.isPointInStroke, lw, lc;
fill = (fill == null) ? item.fill : fill;

if (stroke) {
lw = item.strokeWidth;
lc = item.strokeCap;
g.lineWidth = lw != null ? lw : 1;
g.lineCap = lc != null ? lc : 'butt';
}

return path(g, o) ? false :
(fill && g.isPointInPath(x, y)) ||
(stroke && g.isPointInStroke(x, y));
};
}

function pickPath(path) {
return pick(testPath(path));
}

function fill(g, o, opacity) {
opacity *= (o.fillOpacity==null ? 1 : o.fillOpacity);
if (opacity > 0) {
Expand Down Expand Up @@ -113,6 +136,8 @@ module.exports = {
drawOne: drawOne,
drawAll: drawAll,
pick: pick,
pickPath: pickPath,
testPath: testPath,
stroke: stroke,
fill: fill,
color: color,
Expand Down
1 change: 1 addition & 0 deletions src/render/svg/SVGHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ prototype.listener = function(handler) {
return function(evt) {
var target = evt.target,
item = target.__data__;
evt.vegaType = evt.type;
item = Array.isArray(item) ? item[0] : item;
handler.call(that._obj, evt, item);
};
Expand Down
19 changes: 13 additions & 6 deletions test/handler-canvas.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,12 @@ function renderAsync(scene, w, h, callback) {
function event(name, x, y) {
var evt = jsdom.createEvent('MouseEvents');
evt.initEvent(name, false, true);
evt.clientX = x || 50;
evt.clientY = y || 150;
evt.clientX = x || 0;
evt.clientY = y || 0;
evt.changedTouches = [{
clientX: x || 0,
clientY: y || 0
}];
return evt;
}

Expand All @@ -75,16 +79,19 @@ describe('canvas handler', function() {
handler.events.forEach(function(name) {
canvas.dispatchEvent(event(name));
});

handler.DOMMouseScroll(event('mousewheel'));

canvas.dispatchEvent(event('mousemove', 0, 0));
canvas.dispatchEvent(event('mousemove', 50, 150));
canvas.dispatchEvent(event('mousedown', 50, 150));
canvas.dispatchEvent(event('mouseup', 50, 150));
canvas.dispatchEvent(event('click', 50, 150));
canvas.dispatchEvent(event('mousemove', 50, 151));
canvas.dispatchEvent(event('mousemove', 50, 1));
canvas.dispatchEvent(event('mouseout', 1, 1));

assert.equal(count, handler.events.length + 4);
// 9 events above + 4 triggered (mouseover, mouseout)
assert.equal(count, handler.events.length + 13);

handler.off('mousemove', {});
assert.equal(handler.handlers().length, handler.events.length);
Expand All @@ -109,7 +116,7 @@ describe('canvas handler', function() {
it('should pick arc mark', function() {
var mark = marks.arc;
var handler = new Handler().initialize(render(mark, 500, 500));
assert.ok(handler.pick(mark, 260, 240, 260, 240));
assert.ok(handler.pick(mark, 270, 260, 270, 260));
assert.notOk(handler.pick(mark, 248, 250, 248, 250));
assert.notOk(handler.pick(mark, 800, 800, 800, 800));
});
Expand Down Expand Up @@ -234,7 +241,7 @@ describe('canvas handler', function() {
for (i=0; i<types.length; ++i) {
scene.marktype = types[i];
var handler = new Handler().initialize(render(scene, 500, 500));
assert.isFalse(handler.pick(scene, 0, 0, 0, 0));
assert.isNull(handler.pick(scene, 0, 0, 0, 0));
}
});

Expand Down

0 comments on commit e0c5e7a

Please sign in to comment.