From f706753866446423031a8057b4f57d384e4600df Mon Sep 17 00:00:00 2001 From: yanxiong Date: Mon, 31 Oct 2022 15:44:46 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E8=BF=98=E5=8E=9F=E6=97=A7=E7=89=88?= =?UTF-8?q?=E5=9B=BE=E5=B1=82=E9=80=89=E6=8B=A9=E6=8E=A7=E4=BB=B6=20Layers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dev-demos/layer/control/position.tsx | 9 +- packages/component/src/control/layer.ts | 325 ++++++++++++++++++++++++ packages/component/src/index.ts | 3 + 3 files changed, 329 insertions(+), 8 deletions(-) create mode 100644 packages/component/src/control/layer.ts diff --git a/dev-demos/layer/control/position.tsx b/dev-demos/layer/control/position.tsx index d481248955..a280c59081 100644 --- a/dev-demos/layer/control/position.tsx +++ b/dev-demos/layer/control/position.tsx @@ -1,11 +1,4 @@ -import { - GaodeMap, - IControlOption, - Logo, - Scale, - Scene, - Zoom, -} from '@antv/l7'; +import { GaodeMap, IControlOption, Logo, Scale, Scene, Zoom } from '@antv/l7'; import React, { FunctionComponent, useEffect, useRef } from 'react'; const Demo: FunctionComponent = () => { diff --git a/packages/component/src/control/layer.ts b/packages/component/src/control/layer.ts new file mode 100644 index 0000000000..ff60eecdc7 --- /dev/null +++ b/packages/component/src/control/layer.ts @@ -0,0 +1,325 @@ +import { PositionType } from '@antv/l7-core'; +import { bindAll, DOM } from '@antv/l7-utils'; +import Control, { IControlOption } from './baseControl/control'; + +interface IInputItem extends HTMLInputElement { + layerId: string; +} + +export interface ILayerControlOption extends IControlOption { + collapsed: boolean; + autoZIndex: boolean; + hideSingleBase: boolean; + sortLayers: boolean; + + sortFunction: (...args: any[]) => any; +} + +export default class Layers extends Control { + private layerControlInputs: any[]; + private layers: any[]; + private lastZIndex: number; + private handlingClick: boolean; + private layersLink: HTMLElement; + private baseLayersList: HTMLElement; + private separator: HTMLElement; + private overlaysList: HTMLElement; + private form: HTMLElement; + + constructor(cfg: Partial) { + super(cfg); + this.layerControlInputs = []; + this.layers = []; + this.lastZIndex = 0; + this.handlingClick = false; + this.initLayers(); + + bindAll( + [ + 'checkDisabledLayers', + 'onLayerChange', + 'collapse', + 'extend', + 'expand', + 'onInputClick', + ], + this, + ); + } + + public getDefault() { + return { + collapsed: true, + position: PositionType.TOPRIGHT, + autoZIndex: true, + hideSingleBase: false, + sortLayers: false, + name: 'layers', + }; + } + public onAdd() { + this.initLayout(); + this.update(); + this.mapsService.on('zoomend', this.checkDisabledLayers); + this.layers.forEach((layerItem) => { + layerItem.layer.on('remove', this.onLayerChange); + layerItem.layer.on('add', this.onLayerChange); + }); + return this.container; + } + + public addVisualLayer(layer: any, name: string | number) { + this.addLayer(layer, name, true); + return this.mapsService ? this.update() : this; + } + public expand() { + const { height } = this.renderService.getViewportSize(); + DOM.addClass(this.container, 'l7-control-layers-expanded'); + this.form.style.height = 'null'; + const acceptableHeight = height - (this.container.offsetTop + 50); + if (acceptableHeight < this.form.clientHeight) { + DOM.addClass(this.form, 'l7-control-layers-scrollbar'); + this.form.style.height = acceptableHeight + 'px'; + } else { + DOM.removeClass(this.form, 'l7-control-layers-scrollbar'); + } + this.checkDisabledLayers(); + return this; + } + + public collapse() { + DOM.removeClass(this.container, 'l7-control-layers-expanded'); + return this; + } + + public onRemove() { + if (!this.mapsService) { + return; + } + this.mapsService.off('click', this.collapse); + this.layers.forEach((layerItem) => { + layerItem.layer.off('remove', this.onLayerChange); + layerItem.layer.off('add', this.onLayerChange); + }); + } + private initLayout() { + const className = 'l7-control-layers'; + const container = (this.container = DOM.create('div', className)); + const { collapsed } = this.controlOption; + // makes this work on IE touch devices by stopping it from firing a mouseout event when the touch is released + container.setAttribute('aria-haspopup', 'true'); + const form = (this.form = DOM.create( + 'form', + className + '-list', + ) as HTMLElement); + + if (collapsed) { + this.mapsService.on('click', this.collapse); + container.addEventListener('mouseenter', this.expand); + container.addEventListener('mouseleave', this.collapse); + } + + this.layersLink = DOM.create('a', className + '-toggle', container); + const link = this.layersLink; + // link.href = '#'; + link.title = 'Layers'; + if (!collapsed) { + this.expand(); + } + this.baseLayersList = DOM.create('div', className + '-base', form); + this.separator = DOM.create('div', className + '-separator', form); + this.overlaysList = DOM.create('div', className + '-overlays', form); + container.appendChild(form); + } + private initLayers() { + const { baseLayers = {}, overlayers = {} } = this.controlOption; + Object.keys(baseLayers).forEach((name: string) => { + // baseLayers[name].once('inited', this.update); + this.addLayer(baseLayers[name], name, false); + }); + Object.keys(overlayers).forEach((name: any) => { + // overlayers[name].once('inited', this.update); + this.addLayer(overlayers[name], name, true); + }); + } + + private update() { + if (!this.container) { + return this; + } + + DOM.empty(this.baseLayersList); + DOM.empty(this.overlaysList); + + this.layerControlInputs = []; + let baseLayersPresent; + let overlaysPresent; + let i; + let obj; + let baseLayersCount = 0; + + for (i = 0; i < this.layers.length; i++) { + obj = this.layers[i]; + this.addItem(obj); + overlaysPresent = overlaysPresent || obj.overlay; + baseLayersPresent = baseLayersPresent || !obj.overlay; + baseLayersCount += !obj.overlay ? 1 : 0; + } + + // Hide base layers section if there's only one layer. + if (this.controlOption.hideSingleBase) { + baseLayersPresent = baseLayersPresent && baseLayersCount > 1; + this.baseLayersList.style.display = baseLayersPresent ? '' : 'none'; + } + + this.separator.style.display = + overlaysPresent && baseLayersPresent ? '' : 'none'; + + return this; + } + + private checkDisabledLayers() { + const inputs = this.layerControlInputs; + let input: IInputItem; + let layer; + const zoom = this.mapsService.getZoom(); + + for (let i = inputs.length - 1; i >= 0; i--) { + input = inputs[i]; + layer = this.layerService.getLayer(input.layerId); + + if (layer && layer.inited) { + const minZoom = layer.getMinZoom(); + const maxZoom = layer.getMaxZoom(); + + input.disabled = zoom < minZoom || zoom > maxZoom; + } + } + } + + private addLayer(layer: any, name: string | number, overlay: boolean) { + if (this.mapsService) { + layer.on('add', this.onLayerChange); + layer.on('remove', this.onLayerChange); + } + this.layers.push({ + layer, + name, + overlay, + }); + const { sortLayers, sortFunction, autoZIndex } = this.controlOption; + if (sortLayers) { + this.layers.sort((a, b) => { + return sortFunction(a.layer, b.layer, a.name, b.name); + }); + } + + if (autoZIndex && layer.setZIndex) { + this.lastZIndex++; + layer.setZIndex(this.lastZIndex); + } + + this.expandIfNotCollapsed(); + } + + private expandIfNotCollapsed() { + if (this.mapsService && !this.controlOption.collapsed) { + this.expand(); + } + return this; + } + + private onLayerChange(e: any) { + if (!this.handlingClick) { + this.update(); + } + + const obj = this.layerService.getLayer(e.target.layerId); + + // @ts-ignore + const type = obj?.overlay + ? e.type === 'add' + ? 'overlayadd' + : 'overlayremove' + : e.type === 'add' + ? 'baselayerchange' + : null; + + if (type) { + this.emit(type, obj); + } + } + + private createRadioElement(name: string, checked: boolean): ChildNode { + const radioHtml = + ''; + + const radioFragment = document.createElement('div'); + radioFragment.innerHTML = radioHtml; + + return radioFragment.firstChild as ChildNode; + } + + private addItem(obj: any) { + const label = document.createElement('label'); + const layer = this.layerService.getLayer(obj.layer.id); + const checked = layer && layer.inited && obj.layer.isVisible(); + let input: IInputItem; + if (obj.overlay) { + input = document.createElement('input') as IInputItem; + input.type = 'checkbox'; + input.className = 'l7-control-layers-selector'; + input.defaultChecked = checked; + } else { + input = this.createRadioElement('l7-base-layers', checked) as IInputItem; + } + this.layerControlInputs.push(input); + input.layerId = obj.layer.id; + input.addEventListener('click', this.onInputClick); + + const name = document.createElement('span'); + name.innerHTML = ' ' + obj.name; + + const holder = document.createElement('div'); + + label.appendChild(holder); + holder.appendChild(input); + holder.appendChild(name); + + const container = obj.overlay ? this.overlaysList : this.baseLayersList; + container.appendChild(label); + + this.checkDisabledLayers(); + return label; + } + + private onInputClick() { + const inputs = this.layerControlInputs; + let input; + let layer; + const addedLayers = []; + const removedLayers = []; + this.handlingClick = true; + for (let i = inputs.length - 1; i >= 0; i--) { + input = inputs[i]; + layer = this.layerService.getLayer(input.layerId); + if (input.checked) { + addedLayers.push(layer); + } else if (!input.checked) { + removedLayers.push(layer); + } + } + removedLayers.forEach((l: any) => { + l.hide(); + }); + addedLayers.forEach((l: any) => { + l.show(); + }); + + this.handlingClick = false; + } +} diff --git a/packages/component/src/index.ts b/packages/component/src/index.ts index 73b78e37a6..379982e204 100644 --- a/packages/component/src/index.ts +++ b/packages/component/src/index.ts @@ -18,6 +18,9 @@ export * from './control/scale'; export * from './popup/popup'; export * from './popup/layerPopup'; +// 为了兼容老版 LayerSwitch +export * from './control/layer'; + export { Marker, MarkerLayer }; export * from './interface';