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/Renderer.js',
|
||||||
'layer/vector2/SVG.js',
|
'layer/vector2/SVG.js',
|
||||||
'layer/vector2/SVG.VML.js',
|
'layer/vector2/SVG.VML.js',
|
||||||
'layer/vector2/Canvas.js',
|
|
||||||
'layer/vector2/Path.js',
|
'layer/vector2/Path.js',
|
||||||
'layer/vector2/Path.Popup.js',
|
'layer/vector2/Path.Popup.js',
|
||||||
'geometry/LineUtil.js',
|
'geometry/LineUtil.js',
|
||||||
'layer/vector2/Polyline.js',
|
'layer/vector2/Polyline.js',
|
||||||
'geometry/PolyUtil.js',
|
'geometry/PolyUtil.js',
|
||||||
'layer/vector2/Polygon.js',
|
'layer/vector2/Polygon.js',
|
||||||
'layer/vector2/Rectangle.js'
|
'layer/vector2/Rectangle.js',
|
||||||
|
'layer/vector2/Canvas.js'
|
||||||
],
|
],
|
||||||
desc: 'New vector layers implementation.'
|
desc: 'New vector layers implementation.'
|
||||||
},
|
},
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
map.addLayer(L.marker(latlngs[0]));
|
map.addLayer(L.marker(latlngs[0]));
|
||||||
map.addLayer(L.marker(latlngs[len - 1]));
|
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);
|
var poly = L.polyline([[[60, 30], [60, 50], [40, 50]], [[20, 50], [20, 70], [0, 70]]], {color: 'red'}).addTo(map);
|
||||||
|
|
||||||
map.fitBounds(path);
|
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);
|
this.on('redraw', layer._updatePath, layer);
|
||||||
|
|
||||||
if (layer.options.clickable) {
|
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,
|
_addPath: L.Util.falseFn,
|
||||||
|
|
||||||
_removePath: function (layer) {
|
_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.off('redraw', layer._updatePath, layer);
|
||||||
this._requestRedraw();
|
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
|
// TODO optimization: 1 fill/stroke for all features with equal style instead of 1 for each feature
|
||||||
},
|
},
|
||||||
|
|
||||||
// _bringToFront: function (layer) {
|
_onClick: function (e) {
|
||||||
// // TODO
|
console.log(e);
|
||||||
// },
|
var point = this._map.mouseEventToLayerPoint(e);
|
||||||
|
if (this._containsPoint(point)) {
|
||||||
|
this._onMouseClick(e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// _bringToBack: function (layer) {
|
_onMouseMove: function (e) {
|
||||||
// // TODO
|
if (!this._map || this._map._animatingZoom) { return; }
|
||||||
// },
|
|
||||||
|
|
||||||
// _initEvents: function (layer) {
|
var point = this._map.mouseEventToLayerPoint(e);
|
||||||
// // TODO
|
|
||||||
// }
|
// 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 () {
|
L.Browser.canvas = (function () {
|
||||||
@ -129,3 +153,57 @@ L.Browser.canvas = (function () {
|
|||||||
L.canvas = function () {
|
L.canvas = function () {
|
||||||
return new L.Canvas();
|
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);
|
this._fireMouseEvent(e);
|
||||||
},
|
},
|
||||||
|
|
||||||
_fireMouseEvent: function (e) {
|
_fireMouseEvent: function (e, type) {
|
||||||
if (!this.hasEventListeners(e.type)) { return; }
|
type = type || e.type;
|
||||||
|
|
||||||
|
if (!this.hasEventListeners(type)) { return; }
|
||||||
|
|
||||||
var map = this._map,
|
var map = this._map,
|
||||||
containerPoint = map.mouseEventToContainerPoint(e),
|
containerPoint = map.mouseEventToContainerPoint(e),
|
||||||
layerPoint = map.containerPointToLayerPoint(containerPoint),
|
layerPoint = map.containerPointToLayerPoint(containerPoint),
|
||||||
latlng = map.layerPointToLatLng(layerPoint);
|
latlng = map.layerPointToLatLng(layerPoint);
|
||||||
|
|
||||||
this.fire(e.type, {
|
this.fire(type, {
|
||||||
latlng: latlng,
|
latlng: latlng,
|
||||||
layerPoint: layerPoint,
|
layerPoint: layerPoint,
|
||||||
containerPoint: containerPoint,
|
containerPoint: containerPoint,
|
||||||
originalEvent: e
|
originalEvent: e
|
||||||
});
|
});
|
||||||
|
|
||||||
if (e.type === 'contextmenu') {
|
if (type === 'contextmenu') {
|
||||||
L.DomEvent.preventDefault(e);
|
L.DomEvent.preventDefault(e);
|
||||||
}
|
}
|
||||||
if (e.type !== 'mousemove') {
|
if (type !== 'mousemove') {
|
||||||
L.DomEvent.stopPropagation(e);
|
L.DomEvent.stopPropagation(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ L.SVG = L.Renderer.extend({
|
|||||||
|
|
||||||
onAdd: function () {
|
onAdd: function () {
|
||||||
var container = this._container = L.SVG.create('svg');
|
var container = this._container = L.SVG.create('svg');
|
||||||
|
container.setAttribute('pointer-events', 'none');
|
||||||
|
|
||||||
if (this._zoomAnimated) {
|
if (this._zoomAnimated) {
|
||||||
L.DomUtil.addClass(container, 'leaflet-zoom-animated');
|
L.DomUtil.addClass(container, 'leaflet-zoom-animated');
|
||||||
@ -94,11 +95,7 @@ L.SVG = L.Renderer.extend({
|
|||||||
path.setAttribute('fill', 'none');
|
path.setAttribute('fill', 'none');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.pointerEvents) {
|
path.setAttribute('pointer-events', options.pointerEvents || (options.clickable ? 'auto' : 'none'));
|
||||||
path.setAttribute('pointer-events', options.pointerEvents);
|
|
||||||
} else if (!options.clickable) {
|
|
||||||
path.setAttribute('pointer-events', 'none');
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_updatePoly: function (layer, closed) {
|
_updatePoly: function (layer, closed) {
|
||||||
|
Loading…
Reference in New Issue
Block a user