implement Canvas events and SVG click-through
This commit is contained in:
parent
41a576b930
commit
6f9d05fc40
@ -155,14 +155,14 @@ var deps = {
|
||||
'layer/vector2/Renderer.js',
|
||||
'layer/vector2/SVG.js',
|
||||
'layer/vector2/SVG.VML.js',
|
||||
'layer/vector2/Canvas.js',
|
||||
'layer/vector2/Path.js',
|
||||
'layer/vector2/Path.Popup.js',
|
||||
'geometry/LineUtil.js',
|
||||
'layer/vector2/Polyline.js',
|
||||
'geometry/PolyUtil.js',
|
||||
'layer/vector2/Polygon.js',
|
||||
'layer/vector2/Rectangle.js'
|
||||
'layer/vector2/Rectangle.js',
|
||||
'layer/vector2/Canvas.js'
|
||||
],
|
||||
desc: 'New vector layers implementation.'
|
||||
},
|
||||
|
@ -26,7 +26,7 @@
|
||||
map.addLayer(L.marker(latlngs[0]));
|
||||
map.addLayer(L.marker(latlngs[len - 1]));
|
||||
|
||||
var path = L.polygon([[latlngs, [[50.5, 30.5], [50.5, 40], [40, 40]]], [[20, 0], [20, 40], [0, 40]]]).addTo(map);
|
||||
var path = L.polygon([[latlngs, [[50.5, 30.5], [50.5, 40], [40, 40]]], [[20, 0], [20, 40], [0, 40]]], {renderer: L.canvas()}).addTo(map);
|
||||
var poly = L.polyline([[[60, 30], [60, 50], [40, 50]], [[20, 50], [20, 70], [0, 70]]], {color: 'red'}).addTo(map);
|
||||
|
||||
map.fitBounds(path);
|
||||
|
@ -1,9 +0,0 @@
|
||||
/*
|
||||
* CircleMarker canvas specific drawing parts.
|
||||
*/
|
||||
|
||||
L.CircleMarker.include(!L.Path.CANVAS ? {} : {
|
||||
_updateStyle: function () {
|
||||
L.Path.prototype._updateStyle.call(this);
|
||||
}
|
||||
});
|
@ -1,192 +0,0 @@
|
||||
/*
|
||||
* Vector rendering for all browsers that support canvas.
|
||||
*/
|
||||
|
||||
L.Browser.canvas = (function () {
|
||||
return !!document.createElement('canvas').getContext;
|
||||
}());
|
||||
|
||||
L.Path = (L.Path.SVG && !window.L_PREFER_CANVAS) || !L.Browser.canvas ? L.Path : L.Path.extend({
|
||||
statics: {
|
||||
//CLIP_PADDING: 0.02, // not sure if there's a need to set it to a small value
|
||||
CANVAS: true,
|
||||
SVG: false
|
||||
},
|
||||
|
||||
redraw: function () {
|
||||
if (this._map) {
|
||||
this.projectLatlngs();
|
||||
this._requestUpdate();
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
setStyle: function (style) {
|
||||
L.setOptions(this, style);
|
||||
|
||||
if (this._map) {
|
||||
this._updateStyle();
|
||||
this._requestUpdate();
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
onRemove: function (map) {
|
||||
map
|
||||
.off('viewreset', this.projectLatlngs, this)
|
||||
.off('moveend', this._updatePath, this);
|
||||
|
||||
if (this.options.clickable) {
|
||||
this._map.off('click', this._onClick, this);
|
||||
this._map.off('mousemove', this._onMouseMove, this);
|
||||
}
|
||||
|
||||
this._requestUpdate();
|
||||
},
|
||||
|
||||
_requestUpdate: function () {
|
||||
if (this._map && !L.Path._updateRequest) {
|
||||
L.Path._updateRequest = L.Util.requestAnimFrame(this._fireMapMoveEnd, this._map);
|
||||
}
|
||||
},
|
||||
|
||||
_fireMapMoveEnd: function () {
|
||||
L.Path._updateRequest = null;
|
||||
this.fire('moveend');
|
||||
},
|
||||
|
||||
_initElements: function () {
|
||||
this._map._initPathRoot();
|
||||
this._ctx = this._map._canvasCtx;
|
||||
},
|
||||
|
||||
_updateStyle: function () {
|
||||
var options = this.options;
|
||||
|
||||
if (options.stroke) {
|
||||
this._ctx.lineWidth = options.weight;
|
||||
this._ctx.strokeStyle = options.color;
|
||||
}
|
||||
if (options.fill) {
|
||||
this._ctx.fillStyle = options.fillColor || options.color;
|
||||
}
|
||||
},
|
||||
|
||||
_drawPath: function () {
|
||||
var i, j, len, len2, point, drawMethod;
|
||||
|
||||
this._ctx.beginPath();
|
||||
|
||||
for (i = 0, len = this._parts.length; i < len; i++) {
|
||||
for (j = 0, len2 = this._parts[i].length; j < len2; j++) {
|
||||
point = this._parts[i][j];
|
||||
drawMethod = (j === 0 ? 'move' : 'line') + 'To';
|
||||
|
||||
this._ctx[drawMethod](point.x, point.y);
|
||||
}
|
||||
// TODO refactor ugly hack
|
||||
if (this instanceof L.Polygon) {
|
||||
this._ctx.closePath();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_checkIfEmpty: function () {
|
||||
return !this._parts.length;
|
||||
},
|
||||
|
||||
_updatePath: function () {
|
||||
if (this._checkIfEmpty()) { return; }
|
||||
|
||||
var ctx = this._ctx,
|
||||
options = this.options;
|
||||
|
||||
this._drawPath();
|
||||
ctx.save();
|
||||
this._updateStyle();
|
||||
|
||||
if (options.fill) {
|
||||
ctx.globalAlpha = options.fillOpacity;
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
if (options.stroke) {
|
||||
ctx.globalAlpha = options.opacity;
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
ctx.restore();
|
||||
|
||||
// TODO optimization: 1 fill/stroke for all features with equal style instead of 1 for each feature
|
||||
},
|
||||
|
||||
_initEvents: function () {
|
||||
// TODO dblclick
|
||||
this._map.on('mousemove', this._onMouseMove, this);
|
||||
this._map.on('click', this._onClick, this);
|
||||
},
|
||||
|
||||
_onClick: function (e) {
|
||||
if (this._containsPoint(e.layerPoint)) {
|
||||
this.fire('click', e);
|
||||
}
|
||||
},
|
||||
|
||||
_onMouseMove: function (e) {
|
||||
if (!this._map || this._map._animatingZoom) { return; }
|
||||
|
||||
// TODO don't do on each move
|
||||
if (this._containsPoint(e.layerPoint)) {
|
||||
this._ctx.canvas.style.cursor = 'pointer';
|
||||
this._mouseInside = true;
|
||||
this.fire('mouseover', e);
|
||||
|
||||
} else if (this._mouseInside) {
|
||||
this._ctx.canvas.style.cursor = '';
|
||||
this._mouseInside = false;
|
||||
this.fire('mouseout', e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
L.Map.include((L.Path.SVG && !window.L_PREFER_CANVAS) || !L.Browser.canvas ? {} : {
|
||||
_initPathRoot: function () {
|
||||
var root = this._pathRoot,
|
||||
ctx;
|
||||
|
||||
if (!root) {
|
||||
root = this._pathRoot = document.createElement('canvas');
|
||||
root.style.position = 'absolute';
|
||||
ctx = this._canvasCtx = root.getContext('2d');
|
||||
|
||||
ctx.lineCap = 'round';
|
||||
ctx.lineJoin = 'round';
|
||||
|
||||
this._panes.overlayPane.appendChild(root);
|
||||
|
||||
if (this._zoomAnimated) {
|
||||
this._pathRoot.className = 'leaflet-zoom-animated';
|
||||
this.on('zoomanim', this._animatePathZoom);
|
||||
this.on('zoomend', this._endPathZoom);
|
||||
}
|
||||
this.on('moveend', this._updateCanvasViewport);
|
||||
this._updateCanvasViewport();
|
||||
}
|
||||
},
|
||||
|
||||
_updateCanvasViewport: function () {
|
||||
// don't redraw while zooming. See _updateSvgViewport for more details
|
||||
if (this._pathZooming) { return; }
|
||||
this._updatePathViewport();
|
||||
|
||||
var vp = this._pathViewport,
|
||||
size = vp.getSize(),
|
||||
root = this._pathRoot;
|
||||
|
||||
//TODO check if this works properly on mobile webkit
|
||||
L.DomUtil.setPosition(root, vp.min);
|
||||
root.width = size.x;
|
||||
root.height = size.y;
|
||||
root.getContext('2d').translate(-vp.min.x, -vp.min.y);
|
||||
}
|
||||
});
|
@ -1,37 +0,0 @@
|
||||
/*
|
||||
* Extends L.Polygon to be able to manually detect clicks on Canvas-rendered polygons.
|
||||
*/
|
||||
|
||||
L.Polygon.include(!L.Path.CANVAS ? {} : {
|
||||
_containsPoint: function (p) {
|
||||
var inside = false,
|
||||
part, p1, p2,
|
||||
i, j, k,
|
||||
len, len2;
|
||||
|
||||
// TODO optimization: check if within bounds first
|
||||
|
||||
if (L.Polyline.prototype._containsPoint.call(this, p, true)) {
|
||||
// click on polygon border
|
||||
return true;
|
||||
}
|
||||
|
||||
// ray casting algorithm for detecting if point is in polygon
|
||||
|
||||
for (i = 0, len = this._parts.length; i < len; i++) {
|
||||
part = this._parts[i];
|
||||
|
||||
for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
|
||||
p1 = part[j];
|
||||
p2 = part[k];
|
||||
|
||||
if (((p1.y > p.y) !== (p2.y > p.y)) &&
|
||||
(p.x < (p2.x - p1.x) * (p.y - p1.y) / (p2.y - p1.y) + p1.x)) {
|
||||
inside = !inside;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return inside;
|
||||
}
|
||||
});
|
@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Extends L.Polyline to be able to manually detect clicks on Canvas-rendered polylines.
|
||||
*/
|
||||
|
||||
L.Polyline.include(!L.Path.CANVAS ? {} : {
|
||||
_containsPoint: function (p, closed) {
|
||||
var i, j, k, len, len2, dist, part,
|
||||
w = this.options.weight / 2;
|
||||
|
||||
if (L.Browser.touch) {
|
||||
w += 10; // polyline click tolerance on touch devices
|
||||
}
|
||||
|
||||
for (i = 0, len = this._parts.length; i < len; i++) {
|
||||
part = this._parts[i];
|
||||
for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
|
||||
if (!closed && (j === 0)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
dist = L.LineUtil.pointToSegmentDistance(p, part[k], part[j]);
|
||||
|
||||
if (dist <= w) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
@ -40,13 +40,21 @@ L.Canvas = L.Renderer.extend({
|
||||
this.on('redraw', layer._updatePath, layer);
|
||||
|
||||
if (layer.options.clickable) {
|
||||
this._initEvents(layer);
|
||||
L.DomEvent
|
||||
.on(this._container, 'mousemove', this._onMouseMove, layer)
|
||||
.on(this._container, 'click', this._onClick, layer);
|
||||
}
|
||||
},
|
||||
|
||||
_addPath: L.Util.falseFn,
|
||||
|
||||
_removePath: function (layer) {
|
||||
if (layer.options.clickable) {
|
||||
L.DomEvent
|
||||
.off(this._container, 'mousemove', this._onMouseMove, layer)
|
||||
.off(this._container, 'click', this._onClick, layer);
|
||||
}
|
||||
|
||||
this.off('redraw', layer._updatePath, layer);
|
||||
this._requestRedraw();
|
||||
},
|
||||
@ -109,17 +117,33 @@ L.Canvas = L.Renderer.extend({
|
||||
// TODO optimization: 1 fill/stroke for all features with equal style instead of 1 for each feature
|
||||
},
|
||||
|
||||
// _bringToFront: function (layer) {
|
||||
// // TODO
|
||||
// },
|
||||
_onClick: function (e) {
|
||||
console.log(e);
|
||||
var point = this._map.mouseEventToLayerPoint(e);
|
||||
if (this._containsPoint(point)) {
|
||||
this._onMouseClick(e);
|
||||
}
|
||||
},
|
||||
|
||||
// _bringToBack: function (layer) {
|
||||
// // TODO
|
||||
// },
|
||||
_onMouseMove: function (e) {
|
||||
if (!this._map || this._map._animatingZoom) { return; }
|
||||
|
||||
// _initEvents: function (layer) {
|
||||
// // TODO
|
||||
// }
|
||||
var point = this._map.mouseEventToLayerPoint(e);
|
||||
|
||||
// TODO don't do on each move
|
||||
if (this._containsPoint(point)) {
|
||||
this._renderer._container.style.cursor = 'pointer';
|
||||
this._mouseInside = true;
|
||||
this._fireMouseEvent(e, 'mouseover');
|
||||
|
||||
} else if (this._mouseInside) {
|
||||
this._renderer._container.style.cursor = '';
|
||||
this._mouseInside = false;
|
||||
this._fireMouseEvent(e, 'mouseout');
|
||||
}
|
||||
}
|
||||
|
||||
// TODO _bringToFront & _bringToBack
|
||||
});
|
||||
|
||||
L.Browser.canvas = (function () {
|
||||
@ -129,3 +153,57 @@ L.Browser.canvas = (function () {
|
||||
L.canvas = function () {
|
||||
return new L.Canvas();
|
||||
};
|
||||
|
||||
|
||||
L.Polyline.prototype._containsPoint = function (p, closed) {
|
||||
var i, j, k, len, len2, part,
|
||||
w = (this.options.stroke ? this.options.weight / 2 : 0) + (L.Browser.touch ? 10 : 0);
|
||||
|
||||
for (i = 0, len = this._parts.length; i < len; i++) {
|
||||
part = this._parts[i];
|
||||
|
||||
for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
|
||||
if (!closed && (j === 0)) { continue; }
|
||||
|
||||
if (L.LineUtil.pointToSegmentDistance(p, part[k], part[j]) <= w) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
L.Polygon.prototype._containsPoint = function (p) {
|
||||
var inside = false,
|
||||
part, p1, p2, i, j, k, len, len2;
|
||||
|
||||
// TODO optimization: check if within bounds first
|
||||
|
||||
// click on polygon border
|
||||
if (L.Polyline.prototype._containsPoint.call(this, p, true)) { return true; }
|
||||
|
||||
// ray casting algorithm for detecting if point is in polygon
|
||||
for (i = 0, len = this._parts.length; i < len; i++) {
|
||||
part = this._parts[i];
|
||||
|
||||
for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
|
||||
p1 = part[j];
|
||||
p2 = part[k];
|
||||
|
||||
if (((p1.y > p.y) !== (p2.y > p.y)) && (p.x < (p2.x - p1.x) * (p.y - p1.y) / (p2.y - p1.y) + p1.x)) {
|
||||
inside = !inside;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return inside;
|
||||
};
|
||||
|
||||
/*
|
||||
L.Circle.prototype._containsPoint = function (p) {
|
||||
var center = this._point,
|
||||
w2 = this.options.stroke ? this.options.weight / 2 : 0;
|
||||
|
||||
return (p.distanceTo(center) <= this._radius + w2);
|
||||
};
|
||||
*/
|
||||
|
@ -82,25 +82,27 @@ L.Path = L.Layer.extend({
|
||||
this._fireMouseEvent(e);
|
||||
},
|
||||
|
||||
_fireMouseEvent: function (e) {
|
||||
if (!this.hasEventListeners(e.type)) { return; }
|
||||
_fireMouseEvent: function (e, type) {
|
||||
type = type || e.type;
|
||||
|
||||
if (!this.hasEventListeners(type)) { return; }
|
||||
|
||||
var map = this._map,
|
||||
containerPoint = map.mouseEventToContainerPoint(e),
|
||||
layerPoint = map.containerPointToLayerPoint(containerPoint),
|
||||
latlng = map.layerPointToLatLng(layerPoint);
|
||||
|
||||
this.fire(e.type, {
|
||||
this.fire(type, {
|
||||
latlng: latlng,
|
||||
layerPoint: layerPoint,
|
||||
containerPoint: containerPoint,
|
||||
originalEvent: e
|
||||
});
|
||||
|
||||
if (e.type === 'contextmenu') {
|
||||
if (type === 'contextmenu') {
|
||||
L.DomEvent.preventDefault(e);
|
||||
}
|
||||
if (e.type !== 'mousemove') {
|
||||
if (type !== 'mousemove') {
|
||||
L.DomEvent.stopPropagation(e);
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ L.SVG = L.Renderer.extend({
|
||||
|
||||
onAdd: function () {
|
||||
var container = this._container = L.SVG.create('svg');
|
||||
container.setAttribute('pointer-events', 'none');
|
||||
|
||||
if (this._zoomAnimated) {
|
||||
L.DomUtil.addClass(container, 'leaflet-zoom-animated');
|
||||
@ -94,11 +95,7 @@ L.SVG = L.Renderer.extend({
|
||||
path.setAttribute('fill', 'none');
|
||||
}
|
||||
|
||||
if (options.pointerEvents) {
|
||||
path.setAttribute('pointer-events', options.pointerEvents);
|
||||
} else if (!options.clickable) {
|
||||
path.setAttribute('pointer-events', 'none');
|
||||
}
|
||||
path.setAttribute('pointer-events', options.pointerEvents || (options.clickable ? 'auto' : 'none'));
|
||||
},
|
||||
|
||||
_updatePoly: function (layer, closed) {
|
||||
|
Loading…
Reference in New Issue
Block a user