mirror of https://gitee.com/antv-l7/antv-l7
feat: 1.完善 Scale 组件升级
This commit is contained in:
parent
0197293254
commit
3bdaeb67cd
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
title: 比例尺
|
||||
order: 14
|
||||
---
|
||||
|
||||
<code src="./scale.tsx" compact defaultShowCode></code>
|
|
@ -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<Scale | null>(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 (
|
||||
<>
|
||||
<button
|
||||
onClick={() => {
|
||||
control?.setOptions({
|
||||
imperial: true,
|
||||
metric: false,
|
||||
});
|
||||
}}
|
||||
>
|
||||
更新配置
|
||||
</button>
|
||||
<div
|
||||
id="map"
|
||||
style={{
|
||||
height: '500px',
|
||||
position: 'relative',
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Demo;
|
|
@ -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);
|
||||
});
|
||||
});
|
|
@ -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<IScaleControlOption> {
|
||||
private mScale: HTMLElement;
|
||||
private iScale: HTMLElement;
|
||||
constructor(cfg?: Partial<IScaleControlOption>) {
|
||||
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<IScaleControlOption>) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,4 +7,5 @@
|
|||
@import 'logo';
|
||||
@import 'mouseLocation';
|
||||
@import 'zoom';
|
||||
@import 'scale';
|
||||
@import '../assets/iconfont.css';
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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';
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue