UNPKG

leaflet.pattern

Version:

Provides the ability to use SVG patterns as backgrounds for Leaflet Paths.

502 lines (407 loc) 11.4 kB
/* Leaflet.pattern, Provides tools to set the backgrounds of vector shapes in Leaflet to be patterns. https://github.com/teastman/Leaflet.pattern (c) 2015, Tyler Eastman */ (function (window, document, undefined) {/* * L.Pattern is the base class for fill patterns for leaflet Paths. */ L.Pattern = L.Class.extend({ includes: [L.Mixin.Events], options: { x: 0, y: 0, width: 8, height: 8, patternUnits: 'userSpaceOnUse', patternContentUnits: 'userSpaceOnUse' // angle: <0 - 360> // patternTransform: <transform-list> }, _addShapes: L.Util.falseFn, _update: L.Util.falseFn, initialize: function (options) { this._shapes = {}; L.setOptions(this, options); }, onAdd: function (map) { this._map = map.target ? map.target : map; this._map._initDefRoot(); // Create the DOM Object for the pattern. this._initDom(); // Any shapes that were added before this was added to the map need to have their onAdd called. for (var i in this._shapes) { this._shapes[i].onAdd(this); } // Call any children that want to add their own shapes. this._addShapes(); // Add the DOM Object to the DOM Tree this._addDom(); this.redraw(); if (this.getEvents) { this._map.on(this.getEvents(), this); } this.fire('add'); this._map.fire('patternadd', {pattern: this}); }, onRemove: function () { this._removeDom(); }, redraw: function () { if (this._map) { this._update(); for (var i in this._shapes) { this._shapes[i].redraw(); } } return this; }, setStyle: function (style) { L.setOptions(this, style); if (this._map) { this._updateStyle(); this.redraw(); } return this; }, addTo: function (map) { map.addPattern(this); return this; }, remove: function () { return this.removeFrom(this._map); }, removeFrom: function (map) { if (map) { map.removePattern(this); } return this; } }); L.Map.addInitHook(function () { this._patterns = {}; }); L.Map.include({ addPattern: function (pattern) { var id = L.stamp(pattern); if (this._patterns[id]) { return pattern; } this._patterns[id] = pattern; this.whenReady(pattern.onAdd, pattern); return this; }, removePattern: function (pattern) { var id = L.stamp(pattern); if (!this._patterns[id]) { return this; } if (this._loaded) { pattern.onRemove(this); } if (pattern.getEvents) { this.off(pattern.getEvents(), pattern); } delete this._patterns[id]; if (this._loaded) { this.fire('patternremove', {pattern: pattern}); pattern.fire('remove'); } pattern._map = null; return this; }, hasPattern: function (pattern) { return !!pattern && (L.stamp(pattern) in this._patterns); } }); L.Pattern.SVG_NS = 'http://www.w3.org/2000/svg'; L.Pattern = L.Pattern.extend({ _createElement: function (name) { return document.createElementNS(L.Pattern.SVG_NS, name); }, _initDom: function () { this._dom = this._createElement('pattern'); if (this.options.className) { L.DomUtil.addClass(this._dom, this.options.className); } this._updateStyle(); }, _addDom: function () { this._map._defRoot.appendChild(this._dom); }, _removeDom: function () { L.DomUtil.remove(this._dom); }, _updateStyle: function () { var dom = this._dom, options = this.options; if (!dom) { return; } dom.setAttribute('id', L.stamp(this)); dom.setAttribute('x', options.x); dom.setAttribute('y', options.y); dom.setAttribute('width', options.width); dom.setAttribute('height', options.height); dom.setAttribute('patternUnits', options.patternUnits); dom.setAttribute('patternContentUnits', options.patternContentUnits); if (options.patternTransform || options.angle) { var transform = options.patternTransform ? options.patternTransform + " " : ""; transform += options.angle ? "rotate(" + options.angle + ") " : ""; dom.setAttribute('patternTransform', transform); } else { dom.removeAttribute('patternTransform'); } for (var i in this._shapes) { this._shapes[i]._updateStyle(); } } }); L.Map.include({ _initDefRoot: function () { if (!this._defRoot) { if (typeof this.getRenderer === 'function') { var renderer = this.getRenderer(this); this._defRoot = L.Pattern.prototype._createElement('defs'); renderer._container.appendChild(this._defRoot); } else { if (!this._pathRoot) { this._initPathRoot(); } this._defRoot = L.Pattern.prototype._createElement('defs'); this._pathRoot.appendChild(this._defRoot); } } } }); if (L.SVG) { L.SVG.include({ _superUpdateStyle: L.SVG.prototype._updateStyle, _updateStyle: function (layer) { this._superUpdateStyle(layer); if (layer.options.fill && layer.options.fillPattern) { layer._path.setAttribute('fill', 'url(#' + L.stamp(layer.options.fillPattern) + ")"); } } }); } else { L.Path.include({ _superUpdateStyle: L.Path.prototype._updateStyle, _updateStyle: function () { this._superUpdateStyle(); if (this.options.fill && this.options.fillPattern) { this._path.setAttribute('fill', 'url(#' + L.stamp(this.options.fillPattern) + ")"); } } }); } /* * L.StripePattern is an implementation of Pattern that creates stripes. */ L.StripePattern = L.Pattern.extend({ options: { weight: 4, spaceWeight: 4, color: '#000000', spaceColor: '#ffffff', opacity: 1.0, spaceOpacity: 0.0 }, _addShapes: function () { this._stripe = new L.PatternPath({ stroke: true, weight: this.options.weight, color: this.options.color, opacity: this.options.opacity }); this._space = new L.PatternPath({ stroke: true, weight: this.options.spaceWeight, color: this.options.spaceColor, opacity: this.options.spaceOpacity }); this.addShape(this._stripe); this.addShape(this._space); this._update(); }, _update: function () { this._stripe.options.d = 'M0 ' + this._stripe.options.weight / 2 + ' H ' + this.options.width; this._space.options.d = 'M0 ' + (this._stripe.options.weight + this._space.options.weight / 2) + ' H ' + this.options.width; }, setStyle: L.Pattern.prototype.setStyle }); L.stripePattern = function (options) { return new L.StripePattern(options); }; /* * L.PatternShape is the base class that is used to define the shapes in Patterns. */ L.PatternShape = L.Class.extend({ options: { stroke: true, color: '#3388ff', weight: 3, opacity: 1, lineCap: 'round', lineJoin: 'round', // dashArray: null // dashOffset: null // fill: false // fillColor: same as color by default fillOpacity: 0.2, fillRule: 'evenodd', // fillPattern: L.Pattern }, initialize: function (options) { L.setOptions(this, options); }, // Called when the parent Pattern get's added to the map, // or when added to a Pattern that is already on the map. onAdd: function (pattern) { this._pattern = pattern; if (this._pattern._dom) { this._initDom(); // This function is implemented by it's children. this._addDom(); } }, addTo: function (pattern) { pattern.addShape(this); return this; }, redraw: function () { if (this._pattern) { this._updateShape(); // This function is implemented by it's children. } return this; }, setStyle: function (style) { L.setOptions(this, style); if (this._pattern) { this._updateStyle(); } return this; }, setShape: function (shape) { this.options = L.extend({}, this.options, shape); this._updateShape(); }, }); L.Pattern.include({ addShape: function (shape) { var id = L.stamp(shape); if (this._shapes[id]) { return shape; } this._shapes[id] = shape; shape.onAdd(this); } }); L.PatternShape.SVG_NS = 'http://www.w3.org/2000/svg'; L.PatternShape = L.PatternShape.extend({ _createElement: function (name) { return document.createElementNS(L.PatternShape.SVG_NS, name); }, _initDom: L.Util.falseFn, _updateShape: L.Util.falseFn, _initDomElement: function (type) { this._dom = this._createElement(type); if (this.options.className) { L.DomUtil.addClass(this._dom, this.options.className); } this._updateStyle(); }, _addDom: function () { this._pattern._dom.appendChild(this._dom); }, _updateStyle: function () { var dom = this._dom, options = this.options; if (!dom) { return; } if (options.stroke) { dom.setAttribute('stroke', options.color); dom.setAttribute('stroke-opacity', options.opacity); dom.setAttribute('stroke-width', options.weight); dom.setAttribute('stroke-linecap', options.lineCap); dom.setAttribute('stroke-linejoin', options.lineJoin); if (options.dashArray) { dom.setAttribute('stroke-dasharray', options.dashArray); } else { dom.removeAttribute('stroke-dasharray'); } if (options.dashOffset) { dom.setAttribute('stroke-dashoffset', options.dashOffset); } else { dom.removeAttribute('stroke-dashoffset'); } } else { dom.setAttribute('stroke', 'none'); } if (options.fill) { if (options.fillPattern) { dom.setAttribute('fill', 'url(#' + L.stamp(options.fillPattern) + ")"); } else { dom.setAttribute('fill', options.fillColor || options.color); } dom.setAttribute('fill-opacity', options.fillOpacity); dom.setAttribute('fill-rule', options.fillRule || 'evenodd'); } else { dom.setAttribute('fill', 'none'); } dom.setAttribute('pointer-events', options.pointerEvents || (options.interactive ? 'visiblePainted' : 'none')); } }); /* * L.PatternPath is the implementation of PatternShape for adding Paths */ L.PatternPath = L.PatternShape.extend({ // options: { // d: <svg path code> // }, _initDom: function () { this._initDomElement('path'); }, _updateShape: function () { if (!this._dom) { return; } this._dom.setAttribute('d', this.options.d); } }); /* * L.PatternCircle is the implementation of PatternShape for adding Circles */ L.PatternCircle = L.PatternShape.extend({ options: { x: 0, y: 0, radius: 0 }, _initDom: function () { this._initDomElement('circle'); }, _updateShape: function () { if (!this._dom) { return; } this._dom.setAttribute('cx', this.options.x); this._dom.setAttribute('cy', this.options.y); this._dom.setAttribute('r', this.options.radius); } }); /* * L.PatternRect is the implementation of PatternShape for adding Rectangles */ L.PatternRect = L.PatternShape.extend({ options: { x: 0, y: 0, width: 10, height: 10, // rx: x radius for rounded corners // ry: y radius for rounded corners }, _initDom: function () { this._initDomElement('rect'); }, _updateShape: function () { if (!this._dom) { return; } this._dom.setAttribute('x', this.options.x); this._dom.setAttribute('y', this.options.y); this._dom.setAttribute('width', this.options.width); this._dom.setAttribute('height', this.options.height); if (this.options.rx) { this._dom.setAttribute('rx', this.options.rx); } if (this.options.ry) { this._dom.setAttribute('ry', this.options.ry); } } }); }(window, document));