UNPKG

leaflet

Version:

JavaScript library for mobile-friendly interactive maps

236 lines (192 loc) 6.72 kB
/* * @class SVG * @inherits Renderer * @aka L.SVG * * Allows vector layers to be displayed with [SVG](https://developer.mozilla.org/docs/Web/SVG). * Inherits `Renderer`. * * Due to [technical limitations](http://caniuse.com/#search=svg), SVG is not * available in all web browsers, notably Android 2.x and 3.x. * * Although SVG is not available on IE7 and IE8, these browsers support * [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language) * (a now deprecated technology), and the SVG renderer will fall back to VML in * this case. * * @example * * Use SVG by default for all paths in the map: * * ```js * var map = L.map('map', { * renderer: L.svg() * }); * ``` * * Use a SVG renderer with extra padding for specific vector geometries: * * ```js * var map = L.map('map'); * var myRenderer = L.svg({ padding: 0.5 }); * var line = L.polyline( coordinates, { renderer: myRenderer } ); * var circle = L.circle( center, { renderer: myRenderer } ); * ``` */ L.SVG = L.Renderer.extend({ getEvents: function () { var events = L.Renderer.prototype.getEvents.call(this); events.zoomstart = this._onZoomStart; return events; }, _initContainer: function () { this._container = L.SVG.create('svg'); // makes it possible to click through svg root; we'll reset it back in individual paths this._container.setAttribute('pointer-events', 'none'); this._rootGroup = L.SVG.create('g'); this._container.appendChild(this._rootGroup); }, _onZoomStart: function () { // Drag-then-pinch interactions might mess up the center and zoom. // In this case, the easiest way to prevent this is re-do the renderer // bounds and padding when the zooming starts. this._update(); }, _update: function () { if (this._map._animatingZoom && this._bounds) { return; } L.Renderer.prototype._update.call(this); var b = this._bounds, size = b.getSize(), container = this._container; // set size of svg-container if changed if (!this._svgSize || !this._svgSize.equals(size)) { this._svgSize = size; container.setAttribute('width', size.x); container.setAttribute('height', size.y); } // movement: update container viewBox so that we don't have to change coordinates of individual layers L.DomUtil.setPosition(container, b.min); container.setAttribute('viewBox', [b.min.x, b.min.y, size.x, size.y].join(' ')); this.fire('update'); }, // methods below are called by vector layers implementations _initPath: function (layer) { var path = layer._path = L.SVG.create('path'); // @namespace Path // @option className: String = null // Custom class name set on an element. Only for SVG renderer. if (layer.options.className) { L.DomUtil.addClass(path, layer.options.className); } if (layer.options.interactive) { L.DomUtil.addClass(path, 'leaflet-interactive'); } this._updateStyle(layer); }, _addPath: function (layer) { this._rootGroup.appendChild(layer._path); layer.addInteractiveTarget(layer._path); }, _removePath: function (layer) { L.DomUtil.remove(layer._path); layer.removeInteractiveTarget(layer._path); }, _updatePath: function (layer) { layer._project(); layer._update(); }, _updateStyle: function (layer) { var path = layer._path, options = layer.options; if (!path) { return; } if (options.stroke) { path.setAttribute('stroke', options.color); path.setAttribute('stroke-opacity', options.opacity); path.setAttribute('stroke-width', options.weight); path.setAttribute('stroke-linecap', options.lineCap); path.setAttribute('stroke-linejoin', options.lineJoin); if (options.dashArray) { path.setAttribute('stroke-dasharray', options.dashArray); } else { path.removeAttribute('stroke-dasharray'); } if (options.dashOffset) { path.setAttribute('stroke-dashoffset', options.dashOffset); } else { path.removeAttribute('stroke-dashoffset'); } } else { path.setAttribute('stroke', 'none'); } if (options.fill) { path.setAttribute('fill', options.fillColor || options.color); path.setAttribute('fill-opacity', options.fillOpacity); path.setAttribute('fill-rule', options.fillRule || 'evenodd'); } else { path.setAttribute('fill', 'none'); } }, _updatePoly: function (layer, closed) { this._setPath(layer, L.SVG.pointsToPath(layer._parts, closed)); }, _updateCircle: function (layer) { var p = layer._point, r = layer._radius, r2 = layer._radiusY || r, arc = 'a' + r + ',' + r2 + ' 0 1,0 '; // drawing a circle with two half-arcs var d = layer._empty() ? 'M0 0' : 'M' + (p.x - r) + ',' + p.y + arc + (r * 2) + ',0 ' + arc + (-r * 2) + ',0 '; this._setPath(layer, d); }, _setPath: function (layer, path) { layer._path.setAttribute('d', path); }, // SVG does not have the concept of zIndex so we resort to changing the DOM order of elements _bringToFront: function (layer) { L.DomUtil.toFront(layer._path); }, _bringToBack: function (layer) { L.DomUtil.toBack(layer._path); } }); // @namespace SVG; @section // There are several static functions which can be called without instantiating L.SVG: L.extend(L.SVG, { // @function create(name: String): SVGElement // Returns a instance of [SVGElement](https://developer.mozilla.org/docs/Web/API/SVGElement), // corresponding to the class name passed. For example, using 'line' will return // an instance of [SVGLineElement](https://developer.mozilla.org/docs/Web/API/SVGLineElement). create: function (name) { return document.createElementNS('http://www.w3.org/2000/svg', name); }, // @function pointsToPath(rings: Point[], closed: Boolean): String // Generates a SVG path string for multiple rings, with each ring turning // into "M..L..L.." instructions pointsToPath: function (rings, closed) { var str = '', i, j, len, len2, points, p; for (i = 0, len = rings.length; i < len; i++) { points = rings[i]; for (j = 0, len2 = points.length; j < len2; j++) { p = points[j]; str += (j ? 'L' : 'M') + p.x + ' ' + p.y; } // closes the ring for polygons; "x" is VML syntax str += closed ? (L.Browser.svg ? 'z' : 'x') : ''; } // SVG complains about empty path strings return str || 'M0 0'; } }); // @namespace Browser; @property svg: Boolean // `true` when the browser supports [SVG](https://developer.mozilla.org/docs/Web/SVG). L.Browser.svg = !!(document.createElementNS && L.SVG.create('svg').createSVGRect); // @namespace SVG // @factory L.svg(options?: Renderer options) // Creates a SVG renderer with the given options. L.svg = function (options) { return L.Browser.svg || L.Browser.vml ? new L.SVG(options) : null; };