/
githubmirror
/
Leaflet
Обзор
Документация
Войти
/
githubmirror
/
Leaflet
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/layer/vector/SVG.js
211 строк
6 KB
tomasmusil
Deduplicate code between `Circle` and `CircleMarker` (#10072)
10 апр 2026, 23:04
Не верифицирован
10 апр 2026, 23:04
7c233fb
Код
Авторство
О чём код?
import {Renderer} from './Renderer.js'; import * as DomUtil from '../../dom/DomUtil.js'; import {splitWords, stamp} from '../../core/Util.js'; /* * @class SVG * @inherits Renderer * * Allows vector layers to be displayed with [SVG](https://developer.mozilla.org/docs/Web/SVG). * Inherits `Renderer`. * * @example * * Use SVG by default for all paths in the map: * * ```js * const map = new LeafletMap('map', { * renderer: new SVG() * }); * ``` * * Use a SVG renderer with extra padding for specific vector geometries: * * ```js * const map = new LeafletMap('map'); * const myRenderer = new SVG({ padding: 0.5 }); * const line = new Polyline( coordinates, { renderer: myRenderer } ); * const circle = new Circle( center, { renderer: myRenderer, radius: 100 } ); * ``` */ // @namespace SVG // @constructor SVG(options?: Renderer options) // Creates a SVG renderer with the given options. export class SVG extends Renderer { _initContainer() { this._container = 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 = SVG.create('g'); this._container.appendChild(this._rootGroup); } _destroyContainer() { super._destroyContainer(); delete this._rootGroup; delete this._svgSize; } _resizeContainer() { const size = super._resizeContainer(); // set size of svg-container if changed if (!this._svgSize || !this._svgSize.equals(size)) { this._svgSize = size; this._container.setAttribute('width', size.x); this._container.setAttribute('height', size.y); } // Reset the <SVG>'s viewBox as per _update, but skip redrawing paths. // This keeps paths visually aligned when resizing. const b = this._bounds; if (b) { this._container.setAttribute('viewBox', [b.min.x, b.min.y, size.x, size.y].join(' ')); } } _update() { if (this._map._animatingZoom && this._bounds) { return; } const b = this._bounds, size = b.getSize(); // movement: update container viewBox so that we don't have to change coordinates of individual layers this._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(layer) { const path = layer._path = SVG.create('path'); // @namespace Path // @option className: String = null // Custom class name set on an element. Only for SVG renderer. if (layer.options.className) { path.classList.add(...splitWords(layer.options.className)); } if (layer.options.interactive) { path.classList.add('leaflet-interactive'); } this._updateStyle(layer); this._layers[stamp(layer)] = layer; } _addPath(layer) { if (!this._rootGroup) { this._initContainer(); } this._rootGroup.appendChild(layer._path); layer.addInteractiveTarget(layer._path); } _removePath(layer) { layer._path.remove(); layer.removeInteractiveTarget(layer._path); delete this._layers[stamp(layer)]; } _updatePath(layer) { layer._project(); layer._update(); } _updateStyle(layer) { const 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(layer, closed) { this._setPath(layer, SVG.pointsToPath(layer._parts, closed)); } _updateCircle(layer) { const p = layer._point, r = Math.max(Math.round(layer._pxRadius), 1), r2 = Math.max(Math.round(layer._pxRadiusY), 1) || r, arc = `a${r},${r2} 0 1,0 `; // drawing a circle with two half-arcs const d = layer._empty() ? 'M0 0' : `M${p.x - r},${p.y }${arc}${r * 2},0 ${ arc}${-r * 2},0 `; this._setPath(layer, d); } _setPath(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(layer) { DomUtil.toFront(layer._path); } _bringToBack(layer) { DomUtil.toBack(layer._path); } // @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). static create(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 static pointsToPath(rings, closed) { const str = rings.flatMap(points => [ ...points.map((p, j) => `${(j ? 'L' : 'M') + p.x} ${p.y}`), // closes the ring for polygons closed ? 'z' : '' ]).join(''); // SVG complains about empty path strings return str || 'M0 0'; } }