@heycar/heycars-map 0.3.4 → 0.3.6
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/business-components/AbsoluteAddressBox/AbsoluteAddressBox.d.ts +1 -1
- package/dist/business-components/PickupPoints/PickupPoints.d.ts +2 -1
- package/dist/hooks/useMapLngLatToVw.d.ts +10 -0
- package/dist/hooks/useMapRecomendPlace.d.ts +3 -0
- package/dist/hooks/usePlacesLabelDirection.d.ts +9 -0
- package/dist/hooks/useWaitTimer.d.ts +12 -0
- package/dist/index.cjs +53 -52
- package/dist/index.js +1101 -996
- package/dist/style.css +1 -1
- package/dist/utils/transform.d.ts +2 -0
- package/package.json +1 -1
- package/src/Demo/DemoBusinessRecomendPlace.tsx +8 -3
- package/src/business-components/AbsoluteAddressBox/AbsoluteAddressBox.tsx +13 -2
- package/src/business-components/BusinessRecomendPlaceMap/BusinessRecomendPlaceMap.tsx +21 -8
- package/src/business-components/PickupPoints/PickupPoints.tsx +24 -11
- package/src/business-components/PlaceCircle/PlaceCircle.css.ts +7 -2
- package/src/hooks/useGeoLocation.ts +3 -2
- package/src/hooks/useMapDrag.ts +0 -1
- package/src/hooks/useMapFitView.ts +0 -1
- package/src/hooks/useMapLngLatToVw.ts +31 -0
- package/src/hooks/useMapRecomendPlace.ts +17 -2
- package/src/hooks/usePlacesLabelDirection.ts +61 -0
- package/src/hooks/useWaitTimer.ts +25 -0
- package/src/hooks/useWalkingRoute.ts +0 -1
- package/src/utils/transform.ts +8 -0
- package/todo.md +4 -5
package/dist/style.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
.n8tgem0{pointer-events:none;position:absolute;bottom:50%;left:0;right:0;margin-left:auto;margin-right:auto;display:flex;flex-direction:column;align-items:center;z-index:1}.n8tgem1{pointer-events:auto;padding:2.67vw 4.27vw;background:linear-gradient(90deg,#79AFFF 0%,#5C8DFF 32%,#507FFF 75%,#4471FF 100%);border-radius:3.2vw;display:flex;align-items:center}.n8tgem2{padding-right:2.67vw;max-width:41.33vw}.n8tgem3{display:-webkit-box;font-size:3.47vw;font-family:PingFangSC-Semibold,PingFang SC;font-weight:600;color:#fff;line-height:4.8vw;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.n8tgem4{margin-top:.53vw;font-size:2.93vw;font-family:PingFangSC-Regular,PingFang SC;font-weight:400;color:#fff;line-height:4.27vw;opacity:.8}.n8tgem5{width:1.758vw;height:2.93vw}.n8tgem6{width:.8vw;height:4vw;background:#4C80FF;border-radius:0vw 0vw 26.67vw 26.67vw}.n8tgem7{width:6.9355vw;height:10.67vw}.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}._1d8ur7t0,._7anfuo0{position:relative}._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}._1l6offo0{display:flex}._1l6offo1{width:4.53vw;height:4.53vw;margin-bottom:-2.265vw}._1l6offo2{width:16.27vw;height:16.27vw;margin-bottom:-8.135vw}.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{opacity:0}.fhyw8b{font-size:3.
|
|
1
|
+
.n8tgem0{pointer-events:none;position:absolute;bottom:50%;left:0;right:0;margin-left:auto;margin-right:auto;display:flex;flex-direction:column;align-items:center;z-index:1}.n8tgem1{pointer-events:auto;padding:2.67vw 4.27vw;background:linear-gradient(90deg,#79AFFF 0%,#5C8DFF 32%,#507FFF 75%,#4471FF 100%);border-radius:3.2vw;display:flex;align-items:center}.n8tgem2{padding-right:2.67vw;max-width:41.33vw}.n8tgem3{display:-webkit-box;font-size:3.47vw;font-family:PingFangSC-Semibold,PingFang SC;font-weight:600;color:#fff;line-height:4.8vw;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.n8tgem4{margin-top:.53vw;font-size:2.93vw;font-family:PingFangSC-Regular,PingFang SC;font-weight:400;color:#fff;line-height:4.27vw;opacity:.8}.n8tgem5{width:1.758vw;height:2.93vw}.n8tgem6{width:.8vw;height:4vw;background:#4C80FF;border-radius:0vw 0vw 26.67vw 26.67vw}.n8tgem7{width:6.9355vw;height:10.67vw}.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}._1d8ur7t0,._7anfuo0{position:relative}._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}._1l6offo0{display:flex}._1l6offo1{width:4.53vw;height:4.53vw;margin-bottom:-2.265vw}._1l6offo2{width:16.27vw;height:16.27vw;margin-bottom:-8.135vw}.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{opacity:0}.fhyw8b{display:-webkit-box;font-size:3.2vw;font-family:PingFangSC-Semibold,PingFang SC;font-weight:600;color:#243e66;line-height:4.53vw;width:max-content;max-width:32vw;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}._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}
|
|
@@ -13,3 +13,5 @@ export declare const language2vectorMapForeign: (lang: "en" | "zh") => VectorMap
|
|
|
13
13
|
export declare const place2point: (place: Place) => Point;
|
|
14
14
|
export declare const pipeAsync: <P extends any[]>(fn?: ((...args: P) => any) | undefined) => (...args: P) => void;
|
|
15
15
|
export declare const geocoderResult2cityName: (value: google.maps.GeocoderResult) => string | undefined;
|
|
16
|
+
export declare const isPlaceEqual: (p1: Place, p2?: Place) => boolean;
|
|
17
|
+
export declare const isPointEqual: (p1: Point, p2?: Point) => boolean;
|
package/package.json
CHANGED
|
@@ -42,8 +42,13 @@ export const DemoBusinessRecomendPlace = defineSetup(function DemoBusinessRecome
|
|
|
42
42
|
const { lat, lng } = place;
|
|
43
43
|
console.log("getRecomendPlace context?.source = ", context?.source);
|
|
44
44
|
return [
|
|
45
|
-
{
|
|
46
|
-
|
|
45
|
+
{
|
|
46
|
+
lat: lat + 0.1001,
|
|
47
|
+
lng: lng + 0.0001,
|
|
48
|
+
name: "Blessing Koko Dayc are Blessing Koko which is a very long name",
|
|
49
|
+
},
|
|
50
|
+
{ lat: lat + 0.0002, lng: lng + 0.0003, name: "place name 2" },
|
|
51
|
+
{ lat: lat + 0.0003, lng: lng + 0.0002, name: "place name 3" },
|
|
47
52
|
];
|
|
48
53
|
};
|
|
49
54
|
const {
|
|
@@ -66,7 +71,7 @@ export const DemoBusinessRecomendPlace = defineSetup(function DemoBusinessRecome
|
|
|
66
71
|
class={css.demo}
|
|
67
72
|
geoLoadingTitle={"正在读取GPS位置信息..."}
|
|
68
73
|
unavailableTitle={"当前区域暂未开通服务"}
|
|
69
|
-
noRecomendDescription={"
|
|
74
|
+
noRecomendDescription={""}
|
|
70
75
|
recomendDescription={"最佳上车地点"}
|
|
71
76
|
geoDefaultPosition={[139.728287, 35.661007]}
|
|
72
77
|
getRecomendPlace={getRecomendPlace}
|
|
@@ -1,9 +1,20 @@
|
|
|
1
|
+
import { computed } from "vue-demi";
|
|
1
2
|
import imgAddressLocator from "../../assets/icons/svg/address-locator.svg";
|
|
2
3
|
import imgArrowRight from "../../assets/icons/svg/arrow-right.svg";
|
|
3
4
|
import type { MapLogProps } from "../../hooks/useMapLog";
|
|
4
5
|
import { defineSetup } from "../../types/helper";
|
|
5
6
|
import * as css from "./AbsoluteAddressBox.css";
|
|
6
7
|
|
|
8
|
+
interface MultilineTitleProps {
|
|
9
|
+
value?: string;
|
|
10
|
+
}
|
|
11
|
+
const MultilineTitle = defineSetup(function MultilineTitle(props: MultilineTitleProps) {
|
|
12
|
+
const linesRef = computed(() => {
|
|
13
|
+
return props.value?.split("\n").flatMap((line, idx) => (idx === 0 ? line : [<br />, line]));
|
|
14
|
+
});
|
|
15
|
+
return () => <div>{linesRef.value}</div>;
|
|
16
|
+
});
|
|
17
|
+
|
|
7
18
|
export interface AbsoluteAddressBoxProps extends MapLogProps {
|
|
8
19
|
type: "box" | "locator";
|
|
9
20
|
title: string;
|
|
@@ -27,8 +38,8 @@ export const AbsoluteAddressBox = defineSetup(function AbsoluteAddressBox(
|
|
|
27
38
|
<div class={css.absoluteAddressBoxLayout}>
|
|
28
39
|
<div class={css.absoluteAddressBox} onClick={handleClick}>
|
|
29
40
|
<div class={css.boxTextLayout}>
|
|
30
|
-
<
|
|
31
|
-
{description && <div class={css.boxDescription}>{description}</div>}
|
|
41
|
+
<MultilineTitle class={css.boxTitle} value={title} />
|
|
42
|
+
{!!description && <div class={css.boxDescription}>{description}</div>}
|
|
32
43
|
</div>
|
|
33
44
|
<img class={css.arrowRight} src={imgArrowRight} />
|
|
34
45
|
</div>
|
|
@@ -8,13 +8,16 @@ import { MapLogProps, useMapLog } from "../../hooks/useMapLog";
|
|
|
8
8
|
import type { UseMapPlaceProps } from "../../hooks/useMapPlace";
|
|
9
9
|
import { useMapRecomendPlace, UseMapRecomendPlaceProps } from "../../hooks/useMapRecomendPlace";
|
|
10
10
|
import { useMapSupplier } from "../../hooks/useMapSupplier";
|
|
11
|
+
import { useWaitTimer } from "../../hooks/useWaitTimer";
|
|
11
12
|
import { defineLagecySetup } from "../../types/helper";
|
|
12
13
|
import type { Place, Point } from "../../types/interface";
|
|
14
|
+
import { isPlaceEqual } from "../../utils/transform";
|
|
13
15
|
import { AbsoluteAddressBox, AbsoluteAddressBoxProps } from "../AbsoluteAddressBox";
|
|
14
16
|
import { PassengerCircle } from "../PassengerCircle";
|
|
15
17
|
import { PickupPoints } from "../PickupPoints";
|
|
16
18
|
|
|
17
19
|
const RECOMMEND_PLACE_LIMIT = 30;
|
|
20
|
+
const MAP_DRAG_END_WAIT_ANIMATION = 500;
|
|
18
21
|
|
|
19
22
|
export interface CenterPlaceSource {
|
|
20
23
|
source: "default" | "geo" | "drag" | "recomend" | "api";
|
|
@@ -45,6 +48,7 @@ export const BusinessRecomendPlaceMap = defineLagecySetup(
|
|
|
45
48
|
const { geoDefaultPosition, getRecomendPlace, mapContext } = props;
|
|
46
49
|
const { mapRef, setMap } = useHeycarMap();
|
|
47
50
|
const { readyPromise } = useMapSupplier();
|
|
51
|
+
const { timerStart, timerStop } = useWaitTimer({ interval: MAP_DRAG_END_WAIT_ANIMATION });
|
|
48
52
|
const centerPlace = reactive<Place>({
|
|
49
53
|
lng: geoDefaultPosition?.[0] ?? 0,
|
|
50
54
|
lat: geoDefaultPosition?.[1] ?? 0,
|
|
@@ -98,6 +102,7 @@ export const BusinessRecomendPlaceMap = defineLagecySetup(
|
|
|
98
102
|
mapRef,
|
|
99
103
|
onChange: (point) => {
|
|
100
104
|
centerSource.source = "drag";
|
|
105
|
+
timerStart();
|
|
101
106
|
updatePlace(point);
|
|
102
107
|
emit("changeByDrag", point);
|
|
103
108
|
},
|
|
@@ -109,6 +114,7 @@ export const BusinessRecomendPlaceMap = defineLagecySetup(
|
|
|
109
114
|
updateRecomandPlace,
|
|
110
115
|
placeCandidates,
|
|
111
116
|
availableRef,
|
|
117
|
+
isElectedRef,
|
|
112
118
|
} = useMapRecomendPlace({
|
|
113
119
|
pointRef: centerPoint,
|
|
114
120
|
context: centerSource,
|
|
@@ -132,7 +138,8 @@ export const BusinessRecomendPlaceMap = defineLagecySetup(
|
|
|
132
138
|
}
|
|
133
139
|
emit("changePlace", place);
|
|
134
140
|
},
|
|
135
|
-
onChange: (place) => {
|
|
141
|
+
onChange: async (place) => {
|
|
142
|
+
await timerStop.promise;
|
|
136
143
|
centerSource.source = "recomend";
|
|
137
144
|
Object.assign(centerPlace, { ...place });
|
|
138
145
|
emit("changeRecomandPlace", place);
|
|
@@ -153,15 +160,21 @@ export const BusinessRecomendPlaceMap = defineLagecySetup(
|
|
|
153
160
|
: availableRef.value
|
|
154
161
|
? centerPlace.name
|
|
155
162
|
: props.unavailableTitle;
|
|
156
|
-
const description =
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
163
|
+
const description =
|
|
164
|
+
!availableRef.value || geoLoading.value
|
|
165
|
+
? undefined
|
|
166
|
+
: isElectedRef.value
|
|
167
|
+
? props.recomendDescription
|
|
168
|
+
: props.noRecomendDescription;
|
|
161
169
|
return (
|
|
162
170
|
<HeycarMap center={centerPoint.value} zoom={18} mapRef={setMap} log={props.log}>
|
|
163
|
-
<PassengerCircle position={geoPosition.value} />
|
|
164
|
-
<PickupPoints
|
|
171
|
+
{!geoError.value && <PassengerCircle position={geoPosition.value} />}
|
|
172
|
+
<PickupPoints
|
|
173
|
+
places={placeCandidates.value}
|
|
174
|
+
renderLabel={(place) => (isPlaceEqual(place, centerPlace) ? undefined : place.name)}
|
|
175
|
+
onClick={setCenterByPlace}
|
|
176
|
+
log={props.log}
|
|
177
|
+
/>
|
|
165
178
|
<AbsoluteAddressBox
|
|
166
179
|
title={title}
|
|
167
180
|
description={description}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { reactive, toRef } from "vue-demi";
|
|
1
2
|
import { MapLogProps, useMapLog } from "../../hooks/useMapLog";
|
|
3
|
+
import { usePointsLabelDirection } from "../../hooks/usePlacesLabelDirection";
|
|
2
4
|
import { defineSetup } from "../../types/helper";
|
|
3
5
|
import type { Place } from "../../types/interface";
|
|
4
6
|
import { place2point } from "../../utils/transform";
|
|
@@ -6,20 +8,31 @@ import { PlaceCircle } from "../PlaceCircle";
|
|
|
6
8
|
|
|
7
9
|
export interface PickupPointsProps extends MapLogProps {
|
|
8
10
|
places: Place[];
|
|
11
|
+
renderLabel?: (place: Place) => string | undefined;
|
|
9
12
|
onClick?: (value: Place) => any;
|
|
10
13
|
}
|
|
11
14
|
export const PickupPoints = defineSetup(function PickupPoints(props: PickupPointsProps, { emit }) {
|
|
12
15
|
useMapLog(props, "PickupPoints");
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
<PlaceCircle
|
|
17
|
-
position={place2point(place)}
|
|
18
|
-
label={place.name}
|
|
19
|
-
textAlign="left"
|
|
20
|
-
onClick={() => emit("click", place)}
|
|
21
|
-
/>
|
|
22
|
-
))}
|
|
23
|
-
</div>
|
|
16
|
+
const { renderLabel } = props;
|
|
17
|
+
const directions = usePointsLabelDirection(
|
|
18
|
+
reactive({ places: toRef(props, "places"), heightLimit: 9, widthLimit: 32 })
|
|
24
19
|
);
|
|
20
|
+
return () => {
|
|
21
|
+
return (
|
|
22
|
+
<div>
|
|
23
|
+
{props.places.map((place, idx) => {
|
|
24
|
+
const direction = directions.value[idx];
|
|
25
|
+
const label = renderLabel ? renderLabel(place) : place.name;
|
|
26
|
+
return (
|
|
27
|
+
<PlaceCircle
|
|
28
|
+
position={place2point(place)}
|
|
29
|
+
label={label}
|
|
30
|
+
textAlign={direction}
|
|
31
|
+
onClick={() => emit("click", place)}
|
|
32
|
+
/>
|
|
33
|
+
);
|
|
34
|
+
})}
|
|
35
|
+
</div>
|
|
36
|
+
);
|
|
37
|
+
};
|
|
25
38
|
});
|
|
@@ -62,10 +62,15 @@ export const placeIcon = recipe({
|
|
|
62
62
|
});
|
|
63
63
|
|
|
64
64
|
export const label = style({
|
|
65
|
-
|
|
65
|
+
display: "-webkit-box",
|
|
66
|
+
fontSize: "3.2vw",
|
|
66
67
|
fontFamily: "PingFangSC-Semibold, PingFang SC",
|
|
67
68
|
fontWeight: 600,
|
|
68
69
|
color: "#243E66",
|
|
69
70
|
lineHeight: "4.53vw",
|
|
70
|
-
|
|
71
|
+
width: "max-content",
|
|
72
|
+
maxWidth: "32vw",
|
|
73
|
+
WebkitLineClamp: 2,
|
|
74
|
+
WebkitBoxOrient: "vertical",
|
|
75
|
+
overflow: "hidden",
|
|
71
76
|
});
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { ref, watch } from "vue-demi";
|
|
2
2
|
import type { Point } from "../types/interface";
|
|
3
3
|
|
|
4
|
+
const GEO_TIMEOUT = 5000;
|
|
5
|
+
|
|
4
6
|
export interface UseGeoLocationProps {
|
|
5
7
|
geoDefaultPosition?: Point;
|
|
6
8
|
onChange?: (value: { position: Point; coordinate: GeolocationCoordinates }) => any;
|
|
@@ -52,8 +54,7 @@ export const useGeoLocation = (props?: UseGeoLocationProps) => {
|
|
|
52
54
|
onError?.(error);
|
|
53
55
|
console.log("errorRef = ", errorRef);
|
|
54
56
|
},
|
|
55
|
-
|
|
56
|
-
{ timeout: 3000, maximumAge: 0, enableHighAccuracy: false }
|
|
57
|
+
{ timeout: GEO_TIMEOUT, maximumAge: 0, enableHighAccuracy: false }
|
|
57
58
|
);
|
|
58
59
|
onCleanup(() => navigator.geolocation.clearWatch(watchId));
|
|
59
60
|
},
|
package/src/hooks/useMapDrag.ts
CHANGED
|
@@ -23,7 +23,6 @@ export const useAmapDrag = (props: UseMapDragProps<AMap.Map>) => {
|
|
|
23
23
|
});
|
|
24
24
|
};
|
|
25
25
|
const emitDragEnd: SetupContextEmitHandler<Pick<AmapProps, "onDragEnd">> = (_, { target }) => {
|
|
26
|
-
console.log("on DragEnd ------");
|
|
27
26
|
isDragging.value = false;
|
|
28
27
|
const { lng, lat } = target.getCenter();
|
|
29
28
|
centerRef.value = [lng, lat];
|
|
@@ -32,7 +32,6 @@ export const useAmapFitView = (props: UseMapFitViewProps<AMap.Map>) => {
|
|
|
32
32
|
if (!map) return;
|
|
33
33
|
const vw = window.innerWidth / 100;
|
|
34
34
|
const [top, right, bottom, left] = props.padding ?? [0, 0, 0, 0];
|
|
35
|
-
console.log([...overlayGroup]);
|
|
36
35
|
map.setFitView([...overlayGroup], false, [top * vw, bottom * vw, left * vw, right * vw]);
|
|
37
36
|
};
|
|
38
37
|
const registerFitVeiw: RegisterOverlay<AmapOverlay> = {
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Point } from "../types/interface";
|
|
2
|
+
import { useAmap, useGmap } from "./useMap";
|
|
3
|
+
import { useMapSupplier } from "./useMapSupplier";
|
|
4
|
+
|
|
5
|
+
export const useAmapLngLatToVw = () => {
|
|
6
|
+
const amap = useAmap();
|
|
7
|
+
const apiMapLngLatToVw = (point: Point) => {
|
|
8
|
+
return amap?.value?.lngLatToContainer(point).toArray() as [number, number];
|
|
9
|
+
};
|
|
10
|
+
return { apiMapLngLatToVw };
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const useGmapLngLatToVw = () => {
|
|
14
|
+
const gmap = useGmap();
|
|
15
|
+
const apiMapLngLatToVw = (point: Point) => {
|
|
16
|
+
const [lng, lat] = point;
|
|
17
|
+
const projection = gmap?.value?.getProjection();
|
|
18
|
+
const worldCoordinate = projection?.fromLatLngToPoint({ lng, lat });
|
|
19
|
+
const x = worldCoordinate?.x ?? 0;
|
|
20
|
+
const y = worldCoordinate?.y ?? 0;
|
|
21
|
+
const zoom = gmap?.value?.getZoom() ?? 0;
|
|
22
|
+
const scale = 2 ** zoom;
|
|
23
|
+
return [x * scale, y * scale] as [number, number];
|
|
24
|
+
};
|
|
25
|
+
return { apiMapLngLatToVw };
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export const useMapLngLatToVw = () => {
|
|
29
|
+
const { supplier } = useMapSupplier();
|
|
30
|
+
return supplier === "gmap" ? useGmapLngLatToVw() : useAmapLngLatToVw();
|
|
31
|
+
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { reactive, ref, Ref, watch } from "vue-demi";
|
|
1
|
+
import { computed, reactive, ref, Ref, watch } from "vue-demi";
|
|
2
2
|
import type { Place, Point } from "../types/interface";
|
|
3
|
-
|
|
3
|
+
|
|
4
|
+
import { isPointEqual, place2point } from "../utils/transform";
|
|
4
5
|
import { useAmapPlace, useGmapPlace, UseMapPlaceProps } from "./useMapPlace";
|
|
5
6
|
import { useMapSupplier } from "./useMapSupplier";
|
|
6
7
|
import { useUpdate } from "./useUdate";
|
|
@@ -52,6 +53,12 @@ export const useAmapRecomendPlace = <C>(props: UseMapRecomendPlaceProps<C>) => {
|
|
|
52
53
|
onChangeCity,
|
|
53
54
|
});
|
|
54
55
|
const recomendPlace = reactive<Place>({ ...place });
|
|
56
|
+
const isElectedRef = computed(() => {
|
|
57
|
+
const point = pointRef.value;
|
|
58
|
+
return placeCandidatesRef.value.some((candidate) =>
|
|
59
|
+
isPointEqual(place2point(candidate), point)
|
|
60
|
+
);
|
|
61
|
+
});
|
|
55
62
|
watch(
|
|
56
63
|
() => recomendPlaceKey.value,
|
|
57
64
|
async () => {
|
|
@@ -77,6 +84,7 @@ export const useAmapRecomendPlace = <C>(props: UseMapRecomendPlaceProps<C>) => {
|
|
|
77
84
|
recomendPlace,
|
|
78
85
|
placeCandidates: placeCandidatesRef,
|
|
79
86
|
availableRef,
|
|
87
|
+
isElectedRef,
|
|
80
88
|
updateRecomandPlace,
|
|
81
89
|
updatePlace,
|
|
82
90
|
setPlace,
|
|
@@ -97,6 +105,12 @@ export const useGmapRecomendPlace = <C>(props: UseMapRecomendPlaceProps<C>) => {
|
|
|
97
105
|
onChangeCity,
|
|
98
106
|
});
|
|
99
107
|
const recomendPlace = reactive<Place>({ ...place });
|
|
108
|
+
const isElectedRef = computed(() => {
|
|
109
|
+
const point = pointRef.value;
|
|
110
|
+
return placeCandidatesRef.value.some((candidate) =>
|
|
111
|
+
isPointEqual(place2point(candidate), point)
|
|
112
|
+
);
|
|
113
|
+
});
|
|
100
114
|
watch(
|
|
101
115
|
() => recomendPlaceKey.value,
|
|
102
116
|
async () => {
|
|
@@ -122,6 +136,7 @@ export const useGmapRecomendPlace = <C>(props: UseMapRecomendPlaceProps<C>) => {
|
|
|
122
136
|
recomendPlace,
|
|
123
137
|
placeCandidates: placeCandidatesRef,
|
|
124
138
|
availableRef,
|
|
139
|
+
isElectedRef,
|
|
125
140
|
updateRecomandPlace,
|
|
126
141
|
updatePlace,
|
|
127
142
|
setPlace,
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { ref } from "vue-demi";
|
|
2
|
+
import type { Place, Point } from "../types/interface";
|
|
3
|
+
import { watchEffectForDeepOption } from "../utils/compare";
|
|
4
|
+
import { place2point } from "../utils/transform";
|
|
5
|
+
import { useMapLngLatToVw } from "./useMapLngLatToVw";
|
|
6
|
+
|
|
7
|
+
type LabelDirection = "left" | "right";
|
|
8
|
+
const reverse = (direction: LabelDirection) => (direction === "left" ? "right" : "left");
|
|
9
|
+
|
|
10
|
+
export interface UsePointsLabelDirectionProps {
|
|
11
|
+
places: Place[];
|
|
12
|
+
widthLimit: number;
|
|
13
|
+
heightLimit: number;
|
|
14
|
+
}
|
|
15
|
+
export const usePointsLabelDirection = (props: UsePointsLabelDirectionProps) => {
|
|
16
|
+
const { widthLimit, heightLimit } = props;
|
|
17
|
+
const { apiMapLngLatToVw } = useMapLngLatToVw();
|
|
18
|
+
const diffVw = (from: Point, to: Point) => {
|
|
19
|
+
const vw = 100 / window.innerWidth;
|
|
20
|
+
const [x1, y1] = apiMapLngLatToVw(from);
|
|
21
|
+
const [x2, y2] = apiMapLngLatToVw(to);
|
|
22
|
+
return [(x2 - x1) * vw, (y2 - y1) * vw];
|
|
23
|
+
};
|
|
24
|
+
const directionsRef = ref<LabelDirection[]>(Array(props.places.length).fill("right"));
|
|
25
|
+
watchEffectForDeepOption(
|
|
26
|
+
() => props.places.map(place2point),
|
|
27
|
+
(points) => {
|
|
28
|
+
const sortedPoints = points
|
|
29
|
+
.map((point, index) => ({ point, index }))
|
|
30
|
+
.sort(({ point: [, lat1] }, { point: [, lat2] }) =>
|
|
31
|
+
lat1 < lat2 ? 1 : lat1 > lat2 ? -1 : 0
|
|
32
|
+
);
|
|
33
|
+
const len = points.length;
|
|
34
|
+
const result: LabelDirection[] = Array(len);
|
|
35
|
+
let prevDirection: LabelDirection = "right";
|
|
36
|
+
let isLimited = false;
|
|
37
|
+
for (let idx = 0; idx < len; idx++) {
|
|
38
|
+
const { index } = sortedPoints[idx];
|
|
39
|
+
if (idx === len - 1) {
|
|
40
|
+
result[index] = isLimited ? reverse(prevDirection) : "right";
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
if (idx === 0) {
|
|
44
|
+
const [dx, dy] = diffVw(sortedPoints[idx].point, sortedPoints[idx + 1].point);
|
|
45
|
+
isLimited = dx > -widthLimit && dx < widthLimit && dy > -heightLimit && dy < heightLimit;
|
|
46
|
+
const direction: LabelDirection = !isLimited ? "right" : dx > 0 ? "left" : "right";
|
|
47
|
+
result[index] = direction;
|
|
48
|
+
prevDirection = direction;
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
const [dx, dy] = diffVw(sortedPoints[idx].point, sortedPoints[idx + 1].point);
|
|
52
|
+
const direction: LabelDirection = isLimited ? reverse(prevDirection) : "right";
|
|
53
|
+
result[index] = direction;
|
|
54
|
+
prevDirection = direction;
|
|
55
|
+
isLimited = dx > -widthLimit && dx < widthLimit && dy > -heightLimit && dy < heightLimit;
|
|
56
|
+
}
|
|
57
|
+
directionsRef.value = result;
|
|
58
|
+
}
|
|
59
|
+
);
|
|
60
|
+
return directionsRef;
|
|
61
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
interface TimerStop {
|
|
2
|
+
promise: Promise<void>;
|
|
3
|
+
resolve?: () => void;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export interface UseWaitTimerProps {
|
|
7
|
+
interval: number;
|
|
8
|
+
}
|
|
9
|
+
export const useWaitTimer = (props: UseWaitTimerProps) => {
|
|
10
|
+
const { interval } = props;
|
|
11
|
+
let timer: number | undefined = undefined;
|
|
12
|
+
const timerStop: TimerStop = {
|
|
13
|
+
promise: Promise.resolve(),
|
|
14
|
+
resolve: undefined,
|
|
15
|
+
};
|
|
16
|
+
const timerStart = () => {
|
|
17
|
+
if (timer) clearTimeout(timer);
|
|
18
|
+
timerStop.resolve?.();
|
|
19
|
+
timerStop.promise = new Promise((resolve) => {
|
|
20
|
+
timerStop.resolve = resolve;
|
|
21
|
+
timer = setTimeout(resolve, interval);
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
return { timerStart, timerStop };
|
|
25
|
+
};
|
|
@@ -23,7 +23,6 @@ export const useAWalkingRoute = (props: UseWalkingRouteProps) => {
|
|
|
23
23
|
return;
|
|
24
24
|
}
|
|
25
25
|
const firstRoute = walkingResult.routes[0];
|
|
26
|
-
console.log("firstRoute = ", firstRoute);
|
|
27
26
|
for (const step of firstRoute.steps) {
|
|
28
27
|
const stepPath = step.path.map<Point>(({ lng, lat }) => [lng, lat]);
|
|
29
28
|
path = path.concat(stepPath);
|
package/src/utils/transform.ts
CHANGED
|
@@ -58,3 +58,11 @@ export const geocoderResult2cityName = (value: google.maps.GeocoderResult): stri
|
|
|
58
58
|
if (countryIdx > 0) return address_components[countryIdx - 1].long_name;
|
|
59
59
|
return undefined;
|
|
60
60
|
};
|
|
61
|
+
|
|
62
|
+
export const isPlaceEqual = (p1: Place, p2?: Place) => {
|
|
63
|
+
return p1.lng === p2?.lng && p1.lat === p2?.lat;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export const isPointEqual = (p1: Point, p2?: Point) => {
|
|
67
|
+
return p1[0] === p2?.[0] && p1[1] === p2?.[1];
|
|
68
|
+
};
|
package/todo.md
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
https://dawchihliou.github.io/articles/building-custom-google-maps-marker-react-component-like-airbnb-in-nextjs
|
|
2
2
|
|
|
3
3
|
2. Google 地址名称太长
|
|
4
|
-
3.
|
|
5
|
-
4.
|
|
6
|
-
5. google/高德 坐标系转化
|
|
4
|
+
3. 高德地图加载期间出现大量的请求
|
|
5
|
+
4. google/高德 坐标系转化
|
|
7
6
|
|
|
8
7
|
---
|
|
9
8
|
|
|
@@ -17,5 +16,5 @@ https://dawchihliou.github.io/articles/building-custom-google-maps-marker-react-
|
|
|
17
16
|
|
|
18
17
|
---
|
|
19
18
|
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
2. google cityparent 要用 city 上级,目前是 country 下级
|
|
20
|
+
3. 推荐点搜索是距离参数可能要优化,比如没搜到推荐点,应该扩大搜索距离
|