expo-gaode-map 1.0.2 → 1.0.3
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/README.md +1 -0
- package/android/src/main/java/expo/modules/gaodemap/ExpoGaodeMapModule.kt +17 -1
- package/android/src/main/java/expo/modules/gaodemap/ExpoGaodeMapView.kt +52 -5
- package/android/src/main/java/expo/modules/gaodemap/managers/OverlayManager.kt +184 -6
- package/android/src/main/java/expo/modules/gaodemap/overlays/CircleView.kt +24 -51
- package/android/src/main/java/expo/modules/gaodemap/overlays/MarkerView.kt +3 -0
- package/build/ExpoGaodeMapView.d.ts +26 -1
- package/build/ExpoGaodeMapView.d.ts.map +1 -1
- package/build/ExpoGaodeMapView.js +82 -1
- package/build/ExpoGaodeMapView.js.map +1 -1
- package/build/components/overlays/Circle.d.ts +0 -20
- package/build/components/overlays/Circle.d.ts.map +1 -1
- package/build/components/overlays/Circle.js +28 -45
- package/build/components/overlays/Circle.js.map +1 -1
- package/build/components/overlays/Marker.d.ts +2 -16
- package/build/components/overlays/Marker.d.ts.map +1 -1
- package/build/components/overlays/Marker.js +60 -37
- package/build/components/overlays/Marker.js.map +1 -1
- package/build/components/overlays/Polygon.d.ts.map +1 -1
- package/build/components/overlays/Polygon.js +28 -49
- package/build/components/overlays/Polygon.js.map +1 -1
- package/build/components/overlays/Polyline.d.ts.map +1 -1
- package/build/components/overlays/Polyline.js +22 -11
- package/build/components/overlays/Polyline.js.map +1 -1
- package/build/types/map-view.types.d.ts +30 -0
- package/build/types/map-view.types.d.ts.map +1 -1
- package/build/types/map-view.types.js.map +1 -1
- package/build/types/overlays.types.d.ts +25 -1
- package/build/types/overlays.types.d.ts.map +1 -1
- package/build/types/overlays.types.js.map +1 -1
- package/docs/API.md +40 -0
- package/docs/EXAMPLES.md +86 -2
- package/expo-module.config.json +1 -1
- package/ios/ExpoGaodeMapModule.swift +42 -14
- package/ios/ExpoGaodeMapView.swift +210 -7
- package/ios/managers/OverlayManager.swift +78 -10
- package/ios/overlays/CircleView.swift +41 -12
- package/ios/overlays/MarkerView.swift +55 -3
- package/ios/overlays/PolygonView.swift +25 -5
- package/ios/overlays/PolylineView.swift +23 -4
- package/ios/utils/ColorParser.swift +0 -5
- package/package.json +1 -1
- package/src/ExpoGaodeMapView.tsx +118 -1
- package/src/components/overlays/Circle.tsx +31 -48
- package/src/components/overlays/Marker.tsx +69 -42
- package/src/components/overlays/Polygon.tsx +34 -50
- package/src/components/overlays/Polyline.tsx +29 -14
- package/src/types/map-view.types.ts +25 -0
- package/src/types/overlays.types.ts +30 -1
|
@@ -3,13 +3,20 @@ import MAMapKit
|
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* 标记点视图
|
|
6
|
-
*
|
|
6
|
+
*
|
|
7
7
|
* 负责:
|
|
8
8
|
* - 在地图上显示标记点
|
|
9
9
|
* - 管理标记点属性(位置、标题、描述)
|
|
10
10
|
* - 支持拖拽功能
|
|
11
|
+
* - 支持自定义 children 视图
|
|
11
12
|
*/
|
|
12
13
|
class MarkerView: ExpoView {
|
|
14
|
+
// MARK: - 事件派发器
|
|
15
|
+
let onPress = EventDispatcher()
|
|
16
|
+
let onDragStart = EventDispatcher()
|
|
17
|
+
let onDrag = EventDispatcher()
|
|
18
|
+
let onDragEnd = EventDispatcher()
|
|
19
|
+
|
|
13
20
|
/// 标记点位置
|
|
14
21
|
var position: [String: Double] = [:]
|
|
15
22
|
/// 标题
|
|
@@ -18,11 +25,27 @@ class MarkerView: ExpoView {
|
|
|
18
25
|
var markerDescription: String = ""
|
|
19
26
|
/// 是否可拖拽
|
|
20
27
|
var draggable: Bool = false
|
|
28
|
+
/// 图标 URI
|
|
29
|
+
var iconUri: String?
|
|
30
|
+
/// 图标宽度
|
|
31
|
+
var iconWidth: Double = 40
|
|
32
|
+
/// 图标高度
|
|
33
|
+
var iconHeight: Double = 40
|
|
34
|
+
/// 中心偏移
|
|
35
|
+
var centerOffset: [String: Double]?
|
|
36
|
+
/// 是否显示动画
|
|
37
|
+
var animatesDrop: Bool = false
|
|
38
|
+
/// 大头针颜色
|
|
39
|
+
var pinColor: String = "red"
|
|
40
|
+
/// 是否显示气泡
|
|
41
|
+
var canShowCallout: Bool = true
|
|
21
42
|
|
|
22
43
|
/// 地图视图弱引用
|
|
23
44
|
private var mapView: MAMapView?
|
|
24
45
|
/// 标记点对象
|
|
25
|
-
|
|
46
|
+
var annotation: MAPointAnnotation?
|
|
47
|
+
/// 标记点视图
|
|
48
|
+
private var annotationView: MAAnnotationView?
|
|
26
49
|
|
|
27
50
|
required init(appContext: AppContext? = nil) {
|
|
28
51
|
super.init(appContext: appContext)
|
|
@@ -60,6 +83,9 @@ class MarkerView: ExpoView {
|
|
|
60
83
|
|
|
61
84
|
mapView.addAnnotation(annotation)
|
|
62
85
|
self.annotation = annotation
|
|
86
|
+
|
|
87
|
+
// 获取 annotationView
|
|
88
|
+
annotationView = mapView.view(for: annotation)
|
|
63
89
|
}
|
|
64
90
|
|
|
65
91
|
/**
|
|
@@ -95,7 +121,33 @@ class MarkerView: ExpoView {
|
|
|
95
121
|
*/
|
|
96
122
|
func setDraggable(_ draggable: Bool) {
|
|
97
123
|
self.draggable = draggable
|
|
98
|
-
|
|
124
|
+
updateAnnotation()
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
func setIcon(_ source: [String: Any]?) {
|
|
128
|
+
if let dict = source {
|
|
129
|
+
// 处理 require() 返回的对象
|
|
130
|
+
if let uri = dict["uri"] as? String {
|
|
131
|
+
self.iconUri = uri
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
updateAnnotation()
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
func setCenterOffset(_ offset: [String: Double]) {
|
|
138
|
+
self.centerOffset = offset
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
func setAnimatesDrop(_ animate: Bool) {
|
|
142
|
+
self.animatesDrop = animate
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
func setPinColor(_ color: String) {
|
|
146
|
+
self.pinColor = color
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
func setCanShowCallout(_ show: Bool) {
|
|
150
|
+
self.canShowCallout = show
|
|
99
151
|
}
|
|
100
152
|
|
|
101
153
|
/**
|
|
@@ -43,17 +43,29 @@ class PolygonView: ExpoView {
|
|
|
43
43
|
* 更新多边形覆盖物
|
|
44
44
|
*/
|
|
45
45
|
private func updatePolygon() {
|
|
46
|
-
guard let mapView = mapView else {
|
|
46
|
+
guard let mapView = mapView else {
|
|
47
|
+
print("❌ PolygonView.updatePolygon: mapView 为空")
|
|
48
|
+
return
|
|
49
|
+
}
|
|
47
50
|
if let old = polygon { mapView.remove(old) }
|
|
48
51
|
|
|
49
52
|
var coords = points.compactMap { point -> CLLocationCoordinate2D? in
|
|
50
53
|
guard let lat = point["latitude"], let lng = point["longitude"] else { return nil }
|
|
51
54
|
return CLLocationCoordinate2D(latitude: lat, longitude: lng)
|
|
52
55
|
}
|
|
53
|
-
guard !coords.isEmpty else {
|
|
56
|
+
guard !coords.isEmpty else {
|
|
57
|
+
print("❌ PolygonView.updatePolygon: 点数组为空")
|
|
58
|
+
return
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
print("🔶 PolygonView.updatePolygon: points=\(coords.count)个点")
|
|
62
|
+
print("🔶 PolygonView.updatePolygon: fillColor=\(String(describing: fillColor)), strokeColor=\(String(describing: strokeColor)), strokeWidth=\(strokeWidth)")
|
|
54
63
|
|
|
55
64
|
polygon = MAPolygon(coordinates: &coords, count: UInt(coords.count))
|
|
56
65
|
mapView.add(polygon!)
|
|
66
|
+
|
|
67
|
+
renderer = nil
|
|
68
|
+
print("🔶 PolygonView.updatePolygon: renderer 已清空")
|
|
57
69
|
}
|
|
58
70
|
|
|
59
71
|
/**
|
|
@@ -63,9 +75,15 @@ class PolygonView: ExpoView {
|
|
|
63
75
|
func getRenderer() -> MAOverlayRenderer {
|
|
64
76
|
if renderer == nil, let polygon = polygon {
|
|
65
77
|
renderer = MAPolygonRenderer(polygon: polygon)
|
|
66
|
-
|
|
67
|
-
|
|
78
|
+
let parsedFillColor = ColorParser.parseColor(fillColor)
|
|
79
|
+
let parsedStrokeColor = ColorParser.parseColor(strokeColor)
|
|
80
|
+
renderer?.fillColor = parsedFillColor ?? UIColor.clear
|
|
81
|
+
renderer?.strokeColor = parsedStrokeColor ?? UIColor.clear
|
|
68
82
|
renderer?.lineWidth = CGFloat(strokeWidth)
|
|
83
|
+
print("🔶 PolygonView.getRenderer: 创建新 renderer")
|
|
84
|
+
print("🔶 PolygonView.getRenderer: fillColor=\(String(describing: parsedFillColor)), strokeColor=\(String(describing: parsedStrokeColor)), lineWidth=\(strokeWidth)")
|
|
85
|
+
} else {
|
|
86
|
+
print("🔶 PolygonView.getRenderer: 使用缓存的 renderer")
|
|
69
87
|
}
|
|
70
88
|
return renderer!
|
|
71
89
|
}
|
|
@@ -76,7 +94,6 @@ class PolygonView: ExpoView {
|
|
|
76
94
|
*/
|
|
77
95
|
func setPoints(_ points: [[String: Double]]) {
|
|
78
96
|
self.points = points
|
|
79
|
-
renderer = nil
|
|
80
97
|
updatePolygon()
|
|
81
98
|
}
|
|
82
99
|
|
|
@@ -85,6 +102,7 @@ class PolygonView: ExpoView {
|
|
|
85
102
|
* @param color 颜色值
|
|
86
103
|
*/
|
|
87
104
|
func setFillColor(_ color: Any?) {
|
|
105
|
+
print("🔶 PolygonView.setFillColor: \(String(describing: color))")
|
|
88
106
|
fillColor = color
|
|
89
107
|
renderer = nil
|
|
90
108
|
updatePolygon()
|
|
@@ -95,6 +113,7 @@ class PolygonView: ExpoView {
|
|
|
95
113
|
* @param color 颜色值
|
|
96
114
|
*/
|
|
97
115
|
func setStrokeColor(_ color: Any?) {
|
|
116
|
+
print("🔶 PolygonView.setStrokeColor: \(String(describing: color))")
|
|
98
117
|
strokeColor = color
|
|
99
118
|
renderer = nil
|
|
100
119
|
updatePolygon()
|
|
@@ -105,6 +124,7 @@ class PolygonView: ExpoView {
|
|
|
105
124
|
* @param width 宽度值
|
|
106
125
|
*/
|
|
107
126
|
func setStrokeWidth(_ width: Float) {
|
|
127
|
+
print("🔶 PolygonView.setStrokeWidth: \(width)")
|
|
108
128
|
strokeWidth = width
|
|
109
129
|
renderer = nil
|
|
110
130
|
updatePolygon()
|
|
@@ -43,17 +43,29 @@ class PolylineView: ExpoView {
|
|
|
43
43
|
* 更新折线覆盖物
|
|
44
44
|
*/
|
|
45
45
|
private func updatePolyline() {
|
|
46
|
-
guard let mapView = mapView else {
|
|
46
|
+
guard let mapView = mapView else {
|
|
47
|
+
print("❌ PolylineView.updatePolyline: mapView 为空")
|
|
48
|
+
return
|
|
49
|
+
}
|
|
47
50
|
if let old = polyline { mapView.remove(old) }
|
|
48
51
|
|
|
49
52
|
var coords = points.compactMap { point -> CLLocationCoordinate2D? in
|
|
50
53
|
guard let lat = point["latitude"], let lng = point["longitude"] else { return nil }
|
|
51
54
|
return CLLocationCoordinate2D(latitude: lat, longitude: lng)
|
|
52
55
|
}
|
|
53
|
-
guard !coords.isEmpty else {
|
|
56
|
+
guard !coords.isEmpty else {
|
|
57
|
+
print("❌ PolylineView.updatePolyline: 点数组为空")
|
|
58
|
+
return
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
print("🔷 PolylineView.updatePolyline: points=\(coords.count)个点")
|
|
62
|
+
print("🔷 PolylineView.updatePolyline: strokeColor=\(String(describing: strokeColor)), strokeWidth=\(strokeWidth), texture=\(String(describing: textureUrl))")
|
|
54
63
|
|
|
55
64
|
polyline = MAPolyline(coordinates: &coords, count: UInt(coords.count))
|
|
56
65
|
mapView.add(polyline!)
|
|
66
|
+
|
|
67
|
+
renderer = nil
|
|
68
|
+
print("🔷 PolylineView.updatePolyline: renderer 已清空")
|
|
57
69
|
}
|
|
58
70
|
|
|
59
71
|
/**
|
|
@@ -66,10 +78,15 @@ class PolylineView: ExpoView {
|
|
|
66
78
|
renderer?.lineWidth = CGFloat(strokeWidth)
|
|
67
79
|
|
|
68
80
|
if let url = textureUrl {
|
|
81
|
+
print("🔷 PolylineView.getRenderer: 加载纹理 \(url)")
|
|
69
82
|
loadTexture(url: url, renderer: renderer!)
|
|
70
83
|
} else {
|
|
71
|
-
|
|
84
|
+
let parsedColor = ColorParser.parseColor(strokeColor)
|
|
85
|
+
renderer?.strokeColor = parsedColor ?? UIColor.clear
|
|
86
|
+
print("🔷 PolylineView.getRenderer: 创建新 renderer, strokeColor=\(String(describing: parsedColor)), lineWidth=\(strokeWidth)")
|
|
72
87
|
}
|
|
88
|
+
} else {
|
|
89
|
+
print("🔷 PolylineView.getRenderer: 使用缓存的 renderer")
|
|
73
90
|
}
|
|
74
91
|
return renderer!
|
|
75
92
|
}
|
|
@@ -126,7 +143,6 @@ class PolylineView: ExpoView {
|
|
|
126
143
|
*/
|
|
127
144
|
func setPoints(_ points: [[String: Double]]) {
|
|
128
145
|
self.points = points
|
|
129
|
-
renderer = nil
|
|
130
146
|
updatePolyline()
|
|
131
147
|
}
|
|
132
148
|
|
|
@@ -135,6 +151,7 @@ class PolylineView: ExpoView {
|
|
|
135
151
|
* @param width 线宽值
|
|
136
152
|
*/
|
|
137
153
|
func setStrokeWidth(_ width: Float) {
|
|
154
|
+
print("🔷 PolylineView.setStrokeWidth: \(width)")
|
|
138
155
|
strokeWidth = width
|
|
139
156
|
renderer = nil
|
|
140
157
|
updatePolyline()
|
|
@@ -145,6 +162,7 @@ class PolylineView: ExpoView {
|
|
|
145
162
|
* @param color 颜色值
|
|
146
163
|
*/
|
|
147
164
|
func setStrokeColor(_ color: Any?) {
|
|
165
|
+
print("🔷 PolylineView.setStrokeColor: \(String(describing: color))")
|
|
148
166
|
strokeColor = color
|
|
149
167
|
renderer = nil
|
|
150
168
|
updatePolyline()
|
|
@@ -155,6 +173,7 @@ class PolylineView: ExpoView {
|
|
|
155
173
|
* @param url 图片 URL
|
|
156
174
|
*/
|
|
157
175
|
func setTexture(_ url: String?) {
|
|
176
|
+
print("🔷 PolylineView.setTexture: \(String(describing: url))")
|
|
158
177
|
textureUrl = url
|
|
159
178
|
renderer = nil
|
|
160
179
|
updatePolyline()
|
|
@@ -12,12 +12,10 @@ class ColorParser {
|
|
|
12
12
|
static func parseColor(_ colorValue: Any?) -> UIColor? {
|
|
13
13
|
guard let colorValue = colorValue else { return nil }
|
|
14
14
|
|
|
15
|
-
// 如果是数字类型
|
|
16
15
|
if let number = colorValue as? Int {
|
|
17
16
|
return UIColor(hex: number)
|
|
18
17
|
}
|
|
19
18
|
|
|
20
|
-
// 如果是字符串类型
|
|
21
19
|
if let string = colorValue as? String {
|
|
22
20
|
return parseColorString(string)
|
|
23
21
|
}
|
|
@@ -79,12 +77,10 @@ class ColorParser {
|
|
|
79
77
|
var hexNumber: UInt64 = 0
|
|
80
78
|
|
|
81
79
|
if scanner.scanHexInt64(&hexNumber) {
|
|
82
|
-
// 尝试 ARGB 格式(Android 风格)
|
|
83
80
|
let alphaARGB = CGFloat((hexNumber & 0xff000000) >> 24) / 255
|
|
84
81
|
let redARGB = CGFloat((hexNumber & 0x00ff0000) >> 16) / 255
|
|
85
82
|
let greenARGB = CGFloat((hexNumber & 0x0000ff00) >> 8) / 255
|
|
86
83
|
let blueARGB = CGFloat(hexNumber & 0x000000ff) / 255
|
|
87
|
-
|
|
88
84
|
return UIColor(red: redARGB, green: greenARGB, blue: blueARGB, alpha: alphaARGB)
|
|
89
85
|
}
|
|
90
86
|
}
|
|
@@ -98,7 +94,6 @@ class ColorParser {
|
|
|
98
94
|
let red = CGFloat((hexNumber & 0xff0000) >> 16) / 255
|
|
99
95
|
let green = CGFloat((hexNumber & 0x00ff00) >> 8) / 255
|
|
100
96
|
let blue = CGFloat(hexNumber & 0x0000ff) / 255
|
|
101
|
-
|
|
102
97
|
return UIColor(red: red, green: green, blue: blue, alpha: alpha)
|
|
103
98
|
}
|
|
104
99
|
}
|
package/package.json
CHANGED
package/src/ExpoGaodeMapView.tsx
CHANGED
|
@@ -22,6 +22,61 @@ const NativeView: React.ComponentType<MapViewProps & { ref?: React.Ref<NativeMap
|
|
|
22
22
|
// 创建 Context 用于子组件访问 MapRef
|
|
23
23
|
export const MapContext = React.createContext<React.RefObject<MapViewRef | null> | null>(null);
|
|
24
24
|
|
|
25
|
+
// Marker 事件管理器
|
|
26
|
+
type MarkerEventCallbacks = {
|
|
27
|
+
onPress?: () => void;
|
|
28
|
+
onDragStart?: () => void;
|
|
29
|
+
onDrag?: () => void;
|
|
30
|
+
onDragEnd?: (event: { nativeEvent: LatLng }) => void;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
class MarkerEventManager {
|
|
34
|
+
private callbacks = new Map<string, MarkerEventCallbacks>();
|
|
35
|
+
|
|
36
|
+
register(markerId: string, callbacks: MarkerEventCallbacks) {
|
|
37
|
+
this.callbacks.set(markerId, callbacks);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
unregister(markerId: string) {
|
|
41
|
+
this.callbacks.delete(markerId);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
trigger(markerId: string, eventType: keyof MarkerEventCallbacks, data?: any) {
|
|
45
|
+
const callbacks = this.callbacks.get(markerId);
|
|
46
|
+
if (callbacks && callbacks[eventType]) {
|
|
47
|
+
callbacks[eventType]!(data);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export const MarkerEventContext = React.createContext<MarkerEventManager | null>(null);
|
|
53
|
+
|
|
54
|
+
// Circle 事件管理器
|
|
55
|
+
type CircleEventCallbacks = {
|
|
56
|
+
onPress?: () => void;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
class CircleEventManager {
|
|
60
|
+
private callbacks = new Map<string, CircleEventCallbacks>();
|
|
61
|
+
|
|
62
|
+
register(circleId: string, callbacks: CircleEventCallbacks) {
|
|
63
|
+
this.callbacks.set(circleId, callbacks);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
unregister(circleId: string) {
|
|
67
|
+
this.callbacks.delete(circleId);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
trigger(circleId: string, eventType: keyof CircleEventCallbacks) {
|
|
71
|
+
const callbacks = this.callbacks.get(circleId);
|
|
72
|
+
if (callbacks && callbacks[eventType]) {
|
|
73
|
+
callbacks[eventType]!();
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export const CircleEventContext = React.createContext<CircleEventManager | null>(null);
|
|
79
|
+
|
|
25
80
|
/**
|
|
26
81
|
* 高德地图视图组件,提供地图操作API和覆盖物管理功能
|
|
27
82
|
*
|
|
@@ -43,6 +98,54 @@ export const MapContext = React.createContext<React.RefObject<MapViewRef | null>
|
|
|
43
98
|
const ExpoGaodeMapView = React.forwardRef<MapViewRef, MapViewProps>((props, ref) => {
|
|
44
99
|
const nativeRef = React.useRef<NativeMapViewRef>(null);
|
|
45
100
|
const internalRef = React.useRef<MapViewRef | null>(null);
|
|
101
|
+
const markerEventManager = React.useRef(new MarkerEventManager()).current;
|
|
102
|
+
const circleEventManager = React.useRef(new CircleEventManager()).current;
|
|
103
|
+
|
|
104
|
+
// 处理 Marker 事件
|
|
105
|
+
const handleMarkerPress = React.useCallback((event: any) => {
|
|
106
|
+
const markerId = event.nativeEvent?.markerId;
|
|
107
|
+
if (markerId) {
|
|
108
|
+
markerEventManager.trigger(markerId, 'onPress');
|
|
109
|
+
}
|
|
110
|
+
props.onMarkerPress?.(event);
|
|
111
|
+
}, [props.onMarkerPress]);
|
|
112
|
+
|
|
113
|
+
const handleMarkerDragStart = React.useCallback((event: any) => {
|
|
114
|
+
const markerId = event.nativeEvent?.markerId;
|
|
115
|
+
if (markerId) {
|
|
116
|
+
markerEventManager.trigger(markerId, 'onDragStart');
|
|
117
|
+
}
|
|
118
|
+
props.onMarkerDragStart?.(event);
|
|
119
|
+
}, [props.onMarkerDragStart]);
|
|
120
|
+
|
|
121
|
+
const handleMarkerDrag = React.useCallback((event: any) => {
|
|
122
|
+
const markerId = event.nativeEvent?.markerId;
|
|
123
|
+
if (markerId) {
|
|
124
|
+
markerEventManager.trigger(markerId, 'onDrag');
|
|
125
|
+
}
|
|
126
|
+
props.onMarkerDrag?.(event);
|
|
127
|
+
}, [props.onMarkerDrag]);
|
|
128
|
+
|
|
129
|
+
const handleMarkerDragEnd = React.useCallback((event: any) => {
|
|
130
|
+
const markerId = event.nativeEvent?.markerId;
|
|
131
|
+
if (markerId) {
|
|
132
|
+
markerEventManager.trigger(markerId, 'onDragEnd', {
|
|
133
|
+
nativeEvent: {
|
|
134
|
+
latitude: event.nativeEvent.latitude,
|
|
135
|
+
longitude: event.nativeEvent.longitude
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
props.onMarkerDragEnd?.(event);
|
|
140
|
+
}, [props.onMarkerDragEnd]);
|
|
141
|
+
|
|
142
|
+
const handleCirclePress = React.useCallback((event: any) => {
|
|
143
|
+
const circleId = event.nativeEvent?.circleId;
|
|
144
|
+
if (circleId) {
|
|
145
|
+
circleEventManager.trigger(circleId, 'onPress');
|
|
146
|
+
}
|
|
147
|
+
props.onCirclePress?.(event);
|
|
148
|
+
}, [props.onCirclePress]);
|
|
46
149
|
|
|
47
150
|
const apiRef: MapViewRef = React.useMemo(() => ({
|
|
48
151
|
/**
|
|
@@ -243,7 +346,21 @@ const ExpoGaodeMapView = React.forwardRef<MapViewRef, MapViewProps>((props, ref)
|
|
|
243
346
|
|
|
244
347
|
return (
|
|
245
348
|
<MapContext.Provider value={internalRef}>
|
|
246
|
-
<
|
|
349
|
+
<MarkerEventContext.Provider value={markerEventManager}>
|
|
350
|
+
<CircleEventContext.Provider value={circleEventManager}>
|
|
351
|
+
<NativeView
|
|
352
|
+
ref={nativeRef}
|
|
353
|
+
{...props}
|
|
354
|
+
onMarkerPress={handleMarkerPress}
|
|
355
|
+
onMarkerDragStart={handleMarkerDragStart}
|
|
356
|
+
onMarkerDrag={handleMarkerDrag}
|
|
357
|
+
onMarkerDragEnd={handleMarkerDragEnd}
|
|
358
|
+
onCirclePress={handleCirclePress}
|
|
359
|
+
>
|
|
360
|
+
{props.children}
|
|
361
|
+
</NativeView>
|
|
362
|
+
</CircleEventContext.Provider>
|
|
363
|
+
</MarkerEventContext.Provider>
|
|
247
364
|
</MapContext.Provider>
|
|
248
365
|
);
|
|
249
366
|
});
|
|
@@ -1,72 +1,55 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import type { CircleProps } from '../../types';
|
|
3
|
-
import { MapContext } from '../../ExpoGaodeMapView';
|
|
3
|
+
import { MapContext, CircleEventContext } from '../../ExpoGaodeMapView';
|
|
4
4
|
|
|
5
|
-
/**
|
|
6
|
-
* Circle 组件 - 高德地图圆形覆盖物
|
|
7
|
-
*
|
|
8
|
-
* 该组件用于在高德地图上绘制圆形覆盖物,支持动态更新圆形属性。
|
|
9
|
-
*
|
|
10
|
-
* @param {CircleProps} props - 圆形属性配置
|
|
11
|
-
* @param {LatLng} props.center - 圆形中心点坐标
|
|
12
|
-
* @param {number} props.radius - 圆形半径(米)
|
|
13
|
-
* @param {string} props.fillColor - 填充颜色(十六进制或RGBA)
|
|
14
|
-
* @param {string} props.strokeColor - 边框颜色(十六进制或RGBA)
|
|
15
|
-
* @param {number} props.strokeWidth - 边框宽度(像素)
|
|
16
|
-
*
|
|
17
|
-
* @returns {null} 该组件不渲染任何UI元素
|
|
18
|
-
*
|
|
19
|
-
* @remarks
|
|
20
|
-
* 1. 组件挂载时自动添加圆形到地图
|
|
21
|
-
* 2. 组件卸载时自动移除圆形
|
|
22
|
-
* 3. 当中心点、半径、颜色等属性变化时自动更新圆形
|
|
23
|
-
* 4. 使用 React Context 获取地图实例引用
|
|
24
|
-
*/
|
|
25
5
|
export default function Circle(props: CircleProps) {
|
|
26
6
|
const mapRef = React.useContext(MapContext);
|
|
7
|
+
const eventManager = React.useContext(CircleEventContext);
|
|
27
8
|
const circleIdRef = React.useRef<string | null>(null);
|
|
9
|
+
const propsRef = React.useRef(props);
|
|
28
10
|
|
|
29
|
-
|
|
11
|
+
React.useEffect(() => {
|
|
12
|
+
propsRef.current = props;
|
|
13
|
+
}, [props]);
|
|
30
14
|
|
|
31
15
|
React.useEffect(() => {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
16
|
+
const checkAndAdd = () => {
|
|
17
|
+
if (!mapRef?.current) {
|
|
18
|
+
setTimeout(checkAndAdd, 50);
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const circleId = `circle_${Date.now()}_${Math.random()}`;
|
|
23
|
+
circleIdRef.current = circleId;
|
|
24
|
+
|
|
25
|
+
if (eventManager && props.onPress) {
|
|
26
|
+
eventManager.register(circleId, {
|
|
27
|
+
onPress: props.onPress,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
mapRef.current.addCircle(circleId, propsRef.current);
|
|
32
|
+
};
|
|
42
33
|
|
|
43
|
-
|
|
44
|
-
console.log('✅ 圆形已添加:', circleId);
|
|
34
|
+
checkAndAdd();
|
|
45
35
|
|
|
46
|
-
// 清理函数 - 移除圆形
|
|
47
36
|
return () => {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
37
|
+
if (circleIdRef.current) {
|
|
38
|
+
if (eventManager) {
|
|
39
|
+
eventManager.unregister(circleIdRef.current);
|
|
40
|
+
}
|
|
41
|
+
if (mapRef?.current) {
|
|
42
|
+
mapRef.current.removeCircle(circleIdRef.current);
|
|
43
|
+
}
|
|
52
44
|
}
|
|
53
45
|
};
|
|
54
46
|
}, []);
|
|
55
47
|
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* 当Circle组件的props发生变化时,更新地图上的圆形覆盖物
|
|
59
|
-
* 如果圆形ID和地图引用都存在,则调用地图实例的updateCircle方法更新圆形
|
|
60
|
-
* 更新成功后会打印日志确认
|
|
61
|
-
*/
|
|
62
48
|
React.useEffect(() => {
|
|
63
|
-
console.log('Circle props 变化,更新圆形');
|
|
64
49
|
if (circleIdRef.current && mapRef?.current) {
|
|
65
50
|
mapRef.current.updateCircle(circleIdRef.current, props);
|
|
66
|
-
console.log('✅ 圆形已更新:', circleIdRef.current);
|
|
67
51
|
}
|
|
68
52
|
}, [props.center, props.radius, props.fillColor, props.strokeColor, props.strokeWidth]);
|
|
69
53
|
|
|
70
|
-
// 不渲染任何 UI
|
|
71
54
|
return null;
|
|
72
55
|
}
|
|
@@ -1,56 +1,83 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { Image } from 'react-native';
|
|
3
|
+
import { MapContext, MarkerEventContext } from '../../ExpoGaodeMapView';
|
|
3
4
|
import type { MarkerProps } from '../../types';
|
|
4
5
|
|
|
5
|
-
/**
|
|
6
|
-
* Marker 组件 - 用于在地图上显示标记点
|
|
7
|
-
*
|
|
8
|
-
* @param props - 标记点属性配置
|
|
9
|
-
* @param props.position - 标记点坐标 [经度, 纬度]
|
|
10
|
-
* @param props.title - 标记点标题
|
|
11
|
-
* @param props.draggable - 标记点是否可拖动
|
|
12
|
-
*
|
|
13
|
-
* @remarks
|
|
14
|
-
* 组件内部会自动处理标记点的添加、更新和移除
|
|
15
|
-
* 当组件卸载时会自动移除对应的地图标记
|
|
16
|
-
*
|
|
17
|
-
* @note
|
|
18
|
-
* 组件本身不渲染任何DOM元素,仅作为地图标记的逻辑容器
|
|
19
|
-
*/
|
|
20
6
|
export default function Marker(props: MarkerProps) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
console.log('Marker 组件渲染,props:', props);
|
|
7
|
+
return <MarkerImperative {...props} />;
|
|
8
|
+
}
|
|
25
9
|
|
|
26
|
-
|
|
10
|
+
function MarkerImperative(props: MarkerProps) {
|
|
11
|
+
const mapRef = React.useContext(MapContext);
|
|
12
|
+
const eventManager = React.useContext(MarkerEventContext);
|
|
13
|
+
const markerIdRef = React.useRef<string | null>(null);
|
|
14
|
+
const propsRef = React.useRef(props);
|
|
15
|
+
|
|
16
|
+
React.useEffect(() => {
|
|
17
|
+
propsRef.current = props;
|
|
18
|
+
}, [props]);
|
|
19
|
+
|
|
27
20
|
React.useEffect(() => {
|
|
28
|
-
const
|
|
21
|
+
const checkAndAdd = () => {
|
|
22
|
+
if (!mapRef?.current) {
|
|
23
|
+
setTimeout(checkAndAdd, 50);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const markerId = `marker_${Date.now()}_${Math.random()}`;
|
|
28
|
+
markerIdRef.current = markerId;
|
|
29
|
+
|
|
30
|
+
if (eventManager) {
|
|
31
|
+
eventManager.register(markerId, {
|
|
32
|
+
onPress: props.onPress,
|
|
33
|
+
onDragStart: props.onDragStart,
|
|
34
|
+
onDrag: props.onDrag,
|
|
35
|
+
onDragEnd: props.onDragEnd,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const processedProps = { ...propsRef.current };
|
|
40
|
+
if (processedProps.icon && typeof processedProps.icon === 'number') {
|
|
41
|
+
const resolved = Image.resolveAssetSource(processedProps.icon);
|
|
42
|
+
processedProps.icon = resolved;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
mapRef.current.addMarker(markerId, processedProps);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
checkAndAdd();
|
|
29
49
|
|
|
30
|
-
console.log('Marker useEffect - 添加标记到地图');
|
|
31
|
-
mapRef?.current?.addMarker?.(markerId, props).then(() => {
|
|
32
|
-
console.log('✅ 标记已添加:', markerId);
|
|
33
|
-
}).catch((error: any) => {
|
|
34
|
-
console.error('❌ 添加标记失败:', error);
|
|
35
|
-
});
|
|
36
|
-
|
|
37
50
|
return () => {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
51
|
+
if (markerIdRef.current) {
|
|
52
|
+
if (eventManager) {
|
|
53
|
+
eventManager.unregister(markerIdRef.current);
|
|
54
|
+
}
|
|
55
|
+
if (mapRef?.current) {
|
|
56
|
+
mapRef.current.removeMarker(markerIdRef.current);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
42
59
|
};
|
|
43
60
|
}, []);
|
|
44
61
|
|
|
45
|
-
// 监听 Props 变化,更新标记
|
|
46
62
|
React.useEffect(() => {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
63
|
+
if (markerIdRef.current && mapRef?.current) {
|
|
64
|
+
const processedProps = { ...props };
|
|
65
|
+
if (processedProps.icon && typeof processedProps.icon === 'number') {
|
|
66
|
+
const resolved = Image.resolveAssetSource(processedProps.icon);
|
|
67
|
+
processedProps.icon = resolved;
|
|
68
|
+
}
|
|
69
|
+
mapRef.current.updateMarker(markerIdRef.current, processedProps);
|
|
70
|
+
}
|
|
71
|
+
}, [
|
|
72
|
+
props.position?.latitude,
|
|
73
|
+
props.position?.longitude,
|
|
74
|
+
props.title,
|
|
75
|
+
props.draggable,
|
|
76
|
+
props.icon,
|
|
77
|
+
props.iconWidth,
|
|
78
|
+
props.iconHeight,
|
|
79
|
+
props.pinColor
|
|
80
|
+
]);
|
|
81
|
+
|
|
55
82
|
return null;
|
|
56
83
|
}
|