Browse Source

矩形支持自定义背景图

master
Cmen 3 years ago
parent
commit
0a7a9752be
  1. 43
      src/editor/Property/OverlayBackground.tsx
  2. 19
      src/editor/Property/index.less
  3. 18
      src/editor/Property/index.tsx
  4. 63
      src/map/MapEditor.ts
  5. 1
      src/map/type.ts
  6. 7
      src/store/actions/index.ts
  7. 5
      src/store/reducers/map/index.ts

43
src/editor/Property/OverlayBackground.tsx

@ -0,0 +1,43 @@
import { CSSProperties } from "react";
import { Upload } from "antd";
import { IOverlay } from "@map";
import { editorAction } from "@store";
export type OverlayBackgroundProps = {
overlay: IOverlay;
};
export const OverlayBackground = (props: OverlayBackgroundProps) => {
const { overlay } = props;
const { id, backgroundImage } = overlay;
const beforeUpload = (file: File) => {
const reader = new FileReader();
reader.onload = () => {
editorAction.updateOverlay({
id,
backgroundImage: reader.result as string,
});
};
reader.readAsDataURL(file);
return false;
};
const style: CSSProperties = {};
if (backgroundImage) {
style.backgroundImage = `url(${backgroundImage})`;
}
return (
<div style={style} className="overlay-background">
<Upload
showUploadList={false}
className="overlay-background-upload"
accept="image/png, image/jpg"
beforeUpload={beforeUpload}
>
</Upload>
</div>
);
};

19
src/editor/Property/index.less

@ -1,3 +1,22 @@
.property-area{
padding: 8px;
}
.overlay-background{
background-size: cover;
}
.overlay-background-upload{
display: inline-block;
width: 100%;
height: 120px;
line-height: 120px;
text-align: center;
vertical-align: middle;
background-color: rgba(0, 0, 0, .2);
border: 1px dashed #d9d9d9;
border-radius: 2px;
cursor: pointer;
transition: border-color .3s;
}
.ant-form-item .ant-upload{
color: #fff;
}

18
src/editor/Property/index.tsx

@ -1,11 +1,12 @@
import { useDispatch, useSelector } from "react-redux";
import { Form, Input, Select } from "antd";
import { selectedIdsSelector, mapOptionsSelector, StoreAction } from "@store";
import { useSelector } from "react-redux";
import { Form, Select } from "antd";
import { selectedIdsSelector, mapOptionsSelector, editorAction } from "@store";
import { OverlayCategory, IOverlay } from "@map";
import { OverlayTypes } from "@types";
import { FineInput } from "./FineInput";
import { OverlayBackground } from "./OverlayBackground";
import "./index.less";
@ -19,14 +20,12 @@ const { Option } = Select;
const Property = () => {
const selectedIds = useSelector(selectedIdsSelector);
const { overlays } = useSelector(mapOptionsSelector);
const dispatch = useDispatch();
if (!selectedIds?.length) return <></>;
const [id] = selectedIds;
const overlay = overlays.find((overlay) => overlay.id === id)!;
const updateOverlayProps = (props: Partial<IOverlay>) => {
dispatch(StoreAction.updateOverlayProps(props));
};
const updateOverlayProps = (props: Partial<IOverlay>) =>
editorAction.updateOverlay(props);
const categorys: CategoryItem[] = [];
if (overlay.type === OverlayTypes.Polyline) {
@ -83,6 +82,11 @@ const Property = () => {
/>
</Form.Item>
) : null}
{overlay.type === OverlayTypes.Rectangle ? (
<Form.Item label="背景">
<OverlayBackground overlay={overlay} />
</Form.Item>
) : null}
</Form>
</div>
);

63
src/map/MapEditor.ts

@ -11,7 +11,7 @@ import {
CircleEditor,
} from "./editors";
import { IMapOptions, IMapEditor } from "./type";
import { IMapOptions, IMapEditor, IOverlay } from "./type";
import { PolygonOptions, PolylineOptions, SelectedOptions } from "./constants";
@ -35,6 +35,7 @@ export class MapEditor extends Emitter implements IMapEditor {
private _map: AMap.Map | undefined;
private overlayEditors: BaseOverlayEditor<AMapOverlayEditor>[] = [];
private overlayMap: Record<string, OverlayTemp> = {};
private imageLayerMap: Record<string, AMap.ImageLayer> = {};
private currentOverlayEditor:
| BaseOverlayEditor<AMapOverlayEditor>
| undefined;
@ -122,14 +123,14 @@ export class MapEditor extends Emitter implements IMapEditor {
overlays.forEach((overlay) => {
const { type, id } = overlay;
const target = overlayEditors[type].build(overlay);
this.overlayMap[id] = {
type,
target,
};
this._addOverlay(id, { type, target });
target.setOptions(
type === OverlayTypes.Polyline ? PolylineOptions : PolygonOptions
);
this.map.add(target);
if (overlay.backgroundImage) {
this.updateOverlayBackground(id, overlay.backgroundImage);
}
});
}
@ -146,6 +147,28 @@ export class MapEditor extends Emitter implements IMapEditor {
this.selectedIds = [];
}
updateOverlayBackground(id: string, src?: string) {
if (this.overlayMap[id] == null) return;
const overlay = this.overlayMap[id];
// 只支持矩形
if (overlay.type !== OverlayTypes.Rectangle) return;
const rect = overlay.target as AMap.Rectangle;
const rectBounds = rect.getBounds()!;
if (this.imageLayerMap[id] == null && src) {
const { southWest, northEast } = rectBounds;
this.imageLayerMap[id] = new AMap.ImageLayer({
url: src,
// 居然不一样.....
bounds: [southWest.lng, southWest.lat, northEast.lng, northEast.lat],
zIndex: 0,
});
this.map.add(this.imageLayerMap[id]);
}
this.imageLayerMap[id]?.setBounds(rectBounds);
src && this.imageLayerMap[id]?.setImageUrl(src);
}
createOverlay(type: OverlayTypes) {
this.currentOverlayEditor = this.getEditorByType(type!);
this.currentOverlayEditor?.create();
@ -171,6 +194,10 @@ export class MapEditor extends Emitter implements IMapEditor {
const { target } = this.overlayMap[id];
this.map.remove(target);
delete this.overlayMap[id];
if (this.imageLayerMap[id]) {
this.map.remove(this.imageLayerMap[id]);
delete this.imageLayerMap[id];
}
});
this.selectedIds = [];
}
@ -183,10 +210,20 @@ export class MapEditor extends Emitter implements IMapEditor {
const isCreatingOverlay = this.editorStatus === "creating";
let id = getUID();
if (isCreatingOverlay) {
this.overlayMap[id] = { type, target };
this._addOverlay(id, { type, target });
} else {
id = this.selectedIds[0];
}
this._updateOverlay(id);
target.setOptions(
type === OverlayTypes.Polyline ? PolylineOptions : PolygonOptions
);
this.editorStatus = null;
}
_updateOverlay(id: string) {
if (this.overlayMap[id] == null) return;
const { type, target } = this.overlayMap[id];
const evt: any = { id, type };
if (type === OverlayTypes.Circle) {
const circle = target as AMap.Circle;
@ -196,11 +233,8 @@ export class MapEditor extends Emitter implements IMapEditor {
} else {
evt.path = getOverlayPaths(target, type);
}
target.setOptions(
type === OverlayTypes.Polyline ? PolylineOptions : PolygonOptions
);
this.emit(EventTypes.FinishEditOverlay, evt);
this.editorStatus = null;
this.updateOverlayBackground(id);
}
editSelectedOverlay() {
@ -211,4 +245,13 @@ export class MapEditor extends Emitter implements IMapEditor {
this.currentOverlayEditor?.edit(target);
this.editorStatus = "editing";
}
onOverlayDragEnd(id: string) {
this._updateOverlay(id);
}
_addOverlay(id: string, overlay: OverlayTemp) {
this.overlayMap[id] = overlay;
overlay.target.on("dragend", () => this.onOverlayDragEnd(id));
}
}

1
src/map/type.ts

@ -11,6 +11,7 @@ export interface IMapEditor {
clearOverlays(): void;
getCenter(): GeoJSON.Position;
getZoom(): number;
updateOverlayBackground(id: string, src?: string): void;
}
export enum OverlayCategory {

7
src/store/actions/index.ts

@ -94,6 +94,13 @@ export class EditorAction {
}
}
updateOverlay(props: Partial<IOverlay>) {
this.dispatch(StoreAction.updateOverlayProps(props));
if (props.backgroundImage && props.id) {
this.mapEditor?.updateOverlayBackground(props.id, props.backgroundImage);
}
}
saveGeoJSON() {
const geojson = getGeoJSON(this.mapOptions);
downloadJSON(geojson, "fine.geojson");

5
src/store/reducers/map/index.ts

@ -35,7 +35,10 @@ export function finishEditOverlay(state = initState, payload: any) {
const { overlays } = draft.map;
const existed = overlays.find((overlay) => overlay.id === id);
if (existed) {
overlays[overlays.indexOf(existed)] = overlay;
overlays[overlays.indexOf(existed)] = {
...existed,
...overlay,
};
} else {
overlays.push(overlay);
}

Loading…
Cancel
Save