From 3bdaeb67cd0de45691f5fe100545e1546d172520 Mon Sep 17 00:00:00 2001 From: yanxiong Date: Wed, 31 Aug 2022 15:03:19 +0800 Subject: [PATCH] =?UTF-8?q?feat:=201.=E5=AE=8C=E5=96=84=20Scale=20?= =?UTF-8?q?=E7=BB=84=E4=BB=B6=E5=8D=87=E7=BA=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dev-demos/layer/control/scale.md | 6 ++ dev-demos/layer/control/scale.tsx | 62 ++++++++++++++ packages/component/__tests__/scale.spec.ts | 38 +++++++++ packages/component/src/control/scale.ts | 95 +++++++++++++--------- packages/component/src/css/index.less | 1 + packages/component/src/css/l7.less | 54 ------------ packages/component/src/css/scale.less | 34 ++++++++ packages/component/src/index.ts | 4 +- packages/component/src/interface.ts | 8 -- 9 files changed, 200 insertions(+), 102 deletions(-) create mode 100644 dev-demos/layer/control/scale.md create mode 100644 dev-demos/layer/control/scale.tsx create mode 100644 packages/component/__tests__/scale.spec.ts create mode 100644 packages/component/src/css/scale.less diff --git a/dev-demos/layer/control/scale.md b/dev-demos/layer/control/scale.md new file mode 100644 index 0000000000..b2a456ddcb --- /dev/null +++ b/dev-demos/layer/control/scale.md @@ -0,0 +1,6 @@ +--- +title: 比例尺 +order: 14 +--- + + diff --git a/dev-demos/layer/control/scale.tsx b/dev-demos/layer/control/scale.tsx new file mode 100644 index 0000000000..f1b66be0ef --- /dev/null +++ b/dev-demos/layer/control/scale.tsx @@ -0,0 +1,62 @@ +import { Mapbox, Scene, Scale, Zoom } from '@antv/l7'; +import React, { useState } from 'react'; +// tslint:disable-next-line:no-duplicate-imports +import { FunctionComponent, useEffect } from 'react'; + +const Demo: FunctionComponent = () => { + const [control, setControl] = useState(null); + + useEffect(() => { + const newScene = new Scene({ + id: 'map', + // map: new GaodeMap({ + // center: [120, 30], + // pitch: 0, + // zoom: 6.45, + // }), + map: new Mapbox({ + center: [120, 30], + pitch: 0, + zoom: 6.45, + }), + }); + + newScene.on('loaded', () => { + const scale = new Scale({ + metric: true, + position: 'rightbottom', + // imperial: true, + }); + const zoom = new Zoom({ + position: 'rightbottom', + }); + newScene.addControl(scale); + newScene.addControl(zoom); + setControl(scale); + }); + }, []); + + return ( + <> + +
+ + ); +}; + +export default Demo; diff --git a/packages/component/__tests__/scale.spec.ts b/packages/component/__tests__/scale.spec.ts new file mode 100644 index 0000000000..6bb6c76d4b --- /dev/null +++ b/packages/component/__tests__/scale.spec.ts @@ -0,0 +1,38 @@ +import { TestScene } from '@antv/l7-test-utils'; +import Scale from '../src/control/scale'; + +describe('scale', () => { + const scene = TestScene(); + + it('life cycle', () => { + const scale = new Scale(); + scene.addControl(scale); + + const container = scale.getContainer(); + expect(container.parentElement).toBeInstanceOf(HTMLElement); + + expect( + /\d+\s?km/i.test( + container + .querySelector('.l7-control-scale-line') + ?.innerHTML.toLowerCase() ?? '', + ), + ).toEqual(true); + + scale.setOptions({ + metric: false, + imperial: true, + }); + + expect( + /\d+\s?mi/i.test( + container + .querySelector('.l7-control-scale-line') + ?.innerHTML.toLowerCase() ?? '', + ), + ).toEqual(true); + + scene.removeControl(scale); + expect(container.parentElement).not.toBeInstanceOf(HTMLElement); + }); +}); diff --git a/packages/component/src/control/scale.ts b/packages/component/src/control/scale.ts index d01ac12d82..051c50e6b6 100644 --- a/packages/component/src/control/scale.ts +++ b/packages/component/src/control/scale.ts @@ -1,39 +1,42 @@ -import { bindAll, DOM, lnglatDistance } from '@antv/l7-utils'; -import { IScaleControlOption } from '../interface'; -import { Control, PositionType } from './baseControl'; +import { DOM, lnglatDistance } from '@antv/l7-utils'; +import { Control, IControlOption, PositionType } from './baseControl'; -export default class Scale extends Control { +export interface IScaleControlOption extends IControlOption { + lockWidth: boolean; + maxWidth: number; + metric: boolean; + updateWhenIdle: boolean; + imperial: boolean; +} + +export { Scale }; + +export default class Scale extends Control { private mScale: HTMLElement; private iScale: HTMLElement; - constructor(cfg?: Partial) { - super(cfg); - bindAll(['update'], this); - } public getDefault() { return { + name: 'scale', position: PositionType.BOTTOMLEFT, maxWidth: 100, metric: true, updateWhenIdle: false, imperial: false, - name: 'scale', + lockWidth: true, }; } public onAdd() { const className = 'l7-control-scale'; const container = DOM.create('div', className); - this.addScales(className + '-line', container); + this.resetScaleLines(container); const { updateWhenIdle } = this.controlOption; - // TODO: 高德地图和MapBox地图事件不一致问题 - // 高德zoomchange this.mapsService.on(updateWhenIdle ? 'moveend' : 'mapmove', this.update); this.mapsService.on(updateWhenIdle ? 'zoomend' : 'zoomchange', this.update); - this.update(); - return container; } + public onRemove() { const { updateWhenIdle } = this.controlOption; this.mapsService.off( @@ -42,7 +45,13 @@ export default class Scale extends Control { ); this.mapsService.off(updateWhenIdle ? 'moveend' : 'mapmove', this.update); } - public update() { + + public setOptions(newOption: Partial) { + super.setOptions(newOption); + this.resetScaleLines(this.container); + } + + public update = () => { const mapsService = this.mapsService; const { maxWidth } = this.controlOption; const y = mapsService.getSize()[1] / 2; @@ -51,7 +60,8 @@ export default class Scale extends Control { const p2 = mapsService.containerToLngLat([maxWidth, y]); const maxMeters = lnglatDistance([p1.lng, p1.lat], [p2.lng, p2.lat]); this.updateScales(maxMeters); - } + }; + public updateScales(maxMeters: number) { const { metric, imperial } = this.controlOption; if (metric && maxMeters) { @@ -61,11 +71,42 @@ export default class Scale extends Control { this.updateImperial(maxMeters); } } + + private resetScaleLines(container: HTMLElement) { + DOM.clearChildren(container); + const { metric, imperial, maxWidth, lockWidth } = this.controlOption; + if (lockWidth) { + DOM.addStyle(container, `width: ${maxWidth}px`); + } + if (metric) { + this.mScale = DOM.create('div', 'l7-control-scale-line', container); + } + if (imperial) { + this.iScale = DOM.create('div', 'l7-control-scale-line', container); + } + this.update(); + } + + private updateScale(scale: HTMLElement, text: string, ratio: number) { + const { maxWidth } = this.controlOption; + scale.style.width = Math.round(maxWidth * ratio) + 'px'; + scale.innerHTML = text; + } + private getRoundNum(num: number) { + const pow10 = Math.pow(10, (Math.floor(num) + '').length - 1); + let d = num / pow10; + + d = d >= 10 ? 10 : d >= 5 ? 5 : d >= 3 ? 3 : d >= 2 ? 2 : 1; + + return pow10 * d; + } + private updateMetric(maxMeters: number) { const meters = this.getRoundNum(maxMeters); const label = meters < 1000 ? meters + ' m' : meters / 1000 + ' km'; this.updateScale(this.mScale, label, meters / maxMeters); } + private updateImperial(maxMeters: number) { const maxFeet = maxMeters * 3.2808399; let maxMiles: number; @@ -81,26 +122,4 @@ export default class Scale extends Control { this.updateScale(this.iScale, feet + ' ft', feet / maxFeet); } } - private updateScale(scale: HTMLElement, text: string, ratio: number) { - const { maxWidth } = this.controlOption; - scale.style.width = Math.round(maxWidth * ratio) + 'px'; - scale.innerHTML = text; - } - private getRoundNum(num: number) { - const pow10 = Math.pow(10, (Math.floor(num) + '').length - 1); - let d = num / pow10; - - d = d >= 10 ? 10 : d >= 5 ? 5 : d >= 3 ? 3 : d >= 2 ? 2 : 1; - - return pow10 * d; - } - private addScales(className: string, container: HTMLElement) { - const { metric, imperial } = this.controlOption; - if (metric) { - this.mScale = DOM.create('div', className, container); - } - if (imperial) { - this.iScale = DOM.create('div', className, container); - } - } } diff --git a/packages/component/src/css/index.less b/packages/component/src/css/index.less index 209d4ae35b..facd0566df 100644 --- a/packages/component/src/css/index.less +++ b/packages/component/src/css/index.less @@ -7,4 +7,5 @@ @import 'logo'; @import 'mouseLocation'; @import 'zoom'; +@import 'scale'; @import '../assets/iconfont.css'; diff --git a/packages/component/src/css/l7.less b/packages/component/src/css/l7.less index 9b1721df42..c1384cc9b0 100644 --- a/packages/component/src/css/l7.less +++ b/packages/component/src/css/l7.less @@ -228,60 +228,6 @@ pointer-events: auto; } -/* attribution and scale controls */ - -.l7-control-container .l7-control-attribution { - margin: 0; - background: #fff; - background: rgba(59, 58, 58, 0.7); -} -.l7-control-attribution, -.l7-control-scale-line { - padding: 0 5px; - color: #333; - transition: width 0.1s; -} -.l7-control-attribution a { - text-decoration: none; -} -.l7-control-attribution a:hover { - text-decoration: underline; -} -.l7-container .l7-control-attribution, -.l7-container .l7-control-scale { - padding: 5px 5px 2px 5px; - font-size: 11px; - background: rgba(255, 255, 255, 0.7); -} -.l7-left .l7-control-scale { - margin-left: 5px; -} -.l7-bottom .l7-control-scale { - margin-bottom: 5px; -} -.l7-control-scale-line { - -moz-box-sizing: border-box; - box-sizing: border-box; - padding: 2px 5px 1px; - overflow: hidden; - color: #000; - font-size: 11px; - line-height: 1.1; - white-space: nowrap; - - background: #fff; - border: 2px solid #000; - border-top: none; -} -.l7-control-scale-line:not(:first-child) { - margin-top: -2px; - border-top: 2px solid #777; - border-bottom: none; -} -.l7-control-scale-line:not(:first-child):not(:last-child) { - border-bottom: 2px solid #777; -} - .l7-touch .l7-control-attribution, .l7-touch .l7-control-layers, .l7-touch .l7-bar { diff --git a/packages/component/src/css/scale.less b/packages/component/src/css/scale.less new file mode 100644 index 0000000000..a280e4ee7d --- /dev/null +++ b/packages/component/src/css/scale.less @@ -0,0 +1,34 @@ +@import 'variables'; + +.l7-control-scale { + display: flex; + flex-direction: column; + .l7-control-scale-line { + box-sizing: border-box; + padding: 2px 5px 1px; + overflow: hidden; + color: @l7-control-font-color; + font-size: 10px; + line-height: 1.1; + white-space: nowrap; + background: @l7-control-bg-color; + border: 2px solid #000; + border-top: 0; + transition: width 0.1s; + & + & { + margin-top: -2px; + border-top: 2px solid #777; + border-bottom: none; + } + } +} + +.l7-right { + .l7-control-scale { + display: flex; + align-items: flex-end; + .l7-control-scale-line { + text-align: right; + } + } +} diff --git a/packages/component/src/index.ts b/packages/component/src/index.ts index 60eee58fa7..c08e7e92cd 100644 --- a/packages/component/src/index.ts +++ b/packages/component/src/index.ts @@ -1,4 +1,3 @@ -import Scale from './control/scale'; import Marker from './marker'; import MarkerLayer from './marker-layer'; import Popup from './popup'; @@ -16,7 +15,8 @@ export * from './control/mapTheme'; export * from './control/layerControl'; export * from './control/mouseLocation'; export * from './control/zoom'; +export * from './control/scale'; -export { Scale, Marker, Popup, MarkerLayer, createL7Icon }; +export { Marker, Popup, MarkerLayer, createL7Icon }; export * from './interface'; diff --git a/packages/component/src/interface.ts b/packages/component/src/interface.ts index 37696a4899..9e4e197444 100644 --- a/packages/component/src/interface.ts +++ b/packages/component/src/interface.ts @@ -1,13 +1,5 @@ -import { IControlOption } from '@antv/l7-core'; - export type ControlEvent = 'show' | 'hide' | 'add' | 'remove' | string; -export interface IScaleControlOption extends IControlOption { - maxWidth: number; - metric: boolean; - updateWhenIdle: boolean; - imperial: boolean; -} export interface IMarkerStyleOption { element?: (...args: any[]) => any; style: { [key: string]: any } | ((...args: any[]) => any);