Canvas events fixes (fix #3915)

- fix fireEvent called for each layer containing event point
- fix layer not removed from this._layer at remove
- fix L.DomEvent.stop(e) not honoured for canvas because events
  are both listenned on canvas container and map container
This commit is contained in:
Yohan Boniface 2015-10-09 18:25:59 +02:00
parent c82aaa4158
commit 0b0989f9f1
5 changed files with 92 additions and 26 deletions

View File

@ -77,6 +77,7 @@
<script type="text/javascript" src="suites/layer/vector/PolygonSpec.js"></script> <script type="text/javascript" src="suites/layer/vector/PolygonSpec.js"></script>
<script type="text/javascript" src="suites/layer/vector/PolylineSpec.js"></script> <script type="text/javascript" src="suites/layer/vector/PolylineSpec.js"></script>
<script type="text/javascript" src="suites/layer/vector/PolylineGeometrySpec.js"></script> <script type="text/javascript" src="suites/layer/vector/PolylineGeometrySpec.js"></script>
<script type="text/javascript" src="suites/layer/vector/CanvasSpec.js"></script>
<!-- /map --> <!-- /map -->
<script type="text/javascript" src="suites/map/MapSpec.js"></script> <script type="text/javascript" src="suites/map/MapSpec.js"></script>

View File

@ -0,0 +1,78 @@
describe('Canvas', function () {
var c, map, p2ll, latLngs;
before(function () {
c = document.createElement('div');
c.style.width = '400px';
c.style.height = '400px';
c.style.position = 'absolute';
c.style.top = '0';
c.style.left = '0';
document.body.appendChild(c);
map = new L.Map(c, {preferCanvas: true, zoomControl: false});
map.setView([0, 0], 6);
p2ll = function (x, y) {
return map.layerPointToLatLng([x, y]);
};
latLngs = [p2ll(0, 0), p2ll(0, 100), p2ll(100, 100), p2ll(100, 0)];
});
after(function () {
document.body.removeChild(c);
});
describe("#events", function () {
var layer;
beforeEach(function () {
layer = L.polygon(latLngs).addTo(map);
});
afterEach(function () {
layer.remove();
});
it("should fire event when layer contains mouse", function () {
var spy = sinon.spy();
layer.on('click', spy);
happen.at('click', 50, 50); // Click on the layer.
expect(spy.callCount).to.eql(1);
happen.at('click', 150, 150); // Click outside layer.
expect(spy.callCount).to.eql(1);
layer.off("click", spy);
});
it("DOM events propagate from canvas polygon to map", function () {
var spy = sinon.spy();
map.on("click", spy);
happen.at('click', 50, 50);
expect(spy.callCount).to.eql(1);
map.off("click", spy);
});
it("DOM events fired on canvas polygon can be cancelled before being caught by the map", function () {
var mapSpy = sinon.spy();
var layerSpy = sinon.spy();
map.on("click", mapSpy);
layer.on("click", L.DomEvent.stopPropagation).on("click", layerSpy);
happen.at('click', 50, 50);
expect(layerSpy.callCount).to.eql(1);
expect(mapSpy.callCount).to.eql(0);
map.off("click", mapSpy);
layer.off("click", L.DomEvent.stopPropagation).off("click", layerSpy);
});
it("DOM events fired on canvas polygon are propagated only once to the map even when two layers contains the event", function () {
var spy = sinon.spy();
var layer2 = L.polygon(latLngs).addTo(map);
map.on("click", spy);
happen.at('click', 50, 50);
expect(spy.callCount).to.eql(1);
layer2.remove();
map.off("click", spy);
});
});
});

View File

@ -644,14 +644,6 @@ describe("Map", function () {
expect(spy.calledOnce).to.be.ok(); expect(spy.calledOnce).to.be.ok();
}); });
it("DOM events propagate from canvas polygon to map", function () {
var spy = sinon.spy();
map.on("mousemove", spy);
var layer = new L.Polygon([[1, 2], [3, 4], [5, 6]], {rendered: L.canvas()}).addTo(map);
happen.mousemove(layer._path);
expect(spy.calledOnce).to.be.ok();
});
it("DOM events propagate from marker to map", function () { it("DOM events propagate from marker to map", function () {
var spy = sinon.spy(); var spy = sinon.spy();
map.on("mousemove", spy); map.on("mousemove", spy);
@ -682,17 +674,6 @@ describe("Map", function () {
expect(mapSpy.called).not.to.be.ok(); expect(mapSpy.called).not.to.be.ok();
}); });
it("DOM events fired on canvas polygon can be cancelled before being caught by the map", function () {
var mapSpy = sinon.spy();
var layerSpy = sinon.spy();
map.on("mousemove", mapSpy);
var layer = new L.Polygon([[1, 2], [3, 4], [5, 6]], {rendered: L.canvas()}).addTo(map);
layer.on("mousemove", L.DomEvent.stopPropagation).on("mousemove", layerSpy);
happen.mousemove(layer._path);
expect(layerSpy.calledOnce).to.be.ok();
expect(mapSpy.called).not.to.be.ok();
});
it("mouseout is forwarded if fired on the original target", function () { it("mouseout is forwarded if fired on the original target", function () {
var mapSpy = sinon.spy(), var mapSpy = sinon.spy(),
layerSpy = sinon.spy(), layerSpy = sinon.spy(),

View File

@ -61,6 +61,7 @@ L.Canvas = L.Renderer.extend({
_removePath: function (layer) { _removePath: function (layer) {
layer._removed = true; layer._removed = true;
delete this._layers[L.stamp(layer)];
this._requestRedraw(layer); this._requestRedraw(layer);
}, },
@ -201,14 +202,17 @@ L.Canvas = L.Renderer.extend({
// so we emulate that by calculating what's under the mouse on mousemove/click manually // so we emulate that by calculating what's under the mouse on mousemove/click manually
_onClick: function (e) { _onClick: function (e) {
var point = this._map.mouseEventToLayerPoint(e); var point = this._map.mouseEventToLayerPoint(e), layers = [];
for (var id in this._layers) { for (var id in this._layers) {
if (this._layers[id]._containsPoint(point)) { if (this._layers[id]._containsPoint(point)) {
L.DomEvent._fakeStop(e); L.DomEvent._fakeStop(e);
this._fireEvent(this._layers[id], e); layers.push(this._layers[id]);
} }
} }
if (layers.length) {
this._fireEvent(layers, e);
}
}, },
_onMouseMove: function (e) { _onMouseMove: function (e) {
@ -225,7 +229,7 @@ L.Canvas = L.Renderer.extend({
if (layer && (e.type === 'mouseout' || !layer._containsPoint(point))) { if (layer && (e.type === 'mouseout' || !layer._containsPoint(point))) {
// if we're leaving the layer, fire mouseout // if we're leaving the layer, fire mouseout
L.DomUtil.removeClass(this._container, 'leaflet-interactive'); L.DomUtil.removeClass(this._container, 'leaflet-interactive');
this._fireEvent(layer, e, 'mouseout'); this._fireEvent([layer], e, 'mouseout');
this._hoveredLayer = null; this._hoveredLayer = null;
} }
}, },
@ -237,19 +241,19 @@ L.Canvas = L.Renderer.extend({
layer = this._drawnLayers[id]; layer = this._drawnLayers[id];
if (layer.options.interactive && layer._containsPoint(point)) { if (layer.options.interactive && layer._containsPoint(point)) {
L.DomUtil.addClass(this._container, 'leaflet-interactive'); // change cursor L.DomUtil.addClass(this._container, 'leaflet-interactive'); // change cursor
this._fireEvent(layer, e, 'mouseover'); this._fireEvent([layer], e, 'mouseover');
this._hoveredLayer = layer; this._hoveredLayer = layer;
break; break;
} }
} }
} }
if (this._hoveredLayer) { if (this._hoveredLayer) {
this._fireEvent(this._hoveredLayer, e); this._fireEvent([this._hoveredLayer], e);
} }
}, },
_fireEvent: function (layer, e, type) { _fireEvent: function (layers, e, type) {
this._map._fireDOMEvent(e, type || e.type, [layer]); this._map._fireDOMEvent(e, type || e.type, layers);
}, },
// TODO _bringToFront & _bringToBack, pretty tricky // TODO _bringToFront & _bringToBack, pretty tricky

View File

@ -688,6 +688,8 @@ L.Map = L.Evented.extend({
_fireDOMEvent: function (e, type, targets) { _fireDOMEvent: function (e, type, targets) {
if (e._stopped) { return; }
targets = (targets || []).concat(this._findEventTargets(e, type)); targets = (targets || []).concat(this._findEventTargets(e, type));
if (!targets.length) { return; } if (!targets.length) { return; }