Create marker labels lazily when showLabels is false
This commit is contained in:
parent
02c705de36
commit
a7fa366635
@ -47,8 +47,14 @@ export class GenericIcon extends DivIcon {
|
|||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
options: GenericIconOptions;
|
options: GenericIconOptions;
|
||||||
_image?: HTMLImageElement;
|
|
||||||
_label?: HTMLSpanElement;
|
private _image?: HTMLImageElement;
|
||||||
|
private _label?: HTMLSpanElement;
|
||||||
|
private _container?: HTMLDivElement;
|
||||||
|
private _labelCreated: boolean = false;
|
||||||
|
private _onHover: EventListener = () => {
|
||||||
|
this.createLabel();
|
||||||
|
};
|
||||||
|
|
||||||
constructor(options: GenericIconOptions) {
|
constructor(options: GenericIconOptions) {
|
||||||
super(Object.assign(GenericIcon.defaultOptions, options));
|
super(Object.assign(GenericIcon.defaultOptions, options));
|
||||||
@ -64,14 +70,41 @@ export class GenericIcon extends DivIcon {
|
|||||||
size = point(this.options.iconSize as PointExpression);
|
size = point(this.options.iconSize as PointExpression);
|
||||||
|
|
||||||
this._image = markerIcon.cloneNode(false) as HTMLImageElement;
|
this._image = markerIcon.cloneNode(false) as HTMLImageElement;
|
||||||
this._label = markerLabel.cloneNode(false) as HTMLSpanElement;
|
|
||||||
|
|
||||||
const sizeClass = [size.x, size.y].join('x');
|
|
||||||
|
|
||||||
this._image.width = size.x;
|
this._image.width = size.x;
|
||||||
this._image.height = size.y;
|
this._image.height = size.y;
|
||||||
this._image.src = url;
|
this._image.src = url;
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
Icon.prototype._setIconStyles.call(this, div, 'icon');
|
||||||
|
|
||||||
|
div.appendChild(this._image);
|
||||||
|
div.classList.add('marker');
|
||||||
|
|
||||||
|
if(this.options.className) {
|
||||||
|
div.classList.add(this.options.className);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Create label lazily on hover
|
||||||
|
this._image.addEventListener('mouseover', this._onHover);
|
||||||
|
|
||||||
|
this._container = div;
|
||||||
|
|
||||||
|
return div;
|
||||||
|
}
|
||||||
|
|
||||||
|
createLabel() {
|
||||||
|
if(!this._container || this._labelCreated) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._image?.removeEventListener('mouseover', this._onHover);
|
||||||
|
|
||||||
|
const size = point(this.options.iconSize as PointExpression),
|
||||||
|
sizeClass = [size.x, size.y].join('x');
|
||||||
|
|
||||||
|
this._label = markerLabel.cloneNode(false) as HTMLSpanElement;
|
||||||
|
|
||||||
this._label.classList.add(/*'markerName_' + set.id,*/ `marker__label--${sizeClass}`);
|
this._label.classList.add(/*'markerName_' + set.id,*/ `marker__label--${sizeClass}`);
|
||||||
|
|
||||||
if (this.options.isHtml) {
|
if (this.options.isHtml) {
|
||||||
@ -80,18 +113,8 @@ export class GenericIcon extends DivIcon {
|
|||||||
this._label.textContent = this.options.label;
|
this._label.textContent = this.options.label;
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ts-ignore
|
this._container!.appendChild(this._label);
|
||||||
Icon.prototype._setIconStyles.call(this, div, 'icon');
|
this._labelCreated = true;
|
||||||
|
|
||||||
div.appendChild(this._image);
|
|
||||||
div.appendChild(this._label);
|
|
||||||
div.classList.add('marker');
|
|
||||||
|
|
||||||
if(this.options.className) {
|
|
||||||
div.classList.add(this.options.className);
|
|
||||||
}
|
|
||||||
|
|
||||||
return div;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
update(options: GenericIconOptions) {
|
update(options: GenericIconOptions) {
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {Layer, Map as LeafletMap, LayerGroup, LayerOptions, Util, Marker, Path} from "leaflet";
|
import {Layer, Map as LeafletMap, LayerGroup, LayerOptions, Util, Marker, Path} from "leaflet";
|
||||||
|
import {GenericMarker} from "@/leaflet/marker/GenericMarker";
|
||||||
|
|
||||||
export interface LiveAtlasLayerGroupOptions extends LayerOptions {
|
export interface LiveAtlasLayerGroupOptions extends LayerOptions {
|
||||||
id: string; //Added to the name of layer group panes
|
id: string; //Added to the name of layer group panes
|
||||||
@ -29,11 +30,11 @@ export interface LiveAtlasLayerGroupOptions extends LayerOptions {
|
|||||||
export default class LiveAtlasLayerGroup extends LayerGroup {
|
export default class LiveAtlasLayerGroup extends LayerGroup {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
options: LiveAtlasLayerGroupOptions;
|
options: LiveAtlasLayerGroupOptions;
|
||||||
_zoomLimitedLayers: Set<Layer>; //Layers which are zoom limited and should be checked on zoom
|
private _zoomLimitedLayers: Set<Layer>; //Layers which are zoom limited and should be checked on zoom
|
||||||
_layers: any;
|
_layers: any;
|
||||||
_markerPane?: HTMLElement;
|
_markerPane?: HTMLElement;
|
||||||
|
|
||||||
_zoomEndCallback = () => this._updateLayerVisibility();
|
private _zoomEndCallback = () => this._updateLayerVisibility();
|
||||||
|
|
||||||
constructor(options: LiveAtlasLayerGroupOptions) {
|
constructor(options: LiveAtlasLayerGroupOptions) {
|
||||||
super([], options);
|
super([], options);
|
||||||
@ -80,7 +81,7 @@ export default class LiveAtlasLayerGroup extends LayerGroup {
|
|||||||
layer.options.pane = `vectors`;
|
layer.options.pane = `vectors`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const zoomLimited = this._isLayerZoomLimited(layer);
|
const zoomLimited = LiveAtlasLayerGroup._isLayerZoomLimited(layer);
|
||||||
|
|
||||||
if (zoomLimited) {
|
if (zoomLimited) {
|
||||||
this._zoomLimitedLayers.add(layer);
|
this._zoomLimitedLayers.add(layer);
|
||||||
@ -89,11 +90,11 @@ export default class LiveAtlasLayerGroup extends LayerGroup {
|
|||||||
if (this._map) {
|
if (this._map) {
|
||||||
//If layer is zoom limited, only add to map if it should be visible
|
//If layer is zoom limited, only add to map if it should be visible
|
||||||
if (zoomLimited) {
|
if (zoomLimited) {
|
||||||
if (this._isLayerVisible(layer, this._map.getZoom())) {
|
if (LiveAtlasLayerGroup._isLayerVisible(layer, this._map.getZoom())) {
|
||||||
this._map.addLayer(layer);
|
this._addToMap(layer);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this._map.addLayer(layer);
|
this._addToMap(layer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,7 +107,19 @@ export default class LiveAtlasLayerGroup extends LayerGroup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
update(options: LiveAtlasLayerGroupOptions) {
|
update(options: LiveAtlasLayerGroupOptions) {
|
||||||
this.options.showLabels = options.showLabels;
|
if(this.options.showLabels !== options.showLabels) {
|
||||||
|
//Create labels if they are now always visible
|
||||||
|
//TODO: This will be slow when many markers exist. Is it worth doing?
|
||||||
|
if(options.showLabels) {
|
||||||
|
this.eachLayer((layer) => {
|
||||||
|
if(layer instanceof GenericMarker) {
|
||||||
|
(layer as GenericMarker).createLabel();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.options.showLabels = options.showLabels;
|
||||||
|
}
|
||||||
|
|
||||||
if(this._markerPane) {
|
if(this._markerPane) {
|
||||||
this._markerPane.classList.toggle('leaflet-pane--show-labels', options.showLabels);
|
this._markerPane.classList.toggle('leaflet-pane--show-labels', options.showLabels);
|
||||||
@ -128,7 +141,7 @@ export default class LiveAtlasLayerGroup extends LayerGroup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_updateLayerVisibility(onAdd?: boolean) {
|
private _updateLayerVisibility(onAdd?: boolean) {
|
||||||
if(!this._map) {
|
if(!this._map) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -142,34 +155,34 @@ export default class LiveAtlasLayerGroup extends LayerGroup {
|
|||||||
this.eachLayer((layer) => {
|
this.eachLayer((layer) => {
|
||||||
//Per marker zoom limits take precedence, if present
|
//Per marker zoom limits take precedence, if present
|
||||||
if(this._zoomLimitedLayers.has(layer)) {
|
if(this._zoomLimitedLayers.has(layer)) {
|
||||||
this._isLayerVisible(layer, zoom) ? this._map.addLayer(layer) : this._map.removeLayer(layer);
|
LiveAtlasLayerGroup._isLayerVisible(layer, zoom) ? this._addToMap(layer) : this._removeFromMap(layer);
|
||||||
} else { //Otherwise apply group zoom limit
|
} else { //Otherwise apply group zoom limit
|
||||||
visible ? this._map.addLayer(layer) : this._map.removeLayer(layer);
|
visible ? this._addToMap(layer) : this._removeFromMap(layer);
|
||||||
}
|
}
|
||||||
}, this);
|
}, this);
|
||||||
//Group isn't zoom limited, but some individual markers are
|
//Group isn't zoom limited, but some individual markers are
|
||||||
} else if(this._zoomLimitedLayers.size) {
|
} else if(this._zoomLimitedLayers.size) {
|
||||||
this._zoomLimitedLayers.forEach((layer) => {
|
this._zoomLimitedLayers.forEach((layer) => {
|
||||||
this._isLayerVisible(layer, zoom) ? this._map.addLayer(layer) : this._map.removeLayer(layer);
|
LiveAtlasLayerGroup._isLayerVisible(layer, zoom) ? this._addToMap(layer) : this._removeFromMap(layer);
|
||||||
});
|
});
|
||||||
//Nothing is zoom limited, but we've just been added to the map
|
//Nothing is zoom limited, but we've just been added to the map
|
||||||
} else if(onAdd) {
|
} else if(onAdd) {
|
||||||
this.eachLayer(this._map.addLayer, this._map);
|
this.eachLayer((layer: Layer) => this._addToMap(layer), this._map);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Returns if this layer group has zoom limits defined
|
//Returns if this layer group has zoom limits defined
|
||||||
_isZoomLimited() {
|
private _isZoomLimited() {
|
||||||
return this.options.maxZoom !== undefined || this.options.minZoom !== undefined;
|
return this.options.maxZoom !== undefined || this.options.minZoom !== undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Returns if the given layer has its own zoom limits defined
|
//Returns if the given layer has its own zoom limits defined
|
||||||
_isLayerZoomLimited(layer: Layer) {
|
private static _isLayerZoomLimited(layer: Layer) {
|
||||||
return ((layer as any).options && (layer as any).options.minZoom !== undefined)
|
return ((layer as any).options && (layer as any).options.minZoom !== undefined)
|
||||||
&& ((layer as any).options && (layer as any).options.maxZoom !== undefined);
|
&& ((layer as any).options && (layer as any).options.maxZoom !== undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
_isLayerVisible(layer: Layer, currentZoom: number) {
|
private static _isLayerVisible(layer: Layer, currentZoom: number) {
|
||||||
let minZoom = -Infinity,
|
let minZoom = -Infinity,
|
||||||
maxZoom = Infinity;
|
maxZoom = Infinity;
|
||||||
|
|
||||||
@ -183,4 +196,17 @@ export default class LiveAtlasLayerGroup extends LayerGroup {
|
|||||||
|
|
||||||
return currentZoom >= minZoom && currentZoom <= maxZoom;
|
return currentZoom >= minZoom && currentZoom <= maxZoom;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _addToMap(layer: Layer) {
|
||||||
|
this._map.addLayer(layer)
|
||||||
|
|
||||||
|
//Create marker label immediately if labels are visible by default
|
||||||
|
if(layer instanceof GenericMarker && this.options.showLabels) {
|
||||||
|
(layer as GenericMarker).createLabel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _removeFromMap(layer: Layer) {
|
||||||
|
this._map.removeLayer(layer)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,16 +15,30 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {MarkerOptions, Marker, Util, LatLngExpression, Icon} from 'leaflet';
|
import {MarkerOptions, Marker, Util, LatLngExpression, Icon} from 'leaflet';
|
||||||
|
import {LiveAtlasMarker} from "@/index";
|
||||||
|
import {GenericIcon} from "@/leaflet/icon/GenericIcon";
|
||||||
|
|
||||||
export interface GenericMarkerOptions extends MarkerOptions {
|
export interface GenericMarkerOptions extends MarkerOptions {
|
||||||
|
icon: GenericIcon;
|
||||||
minZoom?: number;
|
minZoom?: number;
|
||||||
maxZoom?: number;
|
maxZoom?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class GenericMarker extends Marker {
|
export class GenericMarker extends Marker {
|
||||||
constructor(latLng: LatLngExpression, options: GenericMarkerOptions) {
|
declare options: GenericMarkerOptions;
|
||||||
super(latLng, options);
|
|
||||||
Util.setOptions(this, options);
|
constructor(latLng: LatLngExpression, options: LiveAtlasMarker) {
|
||||||
|
super(latLng, {});
|
||||||
|
|
||||||
|
this.options.icon = new GenericIcon({
|
||||||
|
icon: options.icon,
|
||||||
|
label: options.label,
|
||||||
|
iconSize: options.dimensions,
|
||||||
|
isHtml: options.isLabelHTML,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.options.maxZoom = options.maxZoom;
|
||||||
|
this.options.minZoom = options.maxZoom;
|
||||||
}
|
}
|
||||||
|
|
||||||
// noinspection JSUnusedGlobalSymbols
|
// noinspection JSUnusedGlobalSymbols
|
||||||
@ -35,4 +49,8 @@ export class GenericMarker extends Marker {
|
|||||||
getIcon(): Icon.Default {
|
getIcon(): Icon.Default {
|
||||||
return this.options.icon as Icon.Default;
|
return this.options.icon as Icon.Default;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createLabel(): void {
|
||||||
|
this.options.icon.createLabel();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,16 +23,7 @@ import {GenericMarker} from "@/leaflet/marker/GenericMarker";
|
|||||||
import {LiveAtlasMarker} from "@/index";
|
import {LiveAtlasMarker} from "@/index";
|
||||||
|
|
||||||
export const createMarker = (options: LiveAtlasMarker, converter: Function): Marker => {
|
export const createMarker = (options: LiveAtlasMarker, converter: Function): Marker => {
|
||||||
const marker = new GenericMarker(converter(options.location), {
|
const marker = new GenericMarker(converter(options.location), options);
|
||||||
icon: new GenericIcon({
|
|
||||||
icon: options.icon,
|
|
||||||
label: options.label,
|
|
||||||
iconSize: options.dimensions,
|
|
||||||
isHtml: options.isLabelHTML,
|
|
||||||
}),
|
|
||||||
maxZoom: options.maxZoom,
|
|
||||||
minZoom: options.minZoom,
|
|
||||||
});
|
|
||||||
|
|
||||||
marker.on('click', (e: LeafletMouseEvent) => {
|
marker.on('click', (e: LeafletMouseEvent) => {
|
||||||
e.target._map.panTo(e.target.getLatLng());
|
e.target._map.panTo(e.target.getLatLng());
|
||||||
|
Loading…
Reference in New Issue
Block a user