@heycar/heycars-map 0.1.4 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/style.css CHANGED
@@ -1 +1 @@
1
- ._15rq2xn0{display:flex;flex-direction:column;align-items:center;margin-bottom:-1.6vw}._15rq2xn1{padding:2.13vw 4.5vw 2.67vw 4.27vw;display:flex;align-items:center;background:linear-gradient(90deg,#79AFFF 0%,#5C8DFF 32%,#507FFF 75%,#4471FF 100%);border-radius:3.2vw}._15rq2xn2{font-size:3.47vw;font-family:PingFangSC-Semibold,PingFang SC;font-weight:600;color:#fff;line-height:4.8vw;white-space:nowrap}._15rq2xn3{font-size:2.93vw;font-family:PingFangSC-Regular,PingFang SC;font-weight:400;color:#eafaf8;line-height:4.27vw;white-space:nowrap;opacity:.8}._15rq2xn4{margin-left:3.2vw;width:1.44vw;height:2.4vw}._15rq2xn5{width:.8vw;height:4vw;background:#4C80FF;border-radius:0vw 0vw 26.67vw 26.67vw;z-index:1}._15rq2xn6{margin-top:-1.5vw;width:3.2vw;height:3.2vw}.rgf09w0{width:7.1045vw;height:10.93vw;display:flex}._1l6offo0{display:flex;margin-bottom:-8.135vw;width:16.27vw;height:16.27vw}.fhyw80{display:flex;align-items:center}.fhyw81{flex-direction:row-reverse}.fhyw83{margin-right:-2.265vw}.fhyw84{margin-left:-2.265vw}.fhyw85{margin-bottom:-2.265vw}.fhyw86{margin-right:calc(50% - 2.265vw)}.fhyw87{margin-left:calc(50% - 2.265vw)}.fhyw88{display:flex;width:4.53vw;height:4.53vw}.fhyw89{font-size:3.47vw;font-family:PingFangSC-Semibold,PingFang SC;font-weight:600;color:#243e66;line-height:4.53vw;white-space:nowrap}._4a4ovk0{margin-bottom:-.4vw;display:flex;flex-direction:column;align-items:center}._4a4ovk1{padding:1.07vw 2.4vw 1.6vw 2.67vw;background:#FFFFFF;box-shadow:0 .53vw 2.4vw #3f3f3f33;border-radius:2.13vw}._4a4ovk2{padding:2.67vw 2.67vw 2.4vw 3.2vw;background:#FFFFFF;box-shadow:0 0 2.67vw #4e556c26;border-radius:4.8vw;font-size:3.2vw;font-family:PingFangSC-Medium,PingFang SC;font-weight:500;color:#1e1e1e;line-height:4.53vw;white-space:nowrap}._4a4ovk3{font-size:3.2vw;font-family:PingFangSC-Medium,PingFang SC;font-weight:500;color:#1e1e1e;line-height:4.53vw;white-space:nowrap}._4a4ovk4{font-size:2.4vw;font-family:PingFangSC-Regular,PingFang SC;font-weight:400;color:#7e7e7e;line-height:3.47vw;white-space:nowrap}._4a4ovk5{color:#4471ff;font-family:PingFangSC-Medium,PingFang SC;font-weight:500}._4a4ovk6{margin-top:1.33vw;width:6.192vw;height:7.998vw}._4a4ovk7{margin-top:2.4vw;width:6.192vw;height:7.998vw}._65j3sr1{margin-top:1.87vw;margin-bottom:-5.5vw;width:6vw;height:11vw}@keyframes rrvp0y0{0%{transform:scale(0);opacity:1}33.33%{transform:scale(.5);opacity:1}66.66%{transform:scale(1);opacity:0}to{transform:scale(1);opacity:0}}@keyframes rrvp0y1{0%{transform:scale(0);opacity:0}33.33%{transform:scale(0);opacity:1}66.66%{transform:scale(.5);opacity:1}to{transform:scale(1);opacity:0}}.rrvp0y2{display:flex;margin-bottom:-30.935vw;width:61.87vw;height:61.87vw;animation-name:rrvp0y0;animation-duration:3s;animation-iteration-count:infinite;animation-timing-function:linear}.rrvp0y3{animation-name:rrvp0y1}
1
+ .rgf09w0{width:7.1045vw;height:10.93vw;display:flex}._15rq2xn0{display:flex;flex-direction:column;align-items:center;margin-bottom:-1.6vw}._15rq2xn1{padding:2.13vw 4.5vw 2.67vw 4.27vw;display:flex;align-items:center;background:linear-gradient(90deg,#79AFFF 0%,#5C8DFF 32%,#507FFF 75%,#4471FF 100%);border-radius:3.2vw}._15rq2xn2{font-size:3.47vw;font-family:PingFangSC-Semibold,PingFang SC;font-weight:600;color:#fff;line-height:4.8vw;white-space:nowrap}._15rq2xn3{font-size:2.93vw;font-family:PingFangSC-Regular,PingFang SC;font-weight:400;color:#eafaf8;line-height:4.27vw;white-space:nowrap;opacity:.8}._15rq2xn4{margin-left:3.2vw;width:1.44vw;height:2.4vw}._15rq2xn5{width:.8vw;height:4vw;background:#4C80FF;border-radius:0vw 0vw 26.67vw 26.67vw;z-index:1}._15rq2xn6{margin-top:-1.5vw;width:3.2vw;height:3.2vw}._1l6offo0{display:flex;margin-bottom:-8.135vw;width:16.27vw;height:16.27vw}.fhyw80{display:flex;align-items:center}.fhyw81{flex-direction:row-reverse}.fhyw83{margin-right:-2.265vw}.fhyw84{margin-left:-2.265vw}.fhyw85{margin-bottom:-2.265vw}.fhyw86{margin-right:calc(50% - 2.265vw)}.fhyw87{margin-left:calc(50% - 2.265vw)}.fhyw88{display:flex;width:4.53vw;height:4.53vw}.fhyw89{font-size:3.47vw;font-family:PingFangSC-Semibold,PingFang SC;font-weight:600;color:#243e66;line-height:4.53vw;white-space:nowrap}._4a4ovk0{margin-bottom:-.4vw;display:flex;flex-direction:column;align-items:center}._4a4ovk1{padding:1.07vw 2.4vw 1.6vw 2.67vw;background:#FFFFFF;box-shadow:0 .53vw 2.4vw #3f3f3f33;border-radius:2.13vw}._4a4ovk2{padding:2.67vw 2.67vw 2.4vw 3.2vw;background:#FFFFFF;box-shadow:0 0 2.67vw #4e556c26;border-radius:4.8vw;font-size:3.2vw;font-family:PingFangSC-Medium,PingFang SC;font-weight:500;color:#1e1e1e;line-height:4.53vw;white-space:nowrap}._4a4ovk3{font-size:3.2vw;font-family:PingFangSC-Medium,PingFang SC;font-weight:500;color:#1e1e1e;line-height:4.53vw;white-space:nowrap}._4a4ovk4{font-size:2.4vw;font-family:PingFangSC-Regular,PingFang SC;font-weight:400;color:#7e7e7e;line-height:3.47vw;white-space:nowrap}._4a4ovk5{color:#4471ff;font-family:PingFangSC-Medium,PingFang SC;font-weight:500}._4a4ovk6{margin-top:1.33vw;width:6.192vw;height:7.998vw}._4a4ovk7{margin-top:2.4vw;width:6.192vw;height:7.998vw}._65j3sr1{margin-top:1.87vw;margin-bottom:-5.5vw;width:6vw;height:11vw}@keyframes rrvp0y0{0%{transform:scale(0);opacity:1}33.33%{transform:scale(.5);opacity:1}66.66%{transform:scale(1);opacity:0}to{transform:scale(1);opacity:0}}@keyframes rrvp0y1{0%{transform:scale(0);opacity:0}33.33%{transform:scale(0);opacity:1}66.66%{transform:scale(.5);opacity:1}to{transform:scale(1);opacity:0}}.rrvp0y2{display:flex;margin-bottom:-30.935vw;width:61.87vw;height:61.87vw;animation-name:rrvp0y0;animation-duration:3s;animation-iteration-count:infinite;animation-timing-function:linear}.rrvp0y3{animation-name:rrvp0y1}
@@ -2,3 +2,8 @@
2
2
  import type { ShallowRef } from "vue-demi";
3
3
  export type Point = [number, number];
4
4
  export type MapShallowRef<M = AMap.Map | google.maps.Map> = ShallowRef<M | undefined>;
5
+ export type Place = {
6
+ lng: number;
7
+ lat: number;
8
+ name: string;
9
+ };
@@ -1,4 +1,5 @@
1
1
  /// <reference types="google.maps" />
2
+ import type { Place, Point } from "../types/interface";
2
3
  export declare const vec2lnglat: ([lng, lat]: [number, number]) => google.maps.LatLngLiteral;
3
4
  export interface AsteriskItem {
4
5
  type: "normal" | "emphasize";
@@ -9,3 +10,4 @@ export declare const property2emitEventName: (propertyName: string) => string;
9
10
  export declare const property2mapEventName: (propertyName: string) => string;
10
11
  export type VectorMapForeign = "style_en" | "style_local" | "style_zh_cn";
11
12
  export declare const language2vectorMapForeign: (lang: "en" | "zh") => VectorMapForeign;
13
+ export declare const place2point: (place: Place) => Point;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heycar/heycars-map",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "dev": "vite -c vite.config.dev.ts",
@@ -8,9 +8,11 @@ import { createElement, defineSetup } from "../../types/helper";
8
8
  import type { Point } from "../../types/interface";
9
9
  import { createDom } from "../../utils/dom";
10
10
  import { vec2lnglat } from "../../utils/transform";
11
+ import { AAddressLocator, GAddressLocator } from "../AddressLocator";
11
12
  import * as css from "./AddressBox.css";
12
13
 
13
14
  interface AddressBoxProps {
15
+ type: "box" | "locator";
14
16
  position: Point;
15
17
  title: string;
16
18
  description?: string;
@@ -31,9 +33,12 @@ export const AAddressBox = defineSetup(function AAddressBox(props: AddressBoxPro
31
33
  </div>
32
34
  `
33
35
  );
34
- return () => (
35
- <AmapMarker position={props.position} content={contentRef.value} anchor={"bottom-center"} />
36
- );
36
+ return () => {
37
+ if (props.type === "locator") return <AAddressLocator position={props.position} />;
38
+ return (
39
+ <AmapMarker position={props.position} content={contentRef.value} anchor={"bottom-center"} />
40
+ );
41
+ };
37
42
  });
38
43
 
39
44
  export const GAddressBox = defineSetup(function GAddressBox(props: AddressBoxProps) {
@@ -55,6 +60,7 @@ export const GAddressBox = defineSetup(function GAddressBox(props: AddressBoxPro
55
60
  )
56
61
  );
57
62
  return () => {
63
+ if (props.type === "locator") return <GAddressLocator position={props.position} />;
58
64
  return (
59
65
  <GmapAdvancedMarkerView position={vec2lnglat(props.position)} content={contentRef.value} />
60
66
  );
@@ -1,4 +1,4 @@
1
- import type { Ref, ShallowRef, VNodeData } from "vue-demi";
1
+ import { reactive, VNodeData } from "vue-demi";
2
2
  import { useAmap } from "../../components/Amap/useAmap";
3
3
  import { useGmap } from "../../components/Gmap/useGmap";
4
4
  import { useMapSupplier } from "../../components/MapProvider/useMapSupplier";
@@ -11,31 +11,31 @@ import { useAmapAngle, useGmapAngle } from "../../hooks/useMapAngle";
11
11
  import { createElement, defineSetup, VNodeChild } from "../../types/helper";
12
12
  import type { Point } from "../../types/interface";
13
13
 
14
+ export type DrivingRouteRenderProps = {
15
+ path: Point[];
16
+ angle?: number;
17
+ };
14
18
  export interface DrivingRouteProps extends UseDrivingRouteProps {
15
- render?: (props: {
16
- pathRef: ShallowRef<Point[]>;
17
- angleRef: Ref<number | undefined>;
18
- }) => VNodeChild;
19
+ render?: (props: DrivingRouteRenderProps) => VNodeChild;
19
20
  }
20
21
  export const ADrivingRoute = defineSetup(function DrivingRoute(
21
22
  props: DrivingRouteProps,
22
23
  { slots }
23
24
  ) {
24
- const mapRef = useAmap();
25
- const pathRef = useADrivingRoute(props);
26
- const angleRef = useAmapAngle({ pathRef, mapRef });
27
- debugger;
28
- return () => <div id="qyc">{slots.render?.({ pathRef, angleRef })}</div>;
25
+ const map = useAmap();
26
+ const path = useADrivingRoute(props);
27
+ const angle = useAmapAngle(reactive({ path, map }));
28
+ return () => <div>{slots.render?.(reactive({ path, angle }))}</div>;
29
29
  });
30
30
 
31
31
  export const GDrivingRoute = defineSetup(function DrivingRoute(
32
32
  props: DrivingRouteProps,
33
33
  { slots }
34
34
  ) {
35
- const mapRef = useGmap();
36
- const pathRef = useGDrivingRoute(props);
37
- const angleRef = useGmapAngle({ pathRef, mapRef });
38
- return () => <div>{slots.render?.({ pathRef, angleRef })}</div>;
35
+ const map = useGmap();
36
+ const path = useGDrivingRoute(props);
37
+ const angle = useGmapAngle(reactive({ path, map }));
38
+ return () => <div>{slots.render?.(reactive({ path, angle }))}</div>;
39
39
  });
40
40
 
41
41
  export const DrivingRoute = defineSetup(function DrivingRoute(props: DrivingRouteProps, { slots }) {
@@ -1,8 +1,12 @@
1
+ import { AddressBox } from "../../business-components/AddressBox";
1
2
  import { DrivingLine } from "../../business-components/DrivingLine";
2
3
  import { DrivingRoute } from "../../business-components/DrivingRoute";
3
4
  import { TaxiCar } from "../../business-components/TaxiCar";
5
+ import {
6
+ useBusinessMapRecomendPlace,
7
+ UseBusinessMapRecomendPlaceProps,
8
+ } from "../../hooks-business/useBusinessMapRecomendPlace";
4
9
  import { useHeycarMap } from "../../hooks/useHeycarMap";
5
- import { useMapDrag } from "../../hooks/useMapDrag";
6
10
  import { defineSetup } from "../../types/helper";
7
11
  import { HeycarMap, MapProvider } from "../MapProvider/MapProvider";
8
12
  import * as css from "./Demo.css";
@@ -28,22 +32,42 @@ export const HeycarDemo = defineSetup(function HeycarDemo() {
28
32
 
29
33
  export const SomePage = defineSetup(function Hello() {
30
34
  const { mapRef, setMap, panTo } = useHeycarMap();
31
- const { centerPlace, isDragging } = useMapDrag(mapRef);
35
+ const getRecomendPlace: UseBusinessMapRecomendPlaceProps["getRecomendPlace"] = async (place) => {
36
+ const { lat, lng } = place;
37
+ console.log("typeof lat = ", typeof lat);
38
+ return [{ lat: lat + 0.0001, lng: lng + 0.0001, name: "place name 1" }];
39
+ };
40
+ const { centerPlace, centerPoint, isDragging, isGeoLoading, geoError } =
41
+ useBusinessMapRecomendPlace({
42
+ mapRef,
43
+ recomendPlaceGeoLimit: 300,
44
+ recomendPlaceDragLimit: 30,
45
+ getRecomendPlace,
46
+ onChangeGeoLocation: (v) => console.log("onChangeGeoLocation: ", v),
47
+ onChangePlace: (v) => console.log("onChangePlace: ", v),
48
+ onChangeRecomandPlace: (v) => console.log("onChangeRecomandPlace: ", v),
49
+ });
32
50
  (window as any).panTo = panTo;
33
51
  return () => {
34
52
  return (
35
53
  <div>
54
+ isGeoLoading: {`${isGeoLoading.value}`} <br />
36
55
  isDragging: {`${isDragging.value}`} <br />
37
- {centerPlace.lng} | {centerPlace.lat} | {centerPlace.name}
56
+ {centerPlace.lng} | {centerPlace.lat} | {centerPlace.name} <br />
38
57
  <HeycarMap
39
58
  class={css.demo}
40
59
  fallback={() => <div>error</div>}
41
60
  loading={() => <div>loading</div>}
42
- center={[121, 31]}
61
+ center={[centerPlace.lng, centerPlace.lat]}
43
62
  zoom={15}
44
63
  mapRef={setMap}
45
64
  >
46
- {/* <AddressBox position={[0, 0]} title={"Martyrs Lawn"} description={"您将在此处上车"} /> */}
65
+ <AddressBox
66
+ type="box"
67
+ position={centerPoint.value}
68
+ title={"Martyrs Lawn"}
69
+ description={"您将在此处上车"}
70
+ />
47
71
  {/* <TaxiCar position={[121, 31]} angle={0} /> */}
48
72
  {/* <PickupPoints
49
73
  positions={[
@@ -55,9 +79,9 @@ export const SomePage = defineSetup(function Hello() {
55
79
  <DrivingRoute
56
80
  from={[121, 31]}
57
81
  to={[121.6, 31.3]}
58
- render={({ pathRef, angleRef }) => [
59
- pathRef.value.length > 0 && <DrivingLine path={pathRef.value} />,
60
- <TaxiCar position={[121, 31]} angle={angleRef.value} />,
82
+ render={({ path, angle }) => [
83
+ <DrivingLine path={path} />,
84
+ <TaxiCar position={[121, 31]} angle={angle} />,
61
85
  ]}
62
86
  />
63
87
  </HeycarMap>
@@ -1,6 +1,7 @@
1
- import { ShallowRef, shallowRef, watch } from "vue-demi";
1
+ import { ShallowRef, shallowRef } from "vue-demi";
2
2
  import { useMapSupplier } from "../components/MapProvider/useMapSupplier";
3
3
  import type { Point } from "../types/interface";
4
+ import { watchPostEffectForDeepOption } from "../utils/compare";
4
5
 
5
6
  export interface UseDrivingRouteProps {
6
7
  from: Point;
@@ -10,9 +11,9 @@ export interface UseDrivingRouteProps {
10
11
  export const useADrivingRoute = (props: UseDrivingRouteProps) => {
11
12
  const pathRef = shallowRef<Point[]>([]);
12
13
  const amapDriving = new AMap.Driving({});
13
- watch(
14
- [() => props.from, () => props.to],
15
- ([from, to]) => {
14
+ watchPostEffectForDeepOption(
15
+ () => ({ ...props }),
16
+ ({ from, to }) => {
16
17
  amapDriving.search(
17
18
  AMap.LngLat.from(from),
18
19
  AMap.LngLat.from(to),
@@ -39,8 +40,7 @@ export const useADrivingRoute = (props: UseDrivingRouteProps) => {
39
40
  }
40
41
  }
41
42
  );
42
- },
43
- { immediate: true }
43
+ }
44
44
  );
45
45
  return pathRef;
46
46
  };
@@ -48,22 +48,25 @@ export const useADrivingRoute = (props: UseDrivingRouteProps) => {
48
48
  export const useGDrivingRoute = (props: UseDrivingRouteProps) => {
49
49
  const pathRef = shallowRef<Point[]>([]);
50
50
  const gmapDirectionsService = new google.maps.DirectionsService();
51
- watch([() => props.from, () => props.to], ([from, to]) => {
52
- gmapDirectionsService
53
- .route({
54
- origin: { lng: from[0], lat: from[1] },
55
- destination: { lng: to[0], lat: to[1] },
56
- travelMode: google.maps.TravelMode.WALKING,
57
- })
58
- .then((result) => {
59
- let path: Point[] = [];
60
- result.routes.map((route) => {
61
- const stepPath = route.overview_path.map<Point>((item) => [item.lng(), item.lat()]);
62
- path = path.concat(stepPath);
51
+ watchPostEffectForDeepOption(
52
+ () => ({ ...props }),
53
+ ({ from, to }) => {
54
+ gmapDirectionsService
55
+ .route({
56
+ origin: { lng: from[0], lat: from[1] },
57
+ destination: { lng: to[0], lat: to[1] },
58
+ travelMode: google.maps.TravelMode.WALKING,
59
+ })
60
+ .then((result) => {
61
+ let path: Point[] = [];
62
+ result.routes.map((route) => {
63
+ const stepPath = route.overview_path.map<Point>((item) => [item.lng(), item.lat()]);
64
+ path = path.concat(stepPath);
65
+ });
66
+ pathRef.value = path;
63
67
  });
64
- pathRef.value = path;
65
- });
66
- });
68
+ }
69
+ );
67
70
  return pathRef;
68
71
  };
69
72
 
@@ -0,0 +1,44 @@
1
+ import { ref, watchEffect } from "vue-demi";
2
+ import type { MapShallowRef, Point } from "../types/interface";
3
+
4
+ export interface UseGeoLocationProps {
5
+ mapRef: MapShallowRef;
6
+ onChange?: (value: { position: Point; coordinate: GeolocationCoordinates }) => any;
7
+ }
8
+ export const useGeoLocation = (props: UseGeoLocationProps) => {
9
+ const { mapRef, onChange } = props;
10
+ const loading = ref(false);
11
+ const errorRef = ref<GeolocationPositionError>();
12
+ const coordinateRef = ref<GeolocationCoordinates>({
13
+ accuracy: 0,
14
+ altitude: 0,
15
+ altitudeAccuracy: 0,
16
+ heading: 0,
17
+ latitude: 0,
18
+ longitude: 0,
19
+ speed: 0,
20
+ });
21
+ const positionRef = ref<Point>([coordinateRef.value.longitude, coordinateRef.value.latitude]);
22
+ watchEffect(() => {
23
+ mapRef.value;
24
+ loading.value = true;
25
+ navigator.geolocation.getCurrentPosition(
26
+ (position) => {
27
+ loading.value = false;
28
+ const coordinate = position.coords;
29
+ const point: Point = [position.coords.longitude, position.coords.latitude];
30
+ coordinateRef.value = coordinate;
31
+ positionRef.value = point;
32
+ onChange?.({ position: point, coordinate });
33
+ },
34
+ (error) => {
35
+ loading.value = false;
36
+ errorRef.value = error;
37
+ console.log("errorRef = ", errorRef);
38
+ },
39
+
40
+ { timeout: 5000 }
41
+ );
42
+ });
43
+ return { errorRef, positionRef, coordinateRef, loading };
44
+ };
@@ -1,35 +1,34 @@
1
- import { computed, ShallowRef } from "vue";
1
+ import { computed } from "vue-demi";
2
2
  import { useMapSupplier } from "../components/MapProvider/useMapSupplier";
3
- import type { MapShallowRef, Point } from "../types/interface";
3
+ import type { Point } from "../types/interface";
4
4
  import { vec2lnglat } from "../utils/transform";
5
5
 
6
6
  const amapComputeHeading = (from: Point, to: Point, map?: AMap.Map) => {
7
7
  if (!map) return 0;
8
8
  const { x: xFrom, y: yFrom } = map.lngLatToContainer(from);
9
9
  const { x: xTo, y: yTo } = map.lngLatToContainer(to);
10
- console.log("yTo - yFrom, xTo - xFrom = ", yTo - yFrom, xTo - xFrom);
11
10
  const theta = Math.atan2(yFrom - yTo, xTo - xFrom);
12
11
  return ((Math.PI / 2 - theta) / Math.PI) * 180;
13
12
  };
14
13
 
15
- export interface UseMapAngleProps<T = AMap.Map | google.maps.Map> {
16
- pathRef: ShallowRef<Point[]>;
17
- mapRef?: MapShallowRef<T>;
18
- }
14
+ export type UseMapAngleProps<M = AMap.Map | google.maps.Map> = {
15
+ path: Point[];
16
+ map?: M;
17
+ };
19
18
 
20
19
  export const useAmapAngle = (props: UseMapAngleProps<AMap.Map>) => {
21
20
  return computed(() => {
22
- const { mapRef, pathRef } = props;
23
- const [from, to] = pathRef.value;
24
- if (!from || !to || !mapRef) return undefined;
25
- return amapComputeHeading(from, to, mapRef.value);
21
+ const { map, path } = props;
22
+ const [from, to] = path;
23
+ if (!from || !to || !map) return undefined;
24
+ return amapComputeHeading(from, to, map);
26
25
  });
27
26
  };
28
27
 
29
28
  export const useGmapAngle = (props: UseMapAngleProps<google.maps.Map>) => {
30
29
  return computed(() => {
31
- const { pathRef } = props;
32
- const [from, to] = pathRef.value;
30
+ const { path } = props;
31
+ const [from, to] = path;
33
32
  if (!from || !to) return undefined;
34
33
  return google.maps.geometry.spherical.computeHeading(vec2lnglat(from), vec2lnglat(to));
35
34
  });
@@ -1,81 +1,67 @@
1
- import { reactive, ref, type ShallowRef } from "vue-demi";
1
+ import { ref, type ShallowRef } from "vue-demi";
2
2
  import type { AmapProps } from "../components/Amap";
3
3
  import type { GmapProps } from "../components/Gmap";
4
4
  import { useMapSupplier } from "../components/MapProvider/useMapSupplier";
5
- import { LANGUAGE, SetupContextEmitHandler } from "../types/helper";
6
- import type { MapShallowRef } from "../types/interface";
5
+ import type { SetupContextEmitHandler } from "../types/helper";
6
+ import type { Point } from "../types/interface";
7
7
  import { watchPostEffectForAMapEvent, watchPostEffectForGMapEvent } from "../utils/compare";
8
8
 
9
9
  export const ON_CHANGE_CENTER_INTERVAL = 200;
10
- export interface UseMapCenterPlace {
11
- lng: number;
12
- lat: number;
13
- name: string;
10
+
11
+ export interface UseMapDragProps<M = AMap.Map | google.maps.Map> {
12
+ mapRef: ShallowRef<M | undefined>;
13
+ onChange?: (value: Point) => any;
14
14
  }
15
- export const useAmapDrag = (amapRef: ShallowRef<AMap.Map | undefined>) => {
16
- const centerPlace = reactive<UseMapCenterPlace>({ lng: 0, lat: 0, name: "" });
15
+ export const useAmapDrag = (props: UseMapDragProps<AMap.Map>) => {
16
+ const { mapRef, onChange } = props;
17
+ const centerRef = ref<Point>([0, 0]);
17
18
  const isDragging = ref(false);
18
19
  const emitDragStart: SetupContextEmitHandler<Pick<AmapProps, "onDragStart">> = () => {
19
20
  Promise.resolve().then(() => (isDragging.value = true));
20
21
  };
21
22
  const emitDragEnd: SetupContextEmitHandler<Pick<AmapProps, "onDragEnd">> = (_, { target }) => {
22
23
  isDragging.value = false;
23
- const center = target.getCenter();
24
- centerPlace.lng = center.lng;
25
- centerPlace.lat = center.lat;
26
- const geocoder = new AMap.Geocoder({ lang: LANGUAGE });
27
- geocoder.getAddress(center, (status, result) => {
28
- switch (status) {
29
- case "complete":
30
- centerPlace.name = (result as AMap.ReGeocoderResult).regeocode.formattedAddress;
31
- return;
32
- case "no_data":
33
- return;
34
- case "error":
35
- throw result;
36
- }
37
- });
24
+ const { lng, lat } = target.getCenter();
25
+ centerRef.value = [lng, lat];
26
+ onChange?.([lng, lat]);
38
27
  };
39
- watchPostEffectForAMapEvent(amapRef, {} as Pick<AmapProps, "onDragStart">, emitDragStart, [
28
+ watchPostEffectForAMapEvent(mapRef, {} as Pick<AmapProps, "onDragStart">, emitDragStart, [
40
29
  "onDragStart",
41
30
  ]);
42
- watchPostEffectForAMapEvent(amapRef, {} as Pick<AmapProps, "onDragEnd">, emitDragEnd, [
31
+ watchPostEffectForAMapEvent(mapRef, {} as Pick<AmapProps, "onDragEnd">, emitDragEnd, [
43
32
  "onDragEnd",
44
33
  ]);
45
- return { centerPlace, isDragging };
34
+ return { centerRef, isDragging };
46
35
  };
47
36
 
48
- export const useGmapDrag = (gmapRef: ShallowRef<google.maps.Map | undefined>) => {
49
- const centerPlace = reactive<UseMapCenterPlace>({ lng: 0, lat: 0, name: "" });
37
+ export const useGmapDrag = (props: UseMapDragProps<google.maps.Map>) => {
38
+ const { mapRef, onChange } = props;
39
+ const centerRef = ref<Point>([0, 0]);
50
40
  const isDragging = ref(false);
51
41
  const emitDragStart: SetupContextEmitHandler<Pick<GmapProps, "onDragStart">> = () => {
52
42
  Promise.resolve().then(() => (isDragging.value = true));
53
43
  };
54
44
  const emitDragEnd: SetupContextEmitHandler<Pick<GmapProps, "onDragEnd">> = async () => {
55
45
  isDragging.value = false;
56
- const center = gmapRef.value?.getCenter();
57
- if (!center) return;
58
- const lng = center?.lng();
59
- const lat = center?.lat();
60
- if (lng) centerPlace.lng = lng;
61
- if (lat) centerPlace.lat = lat;
62
- const geocoder = new google.maps.Geocoder();
63
- const { results } = await geocoder.geocode({ language: LANGUAGE, location: center });
64
- centerPlace.name = results[0].formatted_address;
46
+ const center = mapRef.value?.getCenter();
47
+ const lng = center?.lng() as number;
48
+ const lat = center?.lat() as number;
49
+ centerRef.value = [lng, lat];
50
+ onChange?.([lng, lat]);
65
51
  };
66
- watchPostEffectForGMapEvent(gmapRef!, {} as Pick<GmapProps, "onDragStart">, emitDragStart, [
52
+ watchPostEffectForGMapEvent(mapRef!, {} as Pick<GmapProps, "onDragStart">, emitDragStart, [
67
53
  "onDragStart",
68
54
  ]);
69
- watchPostEffectForGMapEvent(gmapRef!, {} as Pick<GmapProps, "onDragEnd">, emitDragEnd, [
55
+ watchPostEffectForGMapEvent(mapRef!, {} as Pick<GmapProps, "onDragEnd">, emitDragEnd, [
70
56
  "onDragEnd",
71
57
  ]);
72
- return { centerPlace, isDragging };
58
+ return { centerRef, isDragging };
73
59
  };
74
60
 
75
- export const useMapDrag = (mapRef: MapShallowRef) => {
61
+ export const useMapDrag = (props: UseMapDragProps) => {
76
62
  const { supplier } = useMapSupplier();
77
63
  // todo: 这里违反 react hook 规则,但 react 版本应该可以同时调用 useAmap 和 useGmap
78
64
  return supplier === "gmap"
79
- ? useGmapDrag(mapRef as ShallowRef<google.maps.Map | undefined>)
80
- : useAmapDrag(mapRef as ShallowRef<AMap.Map | undefined>);
65
+ ? useGmapDrag(props as UseMapDragProps<google.maps.Map>)
66
+ : useAmapDrag(props as UseMapDragProps<AMap.Map>);
81
67
  };
@@ -0,0 +1,66 @@
1
+ import { reactive, Ref, watchEffect } from "vue-demi";
2
+ import { useMapSupplier } from "../components/MapProvider/useMapSupplier";
3
+ import { LANGUAGE } from "../types/helper";
4
+ import type { MapShallowRef, Place, Point } from "../types/interface";
5
+ import { useMapReady } from "./useMapReady";
6
+
7
+ export interface UseMapPlaceProps<M = AMap.Map | google.maps.Map> {
8
+ pointRef: Ref<Point>;
9
+ mapRef: MapShallowRef<M>;
10
+ onChange?: (value: Place) => any;
11
+ }
12
+ export const useAmapPlace = (props: UseMapPlaceProps<AMap.Map>) => {
13
+ const { mapRef, pointRef, onChange } = props;
14
+ const { readyRef } = useMapReady(mapRef);
15
+ const place = reactive<Place>({ lng: pointRef.value[0], lat: pointRef.value[1], name: "" });
16
+ watchEffect(() => {
17
+ if (!readyRef.value) return;
18
+ const geocoder = new AMap.Geocoder({ lang: LANGUAGE });
19
+ const [lng, lat] = pointRef.value;
20
+ place.lng = lng;
21
+ place.lat = lat;
22
+ geocoder.getAddress(new AMap.LngLat(lng, lat), (status, result) => {
23
+ switch (status) {
24
+ case "complete": {
25
+ const name = (result as AMap.ReGeocoderResult).regeocode.formattedAddress;
26
+ place.name = name;
27
+ onChange?.({ lng, lat, name });
28
+ return;
29
+ }
30
+
31
+ case "no_data":
32
+ place.name = "";
33
+ onChange?.({ lng, lat, name: "" });
34
+ return;
35
+ case "error":
36
+ throw result;
37
+ }
38
+ });
39
+ });
40
+ return place;
41
+ };
42
+
43
+ export const useGmapPlace = (props: UseMapPlaceProps<google.maps.Map>) => {
44
+ const { mapRef, pointRef, onChange } = props;
45
+ const { readyRef } = useMapReady(mapRef);
46
+ const place = reactive<Place>({ lng: pointRef.value[0], lat: pointRef.value[1], name: "" });
47
+ watchEffect(async () => {
48
+ if (!readyRef.value) return;
49
+ const geocoder = new google.maps.Geocoder();
50
+ const [lng, lat] = pointRef.value;
51
+ place.lng = lng;
52
+ place.lat = lat;
53
+ const { results } = await geocoder.geocode({ language: LANGUAGE, location: { lng, lat } });
54
+ const name = results[0].formatted_address;
55
+ place.name = name;
56
+ onChange?.({ lng, lat, name });
57
+ });
58
+ return place;
59
+ };
60
+
61
+ export const useMapPlace = (props: UseMapPlaceProps) => {
62
+ const { supplier } = useMapSupplier();
63
+ return supplier === "gmap"
64
+ ? useGmapPlace(props as UseMapPlaceProps<google.maps.Map>)
65
+ : useAmapPlace(props as UseMapPlaceProps<AMap.Map>);
66
+ };
@@ -0,0 +1,14 @@
1
+ import { ref, watch } from "vue-demi";
2
+ import type { MapShallowRef } from "../types/interface";
3
+
4
+ export const useMapReady = (mapRef: MapShallowRef) => {
5
+ const readyRef = ref(!!mapRef.value);
6
+ watch(
7
+ () => mapRef.value,
8
+ (map) => {
9
+ if (!map) return;
10
+ readyRef.value = true;
11
+ }
12
+ );
13
+ return { readyRef };
14
+ };
@@ -0,0 +1,78 @@
1
+ import { reactive, Ref, watchEffect } from "vue-demi";
2
+ import { useMapSupplier } from "../components/MapProvider/useMapSupplier";
3
+ import type { MapShallowRef, Place, Point } from "../types/interface";
4
+ import { place2point } from "../utils/transform";
5
+ import { useAmapPlace, useGmapPlace, UseMapPlaceProps } from "./useMapPlace";
6
+ import { useMapReady } from "./useMapReady";
7
+
8
+ const findAmapNearestPlace = (place: Place, candidates: Place[]) => {
9
+ let shortestDistance = Infinity;
10
+ let shortestPlace = place;
11
+ for (const candidate of candidates) {
12
+ const distance = AMap.GeometryUtil.distance(place2point(place), place2point(candidate));
13
+ if (distance >= shortestDistance) continue;
14
+ shortestPlace = candidate;
15
+ shortestDistance = distance;
16
+ }
17
+ return { shortestPlace, shortestDistance };
18
+ };
19
+
20
+ const findGmapNearestPlace = (place: Place, candidates: Place[]) => {
21
+ let shortestDistance = Infinity;
22
+ let shortestPlace = place;
23
+ for (const candidate of candidates) {
24
+ const distance = google.maps.geometry.spherical.computeDistanceBetween(place, candidate);
25
+ if (distance >= shortestDistance) continue;
26
+ shortestPlace = candidate;
27
+ shortestDistance = distance;
28
+ }
29
+ return { shortestPlace, shortestDistance };
30
+ };
31
+
32
+ export interface UseMapRecomendPlaceProps<M = AMap.Map | google.maps.Map> {
33
+ mapRef: MapShallowRef<M>;
34
+ pointRef: Ref<Point>;
35
+ limit: number;
36
+ getRecomendPlace: (place: Place) => Promise<Place[]>;
37
+ onChangePlace?: UseMapPlaceProps["onChange"];
38
+ onChange?: (place: Place) => any;
39
+ }
40
+ export const useAmapRecomendPlace = (props: UseMapRecomendPlaceProps<AMap.Map>) => {
41
+ const { pointRef, mapRef, getRecomendPlace, limit, onChange, onChangePlace } = props;
42
+ const { readyRef } = useMapReady(mapRef);
43
+ const place = useAmapPlace({ pointRef, mapRef, onChange: onChangePlace });
44
+ const recomendPlace = reactive<Place>({ ...place });
45
+ watchEffect(async () => {
46
+ if (!readyRef.value) return;
47
+ const placeCandidates = await getRecomendPlace({ ...place });
48
+ const { shortestPlace, shortestDistance } = findAmapNearestPlace(place, placeCandidates);
49
+ console.log("shortestPlace, shortestDistance = ", shortestPlace, shortestDistance);
50
+ const resultPlace = shortestDistance > limit ? place : shortestPlace;
51
+ Object.assign(recomendPlace, { ...resultPlace });
52
+ onChange?.({ ...resultPlace });
53
+ });
54
+ return recomendPlace;
55
+ };
56
+
57
+ export const useGmapRecomendPlace = (props: UseMapRecomendPlaceProps<google.maps.Map>) => {
58
+ const { pointRef, mapRef, getRecomendPlace, limit, onChange, onChangePlace } = props;
59
+ const { readyRef } = useMapReady(mapRef);
60
+ const place = useGmapPlace({ pointRef, mapRef, onChange: onChangePlace });
61
+ const recomendPlace = reactive<Place>({ ...place });
62
+ watchEffect(async () => {
63
+ if (!readyRef.value) return;
64
+ const placeCandidates = await getRecomendPlace({ ...place });
65
+ const { shortestPlace, shortestDistance } = findGmapNearestPlace(place, placeCandidates);
66
+ const resultPlace = shortestDistance > limit ? place : shortestPlace;
67
+ Object.assign(recomendPlace, { ...resultPlace });
68
+ onChange?.({ ...resultPlace });
69
+ });
70
+ return recomendPlace;
71
+ };
72
+
73
+ export const useMapRecomendPlace = (props: UseMapRecomendPlaceProps) => {
74
+ const { supplier } = useMapSupplier();
75
+ return supplier === "gmap"
76
+ ? useGmapRecomendPlace(props as UseMapRecomendPlaceProps<google.maps.Map>)
77
+ : useAmapRecomendPlace(props as UseMapRecomendPlaceProps<AMap.Map>);
78
+ };