diff --git a/packages/draw/src/modes/draw_circle.ts b/packages/draw/src/modes/draw_circle.ts index 407bda8407..03d2b1d73c 100644 --- a/packages/draw/src/modes/draw_circle.ts +++ b/packages/draw/src/modes/draw_circle.ts @@ -12,7 +12,9 @@ import { import turfCircle from '@turf/circle'; import turfDistance from '@turf/distance'; import { Feature, featureCollection, point } from '@turf/helpers'; +import RenderLayer from '../render/render'; import DrawFeature, { IDrawOption } from './draw_feature'; +import DrawSelected from './draw_selected'; let CircleFeatureId = 0; export type unitsType = 'degrees' | 'radians' | 'miles' | 'kilometers'; export interface IDrawCircleOption extends IDrawOption { @@ -28,9 +30,8 @@ export default class DrawCircle extends DrawFeature { private endPoint: ILngLat; private dragStartPoint: ILngLat; // 绘制完成之后显示 - private normalLayer: ILayer; - private normalLineLayer: ILayer; - + private normalLayer: RenderLayer; + private selectMode: DrawSelected; // 编辑过程中显示 private centerLayer: ILayer; private circleLayer: ILayer; @@ -39,15 +40,11 @@ export default class DrawCircle extends DrawFeature { private popup: IPopup; constructor(scene: Scene, options: Partial = {}) { super(scene, options); - this.initNormalLayer(); + this.normalLayer = new RenderLayer(this); } protected onDragStart = (e: IInteractionTarget) => { // @ts-ignore - this.scene.map.dragPan.disable(); this.dragStartPoint = e.lngLat; - if (this.drawStatus === 'DrawSelected') { - return; - } this.center = e.lngLat; const centerStyle = this.getStyle('active_point'); const layer = new PointLayer() @@ -63,28 +60,19 @@ export default class DrawCircle extends DrawFeature { .size(centerStyle.size) .style(centerStyle.style); this.scene.addLayer(layer); - this.initDrawLayer(); this.centerLayer = layer; + this.initDrawLayer(); this.setCursor('grabbing'); }; protected getDefaultOptions() { return { - steps: 4, + steps: 64, units: 'kilometres', + cursor: 'crosshair', }; } protected onDragging = (e: IInteractionTarget) => { - if (this.drawStatus === 'DrawSelected') { - const delta = { - lng: e.lngLat.lng - this.dragStartPoint.lng, - lat: e.lngLat.lat - this.dragStartPoint.lat, - }; - this.moveCircle(this.currentFeature as Feature, delta); - this.dragStartPoint = e.lngLat; - - return; - } this.endPoint = e.lngLat; const currentData = this.createCircleData(this.center, this.endPoint); this.updateDrawLayer(currentData); @@ -93,16 +81,16 @@ export default class DrawCircle extends DrawFeature { }; protected onDragEnd = () => { - if (this.drawStatus === 'DrawSelected') { - return; - } this.source.addFeature(this.currentFeature); // @ts-ignore this.scene.map.dragPan.enable(); this.popup.remove(); - // this.disable(); - this.addCircleLayerEvent(); + // 绘制完成进入选中状态 + this.selectMode = new DrawSelected(this.scene, {}); + this.selectMode.setSelectedFeature(this.currentFeature as Feature); + this.removeDrawLayer(); this.drawStatus = 'DrawSelected'; + this.disable(); return; }; protected onClick = () => { @@ -113,7 +101,7 @@ export default class DrawCircle extends DrawFeature { return; } this.currentFeature = null; - this.updateNormalLayer(); + this.normalLayer.updateData(); this.centerLayer.setData([]); this.circleLayer.setData(InitFeature); this.circleLineLayer.setData(InitFeature); @@ -162,82 +150,21 @@ export default class DrawCircle extends DrawFeature { this.circleLayer.setData(currentData); this.circleLineLayer.setData(currentData); } + + private removeDrawLayer() { + this.scene.removeLayer(this.circleLayer); + this.scene.removeLayer(this.circleLineLayer); + this.scene.removeLayer(this.centerLayer); + } + private addDrawPopup(lnglat: ILngLat, dis: number) { const popup = new Popup({ anchor: 'left', closeButton: false, }) .setLnglat(lnglat) - .setText(`${dis}`); + .setText(`半径:${dis.toFixed(2)}千米`); this.scene.addPopup(popup); this.popup = popup; } - - private initNormalLayer() { - const style = this.getStyle('normal_fill'); - const linestyle = this.getStyle('normal_line'); - this.normalLayer = new PolygonLayer() - .source(this.source.data) - .shape('fill') - .active(true) - .color(style.color) - .style(style.style); - - this.normalLineLayer = new LineLayer() - .source(this.source.data) - .shape('line') - .size(linestyle.size) - .color(linestyle.color) - .style(linestyle.style); - this.scene.addLayer(this.normalLayer); - this.scene.addLayer(this.normalLineLayer); - this.normalLayer.on('click', this.onNormalLayerClick); - } - private updateNormalLayer() { - this.normalLayer.setData(this.source.data); - this.normalLineLayer.setData(this.source.data); - } - - private onNormalLayerClick = (e: any) => { - this.currentFeature = e.feature; - this.normalLayer.filter('id', (id: string) => { - return this.currentFeature === null || id !== e.feature.properties.id; - }); - this.normalLineLayer.filter('id', (id: string) => { - return this.currentFeature === null || id !== e.feature.properties.id; - }); - const seletedFeature = e.feature; - this.setCursor('move'); - this.updateDrawLayer(featureCollection([seletedFeature])); - this.centerLayer.setData([seletedFeature.properties.center]); - this.drawStatus = 'DrawSelected'; - this.enable(); - }; - - private addCircleLayerEvent() { - this.circleLayer.on('mousemove', (e) => { - this.setCursor('move'); - }); - this.circleLayer.on('unmousemove', (e) => { - this.resetCursor(); - }); - this.circleLayer.on('unclick', this.onCircleLayerClick); - } - - private moveCircle(feature: Feature, delta: ILngLat) { - const preCenter = feature?.properties?.center as ILngLat; - const preEndPoint = feature?.properties?.endPoint as ILngLat; - const newCenter = { - lng: preCenter.lng + delta.lng, - lat: preCenter.lat + delta.lat, - }; - const newEndPoint = { - lng: preEndPoint.lng + delta.lng, - lat: preEndPoint.lat + delta.lat, - }; - - const newCircle = this.createCircleData(newCenter, newEndPoint); - this.centerLayer.setData([newCenter]); - this.updateDrawLayer(newCircle); - } } diff --git a/packages/draw/src/modes/draw_edit.ts b/packages/draw/src/modes/draw_edit.ts new file mode 100644 index 0000000000..93dcc57382 --- /dev/null +++ b/packages/draw/src/modes/draw_edit.ts @@ -0,0 +1,109 @@ +import { + IInteractionTarget, + ILayer, + ILngLat, + IPopup, + LineLayer, + PointLayer, + PolygonLayer, + Popup, + Scene, +} from '@antv/l7'; +import turfCircle from '@turf/circle'; +import turfDistance from '@turf/distance'; +import { Feature, featureCollection, point } from '@turf/helpers'; +import EditRender from '../render/edit'; +import DrawFeature, { IDrawOption } from './draw_feature'; +let CircleFeatureId = 0; +export type unitsType = 'degrees' | 'radians' | 'miles' | 'kilometers'; +export interface IDrawCircleOption extends IDrawOption { + units: unitsType; + steps: number; +} +const InitFeature = { + type: 'FeatureCollection', + features: [], +}; +export default class DrawEdit extends DrawFeature { + private center: ILngLat; + private endPoint: ILngLat; + // 绘制完成之后显示 + private editLayer: EditRender; + // 编辑过程中显示 + private currentFeature: Feature | null; + private popup: IPopup; + constructor(scene: Scene, options: Partial = {}) { + super(scene, options); + this.editLayer = new EditRender(this); + } + + public setEditFeature(feature: Feature) { + this.currentFeature = feature; + this.center = feature?.properties?.center; + this.editLayer.updateData({ + type: 'FeatureCollection', + features: [feature], + }); + } + + protected onDragStart = (e: IInteractionTarget) => { + // @ts-ignore + }; + protected getDefaultOptions() { + return { + steps: 64, + units: 'kilometres', + cursor: 'move', + }; + } + + protected onDragging = (e: IInteractionTarget) => { + this.endPoint = e.lngLat; + const currentData = this.createCircleData(this.center, this.endPoint); + this.editLayer.updateData(currentData); + + this.addDrawPopup(this.endPoint, this.currentFeature?.properties?.radius); + return; + }; + + protected onDragEnd = () => { + this.popup.remove(); + this.disable(); + // @ts-ignore + this.scene.map.dragPan.enable(); + }; + protected onClick = () => { + return null; + }; + + private createCircleData(center: ILngLat, endPoint: ILngLat) { + const radius = turfDistance( + point([center.lng, center.lat]), + point([endPoint.lng, endPoint.lat]), + this.getOption('units'), + ); + const feature = turfCircle([center.lng, center.lat], radius, { + units: this.getOption('units'), + steps: this.getOption('steps'), + properties: { + id: `${CircleFeatureId++}`, + radius, + center, + endPoint, + }, + }); + this.currentFeature = feature as Feature; + return featureCollection([feature]); + } + + private addDrawPopup(lnglat: ILngLat, dis: number) { + const popup = new Popup({ + anchor: 'left', + closeButton: false, + }) + .setLnglat(lnglat) + .setText(`半径:${dis.toFixed(2)}千米`); + this.scene.addPopup(popup); + this.popup = popup; + } +} diff --git a/packages/draw/src/modes/draw_feature.ts b/packages/draw/src/modes/draw_feature.ts index efdf413aad..b1f0a1c537 100644 --- a/packages/draw/src/modes/draw_feature.ts +++ b/packages/draw/src/modes/draw_feature.ts @@ -17,8 +17,8 @@ export type DrawStatus = | 'EditFinish'; export default abstract class DrawFeature { - protected source: DrawSource; - protected scene: Scene; + public source: DrawSource; + public scene: Scene; protected options: { [key: string]: any; } = { @@ -36,20 +36,49 @@ export default abstract class DrawFeature { if (this.isEnable) { return; } + // @ts-ignore + this.scene.map.dragPan.disable(); this.scene.on('dragstart', this.onDragStart); this.scene.on('dragging', this.onDragging); this.scene.on('dragend', this.onDragEnd); - // this.scene.on('click', this.onClick); - this.setCursor('crosshair'); + this.setCursor(this.getOption('cursor')); + this.isEnable = true; } public disable() { + if (!this.isEnable) { + return; + } this.scene.off('dragstart', this.onDragStart); this.scene.off('dragging', this.onDragging); this.scene.off('dragend', this.onDragEnd); // this.scene.off('click', this.onClick); this.resetCursor(); + // @ts-ignore + this.scene.map.dragPan.enable(); + this.isEnable = false; } + + public getOption(key: string) { + return this.options[key]; + } + public getStyle(id: string) { + return this.getOption('style')[id]; + } + + public setCursor(cursor: string) { + const container = this.scene.getContainer(); + if (container) { + container.style.cursor = cursor; + } + } + public resetCursor() { + const container = this.scene.getContainer(); + if (container) { + container.style.cursor = 'default'; + } + } + protected getDefaultOptions() { return {}; } @@ -61,23 +90,4 @@ export default abstract class DrawFeature { protected abstract onDragEnd(e: IInteractionTarget): void; protected abstract onClick(): void; - protected getOption(key: string) { - return this.options[key]; - } - protected getStyle(id: string) { - return this.getOption('style')[id]; - } - - protected setCursor(cursor: string) { - const container = this.scene.getContainer(); - if (container) { - container.style.cursor = cursor; - } - } - protected resetCursor() { - const container = this.scene.getContainer(); - if (container) { - container.style.cursor = 'default'; - } - } } diff --git a/packages/draw/src/modes/draw_selected.ts b/packages/draw/src/modes/draw_selected.ts new file mode 100644 index 0000000000..823f68a093 --- /dev/null +++ b/packages/draw/src/modes/draw_selected.ts @@ -0,0 +1,114 @@ +import { + IInteractionTarget, + ILayer, + ILngLat, + IPopup, + LineLayer, + PointLayer, + PolygonLayer, + Popup, + Scene, +} from '@antv/l7'; +import turfCircle from '@turf/circle'; +import turfDistance from '@turf/distance'; +import { Feature, featureCollection, point } from '@turf/helpers'; +import EditRender from '../render/selected'; +import DrawFeature, { IDrawOption } from './draw_feature'; +let CircleFeatureId = 0; +export type unitsType = 'degrees' | 'radians' | 'miles' | 'kilometers'; +export interface IDrawCircleOption extends IDrawOption { + units: unitsType; + steps: number; +} +const InitFeature = { + type: 'FeatureCollection', + features: [], +}; +export default class DrawSelect extends DrawFeature { + private center: ILngLat; + private dragStartPoint: ILngLat; + // 绘制完成之后显示 + private editLayer: EditRender; + // 编辑过程中显示 + private currentFeature: Feature | null; + constructor(scene: Scene, options: Partial = {}) { + super(scene, options); + this.editLayer = new EditRender(this); + } + + public setSelectedFeature(feature: Feature) { + this.currentFeature = feature; + this.editLayer.updateData({ + type: 'FeatureCollection', + features: [feature], + }); + } + + protected onDragStart = (e: IInteractionTarget) => { + // @ts-ignore + this.scene.map.dragPan.disable(); + this.dragStartPoint = e.lngLat; + }; + protected getDefaultOptions() { + return { + steps: 64, + units: 'kilometres', + cursor: 'move', + }; + } + + protected onDragging = (e: IInteractionTarget) => { + const delta = { + lng: e.lngLat.lng - this.dragStartPoint.lng, + lat: e.lngLat.lat - this.dragStartPoint.lat, + }; + this.moveCircle(this.currentFeature as Feature, delta); + this.dragStartPoint = e.lngLat; + + return; + }; + + protected onDragEnd = () => { + return null; + }; + protected onClick = () => { + return null; + }; + + private createCircleData(center: ILngLat, endPoint: ILngLat) { + const radius = turfDistance( + point([center.lng, center.lat]), + point([endPoint.lng, endPoint.lat]), + this.getOption('units'), + ); + const feature = turfCircle([center.lng, center.lat], radius, { + units: this.getOption('units'), + steps: this.getOption('steps'), + properties: { + id: `${CircleFeatureId++}`, + radius, + center, + endPoint, + }, + }); + this.currentFeature = feature as Feature; + return featureCollection([feature]); + } + + private moveCircle(feature: Feature, delta: ILngLat) { + const preCenter = feature?.properties?.center as ILngLat; + const preEndPoint = feature?.properties?.endPoint as ILngLat; + const newCenter = { + lng: preCenter.lng + delta.lng, + lat: preCenter.lat + delta.lat, + }; + const newEndPoint = { + lng: preEndPoint.lng + delta.lng, + lat: preEndPoint.lat + delta.lat, + }; + + const newCircle = this.createCircleData(newCenter, newEndPoint); + // this.centerLayer.setData([newCenter]); + this.editLayer.updateData(newCircle); + } +} diff --git a/packages/draw/src/render.ts b/packages/draw/src/render.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/draw/src/render/edit.ts b/packages/draw/src/render/edit.ts new file mode 100644 index 0000000000..d622f3ad12 --- /dev/null +++ b/packages/draw/src/render/edit.ts @@ -0,0 +1,107 @@ +import { + IInteractionTarget, + ILayer, + ILngLat, + IPopup, + LineLayer, + PointLayer, + PolygonLayer, + Popup, + Scene, +} from '@antv/l7'; +const InitFeature = { + type: 'FeatureCollection', + features: [], +}; +import { Feature } from '@turf/helpers'; +import Draw from '../modes/draw_feature'; +export default class EditRenderLayer { + private polygonLayer: ILayer; + private lineLayer: ILayer; + private centerLayer: ILayer; + private endPointLayer: ILayer; + private draw: Draw; + private currentFeature: Feature; + constructor(draw: Draw) { + this.draw = draw; + this.init(); + } + public init() { + const style = this.draw.getStyle('active_fill'); + const linestyle = this.draw.getStyle('active_line'); + const centerStyle = this.draw.getStyle('active_point'); + this.polygonLayer = new PolygonLayer() + .source(InitFeature) + .shape('fill') + .color(style.color) + .style(style.style); + + this.lineLayer = new LineLayer() + .source(InitFeature) + .shape('line') + .size(linestyle.size) + .color(linestyle.color) + .style(linestyle.style); + this.centerLayer = new PointLayer({ + zIndex: 3, + blend: 'normal', + }) + .source([], { + parser: { + type: 'json', + x: 'lng', + y: 'lat', + }, + }) + .shape('circle') + .color(centerStyle.color) + .size(centerStyle.size) + .style(centerStyle.style); + this.endPointLayer = new PointLayer({ + zIndex: 4, + blend: 'normal', + }) + .source([], { + parser: { + type: 'json', + x: 'lng', + y: 'lat', + }, + }) + .shape('circle') + .color(centerStyle.color) + .size(centerStyle.size) + .style(centerStyle.style); + + this.draw.scene.addLayer(this.polygonLayer); + this.draw.scene.addLayer(this.lineLayer); + this.draw.scene.addLayer(this.centerLayer); + this.draw.scene.addLayer(this.endPointLayer); + this.addLayerEvent(); + } + public updateData(data: any) { + this.currentFeature = data.features[0]; + this.lineLayer.setData(data); + this.polygonLayer.setData(data); + this.centerLayer.setData([data.features[0].properties.center]); + this.endPointLayer.setData([data.features[0].properties.endPoint]); + } + + public destroy() { + this.draw.scene.removeLayer(this.lineLayer); + this.draw.scene.removeLayer(this.polygonLayer); + this.draw.scene.removeLayer(this.centerLayer); + this.draw.scene.removeLayer(this.endPointLayer); + } + + private addLayerEvent() { + this.endPointLayer.on('mousemove', (e) => { + this.draw.setCursor('move'); + this.draw.enable(); + }); + this.endPointLayer.on('unmousemove', (e) => { + this.draw.resetCursor(); + this.draw.disable(); + }); + } +} diff --git a/packages/draw/src/render/render.ts b/packages/draw/src/render/render.ts new file mode 100644 index 0000000000..6c5e77828c --- /dev/null +++ b/packages/draw/src/render/render.ts @@ -0,0 +1,57 @@ +import { + IInteractionTarget, + ILayer, + ILngLat, + IPopup, + LineLayer, + PointLayer, + PolygonLayer, + Popup, + Scene, +} from '@antv/l7'; +const InitFeature = { + type: 'FeatureCollection', + features: [], +}; +import Draw from '../modes/draw_feature'; +export default class RenderLayer { + private polygonLayer: ILayer; + private lineLayer: ILayer; + private draw: Draw; + constructor(draw: Draw) { + this.draw = draw; + this.init(); + } + public init() { + const style = this.draw.getStyle('normal_fill'); + const linestyle = this.draw.getStyle('normal_line'); + this.polygonLayer = new PolygonLayer({ + zIndex: 0, + }) + .source(InitFeature) + .shape('fill') + .active(true) + .color(style.color) + .style(style.style); + + this.lineLayer = new LineLayer({ + zIndex: 1, + }) + .source(InitFeature) + .shape('line') + .size(linestyle.size) + .color(linestyle.color) + .style(linestyle.style); + this.draw.scene.addLayer(this.polygonLayer); + this.draw.scene.addLayer(this.lineLayer); + } + public updateData() { + this.lineLayer.setData(this.draw.source.data); + this.polygonLayer.setData(this.draw.source.data); + } + + public destroy() { + this.draw.scene.removeLayer(this.lineLayer); + this.draw.scene.removeLayer(this.polygonLayer); + } +} diff --git a/packages/draw/src/render/selected.ts b/packages/draw/src/render/selected.ts new file mode 100644 index 0000000000..41c2635c21 --- /dev/null +++ b/packages/draw/src/render/selected.ts @@ -0,0 +1,99 @@ +import { + IInteractionTarget, + ILayer, + ILngLat, + IPopup, + LineLayer, + PointLayer, + PolygonLayer, + Popup, + Scene, +} from '@antv/l7'; +const InitFeature = { + type: 'FeatureCollection', + features: [], +}; +import { Feature } from '@turf/helpers'; +import DrawEdit from '../modes/draw_edit'; +import Draw from '../modes/draw_feature'; +export default class EditRenderLayer { + private polygonLayer: ILayer; + private lineLayer: ILayer; + private centerLayer: ILayer; + private draw: Draw; + private currentFeature: Feature; + constructor(draw: Draw) { + this.draw = draw; + this.init(); + } + public init() { + const style = this.draw.getStyle('active_fill'); + const linestyle = this.draw.getStyle('active_line'); + const centerStyle = this.draw.getStyle('active_point'); + this.polygonLayer = new PolygonLayer() + .source(InitFeature) + .shape('fill') + .color(style.color) + .style(style.style); + + this.lineLayer = new LineLayer() + .source(InitFeature) + .shape('line') + .size(linestyle.size) + .color(linestyle.color) + .style(linestyle.style); + this.centerLayer = new PointLayer({ + zIndex: 3, + blend: 'normal', + }) + .source([], { + parser: { + type: 'json', + x: 'lng', + y: 'lat', + }, + }) + .shape('circle') + .color(centerStyle.color) + .size(centerStyle.size) + .style(centerStyle.style); + this.draw.scene.addLayer(this.polygonLayer); + this.draw.scene.addLayer(this.lineLayer); + this.draw.scene.addLayer(this.centerLayer); + this.addLayerEvent(); + } + public updateData(data: any) { + this.currentFeature = data.features[0]; + this.lineLayer.setData(data); + this.polygonLayer.setData(data); + this.centerLayer.setData([data.features[0].properties.center]); + } + + public destroy() { + this.draw.scene.removeLayer(this.lineLayer); + this.draw.scene.removeLayer(this.polygonLayer); + this.draw.scene.removeLayer(this.centerLayer); + } + + private addLayerEvent() { + this.polygonLayer.on('mousemove', (e) => { + this.draw.setCursor('move'); + this.draw.enable(); + }); + this.polygonLayer.on('unmousemove', (e) => { + this.draw.resetCursor(); + this.draw.disable(); + }); + + this.polygonLayer.on('click', (e) => { + // 进入编辑态 + const drawEdit = new DrawEdit(this.draw.scene, {}); + drawEdit.setEditFeature(this.currentFeature); + this.draw.disable(); + this.destroy(); + }); + this.polygonLayer.on('unclick', (e) => { + // 取消选中 回到初始态 + }); + } +} diff --git a/packages/layers/src/point/index.ts b/packages/layers/src/point/index.ts index 388db517d4..38400fce42 100644 --- a/packages/layers/src/point/index.ts +++ b/packages/layers/src/point/index.ts @@ -30,7 +30,7 @@ export default class PointLayer extends BaseLayer { normal: { blend: 'additive', }, - fill: {}, + fill: { blend: 'normal' }, extrude: {}, image: {}, icon: {}, diff --git a/stories/Draw/Components/Circle.tsx b/stories/Draw/Components/Circle.tsx index ed688cb343..677468fa9b 100644 --- a/stories/Draw/Components/Circle.tsx +++ b/stories/Draw/Components/Circle.tsx @@ -15,9 +15,9 @@ export default class Circle extends React.Component { id: 'map', map: new Mapbox({ pitch: 0, - style: 'normal', - center: [121.775374, 31.31067], - zoom: 15, + style: 'light', + center: [113.775374, 28.31067], + zoom: 12, }), }); this.scene = scene; diff --git a/stories/Layers/components/hexagon.tsx b/stories/Layers/components/hexagon.tsx index 254a5c5393..8eb1c17b32 100644 --- a/stories/Layers/components/hexagon.tsx +++ b/stories/Layers/components/hexagon.tsx @@ -44,7 +44,17 @@ export default class HexagonLayerDemo extends React.Component { ], }) .shape('hexagon') // 支持 circle, hexagon,triangle - .color('mode', ['#ffffe5','#fff7bc','#fee391','#fec44f','#fe9929','#ec7014','#cc4c02','#993404','#662506']) + .color('mode', [ + '#ffffe5', + '#fff7bc', + '#fee391', + '#fec44f', + '#fe9929', + '#ec7014', + '#cc4c02', + '#993404', + '#662506', + ]) .active(false) .style({ coverage: 0.7,