@cn-map/gaode-react-extra 0.0.0 → 0.0.1
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/LICENSE +21 -21
- package/README.md +114 -3
- package/dist/index.d.ts +68 -0
- package/dist/index.js +534 -0
- package/package.json +69 -7
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2024-present Jungzl.
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024-present Jungzl.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,6 +1,117 @@
|
|
|
1
1
|
# @cn-map/gaode-react-extra
|
|
2
2
|
|
|
3
|
-
高德地图 react
|
|
3
|
+
高德地图 react 扩展组件,基于 [`@cn-map/gaode-react`](https://github.com/Jungzl/amap/tree/main/packages/react) 的 `GLCustomLayer` 与 [`@react-three/fiber`](https://r3f.docs.pmnd.rs) 集成 three.js 3D 渲染图层。
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @cn-map/gaode-react-extra three@0.162.0 @types/three@0.162.0
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
yarn add @cn-map/gaode-react-extra three@0.162.0 @types/three@0.162.0
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pnpm add @cn-map/gaode-react-extra three@0.162.0 @types/three@0.162.0
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
bun add @cn-map/gaode-react-extra three@0.162.0 @types/three@0.162.0
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
> ⚠️ 由于 [`r163`](https://github.com/mrdoob/three.js/releases/tag/r163) 移除了 WebGL 1 支持,且高德 JS API 2.0 的 GL 上下文基于 WebGL 1,`three` 版本不得高于 `0.162.0`。
|
|
24
|
+
|
|
25
|
+
> `@cn-map/gaode-react` 与 `@react-three/fiber` 是 peer 依赖,npm 7+ / pnpm 会自动安装,yarn 需手动添加。
|
|
26
|
+
|
|
27
|
+
TypeScript 类型见 [`@cn-map/gaode-types`](../types/README.md)。
|
|
28
|
+
|
|
29
|
+
## 使用
|
|
30
|
+
|
|
31
|
+
| 导出名称 | 类型 | 描述 |
|
|
32
|
+
| ---------- | ---- | --------------------------------------------- |
|
|
33
|
+
| `R3FLayer` | 组件 | 基于 `GLCustomLayer` 的 `three.js` 3D渲染图层 |
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
import { R3FLayer } from '@cn-map/gaode-react-extra'
|
|
37
|
+
|
|
38
|
+
const Demo = () => (
|
|
39
|
+
<R3FLayer>
|
|
40
|
+
<ambientLight intensity={Math.PI / 2} />
|
|
41
|
+
<spotLight angle={0.15} decay={0} intensity={Math.PI} penumbra={1} position={[10, 10, 10]} />
|
|
42
|
+
<pointLight decay={0} intensity={Math.PI} position={[-10, -10, -10]} />
|
|
43
|
+
<mesh>
|
|
44
|
+
<boxGeometry args={[1, 1, 1]} />
|
|
45
|
+
<meshStandardMaterial color="orange" />
|
|
46
|
+
</mesh>
|
|
47
|
+
</R3FLayer>
|
|
48
|
+
)
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## 地理坐标
|
|
52
|
+
|
|
53
|
+
`GeoGroup` 可以直接用经纬度放置对象,内部会通过 `map.customCoords` 转换到当前图层坐标系;children 仍使用相对该经纬度的局部坐标。
|
|
54
|
+
|
|
55
|
+
```tsx
|
|
56
|
+
import { GeoGroup, R3FLayer } from '@cn-map/gaode-react-extra'
|
|
57
|
+
|
|
58
|
+
const Demo = () => (
|
|
59
|
+
<R3FLayer>
|
|
60
|
+
<GeoGroup altitude={100} position={[120.1, 30.2]}>
|
|
61
|
+
<mesh>
|
|
62
|
+
<boxGeometry />
|
|
63
|
+
<meshStandardMaterial />
|
|
64
|
+
</mesh>
|
|
65
|
+
</GeoGroup>
|
|
66
|
+
</R3FLayer>
|
|
67
|
+
)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
需要批量转换或读取转换函数时使用 `useMapCoordinates()`:
|
|
71
|
+
|
|
72
|
+
```tsx
|
|
73
|
+
const coordinates = useMapCoordinates()
|
|
74
|
+
const positions = coordinates.lngLatsToScene(lnglats)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
转换使用的 origin 与图层相机的 origin 一致,因此 2D/3D、平移、缩放和旋转下都保持对齐。
|
|
78
|
+
|
|
79
|
+
`position` 也支持三元组 `[lng, lat, altitude]`;如果同时传了 `altitude`,以 `altitude` 为准。
|
|
80
|
+
|
|
81
|
+
## 2D 地图
|
|
82
|
+
|
|
83
|
+
高德地图的 `viewMode` 默认为 `2D`。`R3FLayer` 会根据当前视图自动选择相机:`2D` 使用 `OrthographicCamera`,`3D` 使用 `PerspectiveCamera`,并在每个地图帧同步 AMap 的相机参数,无需额外配置。地图旋转(`rotation`)也会同步到相机。地图平移时相机会跟随当前地图中心,固定世界坐标的对象保持与地图的地理锚定。
|
|
84
|
+
|
|
85
|
+
> 如果通过 `camera` 传入自定义相机,或在子组件中通过 `makeDefault`(如 drei 的 `<PerspectiveCamera makeDefault />`)切换相机,2D 下必须使用正交相机、3D 下必须使用透视相机;相机类型不匹配时会输出 warning,并可能渲染空白或位置错误。
|
|
86
|
+
|
|
87
|
+
> ⚠️ 一个地图 canvas 只支持挂载一个 `R3FLayer`。第二个实例会输出 warning 并跳过挂载,不会破坏第一个图层。第二个实例不会自动接管;需要在第一个卸载后重新挂载。
|
|
88
|
+
|
|
89
|
+
## renderer 所有权
|
|
90
|
+
|
|
91
|
+
通过 `gl` 传入 renderer 实例或同步工厂时,`R3FLayer` 会在挂载期间临时修改 `render`、`autoClear` 和 `forceContextLoss`,并在卸载完成后恢复原值;不会 dispose renderer,也不会让 AMap 的 GL context 丢失。若要在别处复用同一个 renderer,请等 unmount 完成后再使用。
|
|
92
|
+
|
|
93
|
+
## 更多用法
|
|
94
|
+
|
|
95
|
+
- `origin` 指定 Three.js 坐标系原点,省略时使用初始地图中心点。
|
|
96
|
+
- `ref` 指向底层 `AMap.GLCustomLayer`;`onComplete` 会转发图层 complete 事件。
|
|
97
|
+
- `gl` 支持 `WebGLRenderer` 实例、同步工厂或 `WebGLRendererParameters`;异步工厂以及使用其他 canvas/context 的 renderer 会被拒绝。
|
|
98
|
+
- React 19 的 `StrictMode` 和 `Activity` 已覆盖测试;`Activity` 隐藏时 R3F root 保留、帧循环暂停。
|
|
99
|
+
|
|
100
|
+
## 渲染循环模式
|
|
101
|
+
|
|
102
|
+
| `frameloop` | 说明 |
|
|
103
|
+
| ------------------ | ----------------------------------------------------------------------------- |
|
|
104
|
+
| `demand`(默认值) | 按需渲染,需在子组件中调用 `invalidate()`(例如在 `useFrame` 中)请求地图重绘 |
|
|
105
|
+
| `always` | 持续渲染,内部通过 `requestAnimationFrame` 持续触发地图重绘 |
|
|
106
|
+
| `never` | 不主动渲染,需自行调用 `advance()` 请求渲染(用于完全接管渲染时机) |
|
|
107
|
+
|
|
108
|
+
```tsx
|
|
109
|
+
import { useFrame } from '@react-three/fiber'
|
|
110
|
+
|
|
111
|
+
const Animation = () => {
|
|
112
|
+
// frameloop="demand"(默认值)时,需要调用 invalidate() 请求地图重绘
|
|
113
|
+
useFrame(({ invalidate }) => invalidate())
|
|
114
|
+
|
|
115
|
+
return null
|
|
116
|
+
}
|
|
117
|
+
```
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { MapCoordinates } from "@cn-map/gaode-extra-shared";
|
|
2
|
+
import { FC, PropsWithChildren, ReactElement } from "react";
|
|
3
|
+
import { GLCustomLayerProps } from "@cn-map/gaode-react";
|
|
4
|
+
import { GLProps, RenderProps, Renderer } from "@react-three/fiber";
|
|
5
|
+
import { WebGLRenderer, WebGLRendererParameters } from "three";
|
|
6
|
+
/**
|
|
7
|
+
* Access the coordinate converter of the surrounding `R3FLayer`.
|
|
8
|
+
*
|
|
9
|
+
* The converter uses the same origin as the layer camera, so converted
|
|
10
|
+
* positions remain aligned with the map.
|
|
11
|
+
*/
|
|
12
|
+
export declare function useMapCoordinates(): MapCoordinates;
|
|
13
|
+
interface GeoGroupProps extends PropsWithChildren {
|
|
14
|
+
/** Geographic position of the object. */
|
|
15
|
+
position: AMap.LngLatLike;
|
|
16
|
+
/**
|
|
17
|
+
* Altitude mapped to the scene's Z axis.
|
|
18
|
+
* Overrides a third tuple element such as `[lng, lat, altitude]`.
|
|
19
|
+
*/
|
|
20
|
+
altitude?: number;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Place a R3F object using a geographic position.
|
|
24
|
+
*
|
|
25
|
+
* Children keep their local coordinates relative to the geographic origin.
|
|
26
|
+
*/
|
|
27
|
+
declare const GeoGroup: ({ position, altitude, children }: GeoGroupProps) => ReactElement;
|
|
28
|
+
type GLDefaults = Omit<WebGLRendererParameters, "canvas"> & {
|
|
29
|
+
canvas: HTMLCanvasElement;
|
|
30
|
+
};
|
|
31
|
+
type GLFactory = (...args: never[]) => unknown;
|
|
32
|
+
type GLObjectProps = Exclude<GLProps, Renderer | GLFactory>;
|
|
33
|
+
type SyncRendererFactory = (defaults: GLDefaults) => WebGLRenderer;
|
|
34
|
+
/**
|
|
35
|
+
* `R3FLayer` needs the renderer before its first map frame, so async factories are excluded.
|
|
36
|
+
* Renderer results must also reuse the AMap canvas and context, which is checked at runtime.
|
|
37
|
+
*/
|
|
38
|
+
type R3FLayerGLProps = WebGLRenderer | SyncRendererFactory | GLObjectProps;
|
|
39
|
+
interface R3FLayerProps extends PropsWithChildren<Omit<RenderProps<HTMLCanvasElement>, "frameloop" | "gl"> & Omit<GLCustomLayerProps, "init" | "render">> {
|
|
40
|
+
/**
|
|
41
|
+
* R3F renderer、同步工厂或 renderer 参数。
|
|
42
|
+
*
|
|
43
|
+
* 工厂必须同步返回 renderer,且 renderer 必须复用 `GLCustomLayer` 的 `canvas` 与 WebGL `context`。
|
|
44
|
+
* renderer 参数中的 `canvas` / `context` 会被忽略,始终使用图层的上下文。
|
|
45
|
+
* 异步工厂或使用其他 canvas/context 的 renderer 会被拒绝。
|
|
46
|
+
*/
|
|
47
|
+
gl?: R3FLayerGLProps;
|
|
48
|
+
/**
|
|
49
|
+
* Three.js渲染坐标系原点经纬度
|
|
50
|
+
* @default 地图中心点
|
|
51
|
+
*/
|
|
52
|
+
origin?: AMap.LngLatLike;
|
|
53
|
+
/**
|
|
54
|
+
* R3F's render mode. Set to `demand` to only render on state change or `never` to take control.
|
|
55
|
+
* 与 `@react-three/fiber` 的 `Canvas` 不同,默认值为 `"demand"`
|
|
56
|
+
*
|
|
57
|
+
* @default "demand"
|
|
58
|
+
*
|
|
59
|
+
* @see https://docs.pmnd.rs/react-three-fiber/advanced/scaling-performance#on-demand-rendering
|
|
60
|
+
*/
|
|
61
|
+
frameloop?: RenderProps<HTMLCanvasElement>["frameloop"];
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* `GLCustomLayer` 与 [`@react-three/fiber`](https://r3f.docs.pmnd.rs) 结合使用的组件
|
|
65
|
+
* 可以直接在子组件中使用 `@react-three/fiber` 组件,无需手动处理 `GLCustomLayer` 的 `init` 和 `render`
|
|
66
|
+
*/
|
|
67
|
+
declare const R3FLayer: FC<R3FLayerProps>;
|
|
68
|
+
export { GeoGroup, type GeoGroupProps, R3FLayer, type R3FLayerGLProps, type R3FLayerProps };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,534 @@
|
|
|
1
|
+
import { createMapCoordinates, releaseCanvas, syncMapCamera, tryAcquireCanvas } from "@cn-map/gaode-extra-shared";
|
|
2
|
+
import { logger, lostContextError } from "@cn-map/gaode-shared/utils";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { Fragment, StrictMode, Suspense, createContext, use, useCallback, useInsertionEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
5
|
+
import { jsx } from "react/jsx-runtime";
|
|
6
|
+
import { GLCustomLayer, splitProps, useMapInstance } from "@cn-map/gaode-react";
|
|
7
|
+
import { FiberProvider, traverseFiber, useActivityBridge, useContextBridge, useFiber } from "its-fine";
|
|
8
|
+
import { _roots, applyProps, createRoot, events, unmountComponentAtNode } from "@react-three/fiber";
|
|
9
|
+
import { WebGLRenderer } from "three";
|
|
10
|
+
// src/r3f-layer/coordinates-context.ts
|
|
11
|
+
const EMPTY = Symbol("No MapCoordinates");
|
|
12
|
+
const MapCoordinatesContext = createContext(EMPTY);
|
|
13
|
+
/**
|
|
14
|
+
* Access the coordinate converter of the surrounding `R3FLayer`.
|
|
15
|
+
*
|
|
16
|
+
* The converter uses the same origin as the layer camera, so converted
|
|
17
|
+
* positions remain aligned with the map.
|
|
18
|
+
*/
|
|
19
|
+
function useMapCoordinates() {
|
|
20
|
+
const context = use(MapCoordinatesContext);
|
|
21
|
+
if (context === EMPTY) throw lostContextError("`MapCoordinates`");
|
|
22
|
+
return useMemo(() => createMapCoordinates(context.map, context.origin), [context]);
|
|
23
|
+
}
|
|
24
|
+
// src/r3f-layer/geo-group.tsx
|
|
25
|
+
/**
|
|
26
|
+
* Place a R3F object using a geographic position.
|
|
27
|
+
*
|
|
28
|
+
* Children keep their local coordinates relative to the geographic origin.
|
|
29
|
+
*/
|
|
30
|
+
const GeoGroup = ({ position, altitude, children }) => {
|
|
31
|
+
const coordinates = useMapCoordinates();
|
|
32
|
+
const scenePosition = useMemo(() => coordinates.lngLatToScene(position, altitude), [
|
|
33
|
+
altitude,
|
|
34
|
+
coordinates,
|
|
35
|
+
position
|
|
36
|
+
]);
|
|
37
|
+
return /* @__PURE__ */ jsx("group", {
|
|
38
|
+
position: scenePosition,
|
|
39
|
+
children
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
// src/r3f-layer/map-frame-bridge.ts
|
|
43
|
+
/**
|
|
44
|
+
* 帧外请求与 AMap 帧之间的桥
|
|
45
|
+
*
|
|
46
|
+
* 核心约束:
|
|
47
|
+
* - 同一帧内 `advance` 优先,不能被随后到达的 `redraw` 降级(否则 `useFrame` 链会断掉)
|
|
48
|
+
* - `runInMapFrame` 在回调前取走并清空请求,保证一次请求只消费一次
|
|
49
|
+
* - `inMapFrame` 支持嵌套,回调抛错时也会恢复
|
|
50
|
+
*/
|
|
51
|
+
var MapFrameBridge = class {
|
|
52
|
+
inMapFrame = false;
|
|
53
|
+
pending = null;
|
|
54
|
+
get isInMapFrame() {
|
|
55
|
+
return this.inMapFrame;
|
|
56
|
+
}
|
|
57
|
+
requestAdvance() {
|
|
58
|
+
this.pending = { kind: "advance" };
|
|
59
|
+
}
|
|
60
|
+
requestRedraw(scene, camera) {
|
|
61
|
+
if (this.pending?.kind === "advance") return;
|
|
62
|
+
this.pending = {
|
|
63
|
+
kind: "redraw",
|
|
64
|
+
draw: [scene, camera]
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
runInMapFrame(callback) {
|
|
68
|
+
const wasInMapFrame = this.inMapFrame;
|
|
69
|
+
const pending = this.pending;
|
|
70
|
+
this.pending = null;
|
|
71
|
+
this.inMapFrame = true;
|
|
72
|
+
try {
|
|
73
|
+
callback(pending);
|
|
74
|
+
} finally {
|
|
75
|
+
this.inMapFrame = wasInMapFrame;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
// src/r3f-layer/use-frame-loop.ts
|
|
80
|
+
/**
|
|
81
|
+
* 将 R3F 的 frameloop 接入 AMap 的帧调度
|
|
82
|
+
*/
|
|
83
|
+
function useFrameLoop(store, frameloop, requestAdvance) {
|
|
84
|
+
useLayoutEffect(() => {
|
|
85
|
+
if (!store) return;
|
|
86
|
+
if (!store.getState().gl) return;
|
|
87
|
+
if (frameloop !== "demand") return;
|
|
88
|
+
const setState = store.setState;
|
|
89
|
+
const { invalidate } = store.getState();
|
|
90
|
+
setState({
|
|
91
|
+
frameloop,
|
|
92
|
+
invalidate: () => requestAdvance()
|
|
93
|
+
});
|
|
94
|
+
requestAdvance();
|
|
95
|
+
return () => {
|
|
96
|
+
setState({
|
|
97
|
+
frameloop: "never",
|
|
98
|
+
invalidate
|
|
99
|
+
});
|
|
100
|
+
};
|
|
101
|
+
}, [
|
|
102
|
+
frameloop,
|
|
103
|
+
requestAdvance,
|
|
104
|
+
store
|
|
105
|
+
]);
|
|
106
|
+
useLayoutEffect(() => {
|
|
107
|
+
if (!store || frameloop !== "always") return;
|
|
108
|
+
let raf = 0;
|
|
109
|
+
const tick = () => {
|
|
110
|
+
requestAdvance();
|
|
111
|
+
raf = requestAnimationFrame(tick);
|
|
112
|
+
};
|
|
113
|
+
raf = requestAnimationFrame(tick);
|
|
114
|
+
return () => cancelAnimationFrame(raf);
|
|
115
|
+
}, [
|
|
116
|
+
frameloop,
|
|
117
|
+
requestAdvance,
|
|
118
|
+
store
|
|
119
|
+
]);
|
|
120
|
+
}
|
|
121
|
+
// src/r3f-layer/use-r3f-layer-render.ts
|
|
122
|
+
/**
|
|
123
|
+
* 同步 AMap 相机,并在 AMap 帧内执行 R3F 的 advance/redraw
|
|
124
|
+
*/
|
|
125
|
+
function useR3FLayerRender({ frameBridge, originLngLat }) {
|
|
126
|
+
const warnedCameraMismatchRef = useRef(false);
|
|
127
|
+
return useCallback(({ canvas }, { map }, { viewMode }) => {
|
|
128
|
+
const store = _roots.get(canvas)?.store;
|
|
129
|
+
if (!store) return;
|
|
130
|
+
const state = store.getState();
|
|
131
|
+
if (!state.gl) return;
|
|
132
|
+
state.gl.resetState();
|
|
133
|
+
const result = syncMapCamera({
|
|
134
|
+
map,
|
|
135
|
+
camera: state.camera,
|
|
136
|
+
viewMode,
|
|
137
|
+
origin: originLngLat
|
|
138
|
+
});
|
|
139
|
+
if (!result.synced && !warnedCameraMismatchRef.current) {
|
|
140
|
+
warnedCameraMismatchRef.current = true;
|
|
141
|
+
logger.warn(`[R3FLayer] ${result.viewMode} view requires a ${result.expected}`);
|
|
142
|
+
}
|
|
143
|
+
try {
|
|
144
|
+
frameBridge.runInMapFrame((pending) => {
|
|
145
|
+
if (pending?.kind === "advance") state.advance(performance.now(), true);
|
|
146
|
+
else if (pending?.kind === "redraw") state.gl.render(pending.draw[0], pending.draw[1]);
|
|
147
|
+
else state.gl.render(state.scene, state.camera);
|
|
148
|
+
});
|
|
149
|
+
} finally {
|
|
150
|
+
state.gl.resetState();
|
|
151
|
+
}
|
|
152
|
+
}, [frameBridge, originLngLat]);
|
|
153
|
+
}
|
|
154
|
+
// src/r3f-layer/use-r3f-root-tree.tsx
|
|
155
|
+
const resolveActivityBridge = React["Activity"] ? useActivityBridge : () => Fragment;
|
|
156
|
+
/**
|
|
157
|
+
* 来自 `@react-three/fiber` 的 `useBridge`
|
|
158
|
+
* 桥接上层 Context、StrictMode 和 Activity 可见性
|
|
159
|
+
* @see https://github.com/pmndrs/react-three-fiber/blob/13e5e0e152562a9cb64c31dee1d10a2af7aa39c4/packages/fiber/src/core/utils/react.tsx#L70-L95
|
|
160
|
+
*/
|
|
161
|
+
function useBridge() {
|
|
162
|
+
const fiber = useFiber();
|
|
163
|
+
const ContextBridge = useContextBridge();
|
|
164
|
+
const ActivityBridge = resolveActivityBridge();
|
|
165
|
+
return useMemo(() => ({ children }) => {
|
|
166
|
+
const Root = !!traverseFiber(fiber, true, (node) => node.type === StrictMode) ? StrictMode : Fragment;
|
|
167
|
+
return /* @__PURE__ */ jsx(Root, { children: /* @__PURE__ */ jsx(ActivityBridge, { children: /* @__PURE__ */ jsx(ContextBridge, { children }) }) });
|
|
168
|
+
}, [
|
|
169
|
+
fiber,
|
|
170
|
+
ContextBridge,
|
|
171
|
+
ActivityBridge
|
|
172
|
+
]);
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* 将 React children 渲染到 R3F root
|
|
176
|
+
*/
|
|
177
|
+
function useR3FRootTree({ children, root }) {
|
|
178
|
+
const Bridge = useBridge();
|
|
179
|
+
useLayoutEffect(() => {
|
|
180
|
+
if (!root) return;
|
|
181
|
+
root.render(/* @__PURE__ */ jsx(Bridge, { children: /* @__PURE__ */ jsx(Suspense, { children }) }));
|
|
182
|
+
}, [
|
|
183
|
+
children,
|
|
184
|
+
Bridge,
|
|
185
|
+
root
|
|
186
|
+
]);
|
|
187
|
+
}
|
|
188
|
+
// src/r3f-layer/use-r3f-store.ts
|
|
189
|
+
const DEFAULT_GL_PROPS = {
|
|
190
|
+
powerPreference: "high-performance",
|
|
191
|
+
antialias: true,
|
|
192
|
+
alpha: true
|
|
193
|
+
};
|
|
194
|
+
function isRenderer(value) {
|
|
195
|
+
return !!value && typeof value === "object" && "render" in value && "domElement" in value && "getContext" in value;
|
|
196
|
+
}
|
|
197
|
+
function isPromiseLike(value) {
|
|
198
|
+
return !!value && typeof value.then === "function";
|
|
199
|
+
}
|
|
200
|
+
function getRendererProps(gl) {
|
|
201
|
+
if (!gl || typeof gl === "function" || isRenderer(gl)) return void 0;
|
|
202
|
+
const { canvas: _canvas, context: _context, ...props } = gl;
|
|
203
|
+
return props;
|
|
204
|
+
}
|
|
205
|
+
function assertRendererUsesMapSurface(renderer, canvas, context) {
|
|
206
|
+
if (renderer.domElement !== canvas || renderer.getContext() !== context) throw new Error("[R3FLayer] the gl renderer must reuse the AMap canvas and WebGL context; use the canvas/context from the factory defaults");
|
|
207
|
+
}
|
|
208
|
+
function logConfigureError(error) {
|
|
209
|
+
logger.error("[R3FLayer] failed to configure the R3F root", error);
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* 在 R3F 之外创建 renderer,保证 AMap 的 canvas/context 不会被用户配置覆盖。
|
|
213
|
+
* 实例传给 `root.configure` 后 R3F 只释放缓存,不会 dispose renderer 或丢失上下文。
|
|
214
|
+
*/
|
|
215
|
+
function createRenderer(canvas, context, gl) {
|
|
216
|
+
const defaults = {
|
|
217
|
+
...DEFAULT_GL_PROPS,
|
|
218
|
+
canvas,
|
|
219
|
+
context
|
|
220
|
+
};
|
|
221
|
+
if (typeof gl === "function") {
|
|
222
|
+
const renderer = gl(defaults);
|
|
223
|
+
if (isPromiseLike(renderer)) {
|
|
224
|
+
Promise.resolve(renderer).catch(() => {});
|
|
225
|
+
throw new Error("[R3FLayer] gl factories must return a renderer synchronously; async factories are not supported");
|
|
226
|
+
}
|
|
227
|
+
if (!isRenderer(renderer)) throw new Error("[R3FLayer] gl factories must return a WebGLRenderer");
|
|
228
|
+
assertRendererUsesMapSurface(renderer, canvas, context);
|
|
229
|
+
return renderer;
|
|
230
|
+
}
|
|
231
|
+
if (isRenderer(gl)) {
|
|
232
|
+
assertRendererUsesMapSurface(gl, canvas, context);
|
|
233
|
+
return gl;
|
|
234
|
+
}
|
|
235
|
+
const params = gl ?? {};
|
|
236
|
+
return new WebGLRenderer({
|
|
237
|
+
...defaults,
|
|
238
|
+
...params,
|
|
239
|
+
canvas,
|
|
240
|
+
context
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* 创建并同步 R3F root/store,同时管理尺寸监听和资源清理
|
|
245
|
+
*/
|
|
246
|
+
function useR3FStoreSync({ frameBridge, map, renderProps, requestAdvance, requestRedraw }) {
|
|
247
|
+
const canvasRef = useRef(null);
|
|
248
|
+
const rootEntryRef = useRef(null);
|
|
249
|
+
const renderPropsRef = useRef(renderProps);
|
|
250
|
+
const lifecycleTokenRef = useRef(0);
|
|
251
|
+
const pendingRootCreatesRef = useRef(/* @__PURE__ */ new Map());
|
|
252
|
+
const claimedCanvasRef = useRef(null);
|
|
253
|
+
const ownerTokenRef = useRef(Symbol("R3FLayer"));
|
|
254
|
+
const warnedCanvasOwnershipRef = useRef(false);
|
|
255
|
+
const [ready, setReady] = useState(null);
|
|
256
|
+
renderPropsRef.current = renderProps;
|
|
257
|
+
const flushPendingRoot = useCallback((canvas) => {
|
|
258
|
+
const create = pendingRootCreatesRef.current.get(canvas);
|
|
259
|
+
if (!create) return;
|
|
260
|
+
pendingRootCreatesRef.current.delete(canvas);
|
|
261
|
+
create();
|
|
262
|
+
}, []);
|
|
263
|
+
const disposeRoot = useCallback((entry) => {
|
|
264
|
+
lifecycleTokenRef.current++;
|
|
265
|
+
pendingRootCreatesRef.current.delete(entry.canvas);
|
|
266
|
+
releaseCanvas(entry.canvas, ownerTokenRef.current);
|
|
267
|
+
if (claimedCanvasRef.current === entry.canvas) claimedCanvasRef.current = null;
|
|
268
|
+
if (rootEntryRef.current === entry) {
|
|
269
|
+
rootEntryRef.current = null;
|
|
270
|
+
if (canvasRef.current === entry.canvas) canvasRef.current = null;
|
|
271
|
+
}
|
|
272
|
+
unmountComponentAtNode(entry.canvas, () => {
|
|
273
|
+
entry.restoreRenderer?.();
|
|
274
|
+
entry.restoreRenderer = void 0;
|
|
275
|
+
flushPendingRoot(entry.canvas);
|
|
276
|
+
});
|
|
277
|
+
}, [flushPendingRoot]);
|
|
278
|
+
const releaseRoot = useCallback((entry) => {
|
|
279
|
+
disposeRoot(entry);
|
|
280
|
+
setReady((current) => current?.root === entry.root ? null : current);
|
|
281
|
+
}, [disposeRoot]);
|
|
282
|
+
useInsertionEffect(() => {
|
|
283
|
+
return () => {
|
|
284
|
+
pendingRootCreatesRef.current.clear();
|
|
285
|
+
const entry = rootEntryRef.current;
|
|
286
|
+
if (entry) disposeRoot(entry);
|
|
287
|
+
else if (claimedCanvasRef.current) {
|
|
288
|
+
releaseCanvas(claimedCanvasRef.current, ownerTokenRef.current);
|
|
289
|
+
claimedCanvasRef.current = null;
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
}, [disposeRoot]);
|
|
293
|
+
useLayoutEffect(() => {
|
|
294
|
+
const gl = map?.getGL();
|
|
295
|
+
if (!gl) {
|
|
296
|
+
const entry = rootEntryRef.current;
|
|
297
|
+
if (entry) releaseRoot(entry);
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
const canvas = gl.canvas;
|
|
301
|
+
const existingEntry = rootEntryRef.current;
|
|
302
|
+
if (existingEntry?.canvas === canvas) return;
|
|
303
|
+
if (existingEntry) releaseRoot(existingEntry);
|
|
304
|
+
if (!tryAcquireCanvas(canvas, ownerTokenRef.current)) {
|
|
305
|
+
if (!warnedCanvasOwnershipRef.current) {
|
|
306
|
+
warnedCanvasOwnershipRef.current = true;
|
|
307
|
+
logger.warn("[R3FLayer] only one R3FLayer can be mounted per map canvas; skipping this layer");
|
|
308
|
+
}
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
canvasRef.current = canvas;
|
|
312
|
+
claimedCanvasRef.current = canvas;
|
|
313
|
+
const token = ++lifecycleTokenRef.current;
|
|
314
|
+
const create = () => {
|
|
315
|
+
if (lifecycleTokenRef.current !== token) return;
|
|
316
|
+
if (_roots.has(canvas)) {
|
|
317
|
+
pendingRootCreatesRef.current.set(canvas, create);
|
|
318
|
+
unmountComponentAtNode(canvas, () => flushPendingRoot(canvas));
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
const root = createRoot(canvas);
|
|
322
|
+
const entry = {
|
|
323
|
+
canvas,
|
|
324
|
+
root
|
|
325
|
+
};
|
|
326
|
+
rootEntryRef.current = entry;
|
|
327
|
+
const configure = (renderer, rendererProps) => {
|
|
328
|
+
if (lifecycleTokenRef.current !== token) return;
|
|
329
|
+
const originalRender = renderer.render;
|
|
330
|
+
const originalAutoClear = renderer.autoClear;
|
|
331
|
+
const originalForceContextLoss = renderer.forceContextLoss;
|
|
332
|
+
entry.restoreRenderer = () => {
|
|
333
|
+
renderer.render = originalRender;
|
|
334
|
+
renderer.autoClear = originalAutoClear;
|
|
335
|
+
renderer.forceContextLoss = originalForceContextLoss;
|
|
336
|
+
};
|
|
337
|
+
renderer.autoClear = false;
|
|
338
|
+
renderer.forceContextLoss = () => {};
|
|
339
|
+
const currentRenderProps = renderPropsRef.current;
|
|
340
|
+
let configured;
|
|
341
|
+
try {
|
|
342
|
+
configured = root.configure({
|
|
343
|
+
dpr: window.devicePixelRatio,
|
|
344
|
+
events,
|
|
345
|
+
...currentRenderProps,
|
|
346
|
+
orthographic: map?.getView().type === "2D",
|
|
347
|
+
frameloop: "never",
|
|
348
|
+
gl: renderer,
|
|
349
|
+
onCreated: (state) => {
|
|
350
|
+
const draw = state.gl.render.bind(state.gl);
|
|
351
|
+
state.gl.render = (scene, camera) => {
|
|
352
|
+
if (frameBridge.isInMapFrame) {
|
|
353
|
+
draw(scene, camera);
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
requestRedraw(scene, camera);
|
|
357
|
+
};
|
|
358
|
+
currentRenderProps.onCreated?.(state);
|
|
359
|
+
},
|
|
360
|
+
camera: {
|
|
361
|
+
aspect: canvas.clientWidth / canvas.clientHeight,
|
|
362
|
+
...currentRenderProps.camera
|
|
363
|
+
},
|
|
364
|
+
size: {
|
|
365
|
+
width: canvas.clientWidth,
|
|
366
|
+
height: canvas.clientHeight,
|
|
367
|
+
top: canvas.offsetTop,
|
|
368
|
+
left: canvas.offsetLeft,
|
|
369
|
+
...currentRenderProps.size
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
} catch (error) {
|
|
373
|
+
logConfigureError(error);
|
|
374
|
+
releaseRoot(entry);
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
const onReady = () => {
|
|
378
|
+
if (lifecycleTokenRef.current !== token || rootEntryRef.current !== entry) return;
|
|
379
|
+
if (rendererProps) try {
|
|
380
|
+
applyProps(renderer, rendererProps);
|
|
381
|
+
} catch (error) {
|
|
382
|
+
logger.error("[R3FLayer] failed to apply gl renderer props", error);
|
|
383
|
+
}
|
|
384
|
+
const store = _roots.get(canvas)?.store;
|
|
385
|
+
if (!store) {
|
|
386
|
+
logger.error("[R3FLayer] the R3F root store is not available after configuration");
|
|
387
|
+
releaseRoot(entry);
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
setReady({
|
|
391
|
+
root,
|
|
392
|
+
store
|
|
393
|
+
});
|
|
394
|
+
};
|
|
395
|
+
if (configured.status !== "fulfilled") {
|
|
396
|
+
logConfigureError(configured.status === "rejected" ? configured.reason : /* @__PURE__ */ new Error("[R3FLayer] R3F root configuration did not complete synchronously"));
|
|
397
|
+
releaseRoot(entry);
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
onReady();
|
|
401
|
+
};
|
|
402
|
+
let renderer;
|
|
403
|
+
const glConfig = renderPropsRef.current.gl;
|
|
404
|
+
const rendererProps = getRendererProps(glConfig);
|
|
405
|
+
try {
|
|
406
|
+
renderer = createRenderer(canvas, gl, glConfig);
|
|
407
|
+
} catch (error) {
|
|
408
|
+
logger.error("[R3FLayer] failed to prepare the R3F renderer", error);
|
|
409
|
+
releaseRoot(entry);
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
configure(renderer, rendererProps);
|
|
413
|
+
};
|
|
414
|
+
create();
|
|
415
|
+
}, [
|
|
416
|
+
flushPendingRoot,
|
|
417
|
+
frameBridge,
|
|
418
|
+
map,
|
|
419
|
+
releaseRoot,
|
|
420
|
+
requestRedraw
|
|
421
|
+
]);
|
|
422
|
+
useLayoutEffect(() => {
|
|
423
|
+
if (!ready) return;
|
|
424
|
+
const { store } = ready;
|
|
425
|
+
const { advance } = store.getState();
|
|
426
|
+
store.setState({ advance: (timestamp, runGlobalEffects) => {
|
|
427
|
+
if (frameBridge.isInMapFrame) {
|
|
428
|
+
advance(timestamp, runGlobalEffects);
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
requestAdvance();
|
|
432
|
+
} });
|
|
433
|
+
return () => {
|
|
434
|
+
store.setState({ advance });
|
|
435
|
+
};
|
|
436
|
+
}, [
|
|
437
|
+
frameBridge,
|
|
438
|
+
map,
|
|
439
|
+
requestAdvance,
|
|
440
|
+
ready
|
|
441
|
+
]);
|
|
442
|
+
useLayoutEffect(() => {
|
|
443
|
+
if (!map) return;
|
|
444
|
+
if (!ready) return;
|
|
445
|
+
const canvas = canvasRef.current;
|
|
446
|
+
if (!canvas) return;
|
|
447
|
+
const { store } = ready;
|
|
448
|
+
const onResize = () => {
|
|
449
|
+
const { clientWidth, clientHeight, offsetLeft, offsetTop } = canvas;
|
|
450
|
+
const { setDpr, setSize, gl, camera } = store.getState();
|
|
451
|
+
if (map.getView().type === "3D") camera.aspect = clientWidth / clientHeight;
|
|
452
|
+
camera.updateProjectionMatrix();
|
|
453
|
+
gl.setSize(clientWidth, clientHeight);
|
|
454
|
+
setDpr(window.devicePixelRatio);
|
|
455
|
+
setSize(clientWidth, clientHeight, offsetTop, offsetLeft);
|
|
456
|
+
};
|
|
457
|
+
const ro = new ResizeObserver(onResize);
|
|
458
|
+
ro.observe(canvas);
|
|
459
|
+
return () => {
|
|
460
|
+
ro.disconnect();
|
|
461
|
+
};
|
|
462
|
+
}, [map, ready]);
|
|
463
|
+
return ready;
|
|
464
|
+
}
|
|
465
|
+
// src/r3f-layer/r3f-layer.tsx
|
|
466
|
+
const noopInit = () => {};
|
|
467
|
+
/**
|
|
468
|
+
* `GLCustomLayer` 与 [`@react-three/fiber`](https://r3f.docs.pmnd.rs) 结合使用的组件(内部实现)
|
|
469
|
+
*/
|
|
470
|
+
const R3FLayerImpl = ({ children, frameloop = "demand", origin, ref, onComplete, map: _map, ...props }) => {
|
|
471
|
+
const map = useMapInstance(_map);
|
|
472
|
+
const [layerProps, r3fProps] = splitProps(props, [
|
|
473
|
+
"cacheSize",
|
|
474
|
+
"ce",
|
|
475
|
+
"depthTest",
|
|
476
|
+
"opacity",
|
|
477
|
+
"rejectMapMask",
|
|
478
|
+
"visible",
|
|
479
|
+
"zIndex",
|
|
480
|
+
"zooms"
|
|
481
|
+
]);
|
|
482
|
+
const originLngLat = useMemo(() => {
|
|
483
|
+
if (origin) return origin;
|
|
484
|
+
return map?.getCenter().toArray() || [0, 0];
|
|
485
|
+
}, [origin, map]);
|
|
486
|
+
const [frameBridge] = useState(() => new MapFrameBridge());
|
|
487
|
+
const requestAdvance = useCallback(() => {
|
|
488
|
+
frameBridge.requestAdvance();
|
|
489
|
+
map?.render();
|
|
490
|
+
}, [frameBridge, map]);
|
|
491
|
+
const ready = useR3FStoreSync({
|
|
492
|
+
frameBridge,
|
|
493
|
+
map,
|
|
494
|
+
renderProps: r3fProps,
|
|
495
|
+
requestAdvance,
|
|
496
|
+
requestRedraw: useCallback((scene, camera) => {
|
|
497
|
+
frameBridge.requestRedraw(scene, camera);
|
|
498
|
+
map?.render();
|
|
499
|
+
}, [frameBridge, map])
|
|
500
|
+
});
|
|
501
|
+
const root = ready?.root ?? null;
|
|
502
|
+
useFrameLoop(ready?.store ?? null, frameloop, requestAdvance);
|
|
503
|
+
const coordinateContext = useMemo(() => map ? {
|
|
504
|
+
map,
|
|
505
|
+
origin: originLngLat
|
|
506
|
+
} : null, [map, originLngLat]);
|
|
507
|
+
useR3FRootTree({
|
|
508
|
+
children: useMemo(() => coordinateContext ? /* @__PURE__ */ jsx(MapCoordinatesContext, {
|
|
509
|
+
value: coordinateContext,
|
|
510
|
+
children
|
|
511
|
+
}) : children, [children, coordinateContext]),
|
|
512
|
+
root
|
|
513
|
+
});
|
|
514
|
+
const render = useR3FLayerRender({
|
|
515
|
+
frameBridge,
|
|
516
|
+
originLngLat
|
|
517
|
+
});
|
|
518
|
+
return /* @__PURE__ */ jsx(GLCustomLayer, {
|
|
519
|
+
ref,
|
|
520
|
+
init: noopInit,
|
|
521
|
+
render,
|
|
522
|
+
onComplete,
|
|
523
|
+
...layerProps,
|
|
524
|
+
map: map ?? void 0
|
|
525
|
+
});
|
|
526
|
+
};
|
|
527
|
+
/**
|
|
528
|
+
* `GLCustomLayer` 与 [`@react-three/fiber`](https://r3f.docs.pmnd.rs) 结合使用的组件
|
|
529
|
+
* 可以直接在子组件中使用 `@react-three/fiber` 组件,无需手动处理 `GLCustomLayer` 的 `init` 和 `render`
|
|
530
|
+
*/
|
|
531
|
+
const R3FLayer = (props) => {
|
|
532
|
+
return /* @__PURE__ */ jsx(FiberProvider, { children: /* @__PURE__ */ jsx(R3FLayerImpl, { ...props }) });
|
|
533
|
+
};
|
|
534
|
+
export { GeoGroup, R3FLayer, useMapCoordinates };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cn-map/gaode-react-extra",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.1",
|
|
4
4
|
"description": "高德地图 react 扩展组件",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"amap",
|
|
@@ -9,18 +9,80 @@
|
|
|
9
9
|
"高德",
|
|
10
10
|
"高德地图"
|
|
11
11
|
],
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://gtihub.com/Jungzl/amap/issues"
|
|
14
|
+
},
|
|
12
15
|
"license": "MIT",
|
|
13
|
-
"author": "jungzl",
|
|
14
16
|
"repository": {
|
|
15
17
|
"type": "git",
|
|
16
18
|
"url": "git+https://github.com/Jungzl/amap.git",
|
|
17
19
|
"directory": "packages/react-extra"
|
|
18
20
|
},
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
+
"files": [
|
|
22
|
+
"dist"
|
|
23
|
+
],
|
|
24
|
+
"type": "module",
|
|
25
|
+
"sideEffects": false,
|
|
26
|
+
"main": "dist/index.js",
|
|
27
|
+
"module": "dist/index.js",
|
|
28
|
+
"types": "dist/index.d.ts",
|
|
29
|
+
"imports": {
|
|
30
|
+
"#lib": "./src/index.ts"
|
|
31
|
+
},
|
|
32
|
+
"exports": {
|
|
33
|
+
".": {
|
|
34
|
+
"types": "./dist/index.d.ts",
|
|
35
|
+
"default": "./dist/index.js"
|
|
36
|
+
},
|
|
37
|
+
"./package.json": "./package.json"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@cn-map/gaode-extra-shared": "^0.0.1",
|
|
41
|
+
"@cn-map/gaode-shared": "^0.0.1"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@cn-map/gaode-react": "0.0.1",
|
|
45
|
+
"@cn-map/gaode-types": "^2.1.0",
|
|
46
|
+
"@react-three/fiber": "^9.8.2",
|
|
47
|
+
"@types/react": "^19.3.0",
|
|
48
|
+
"@types/react-dom": "^19.3.0",
|
|
49
|
+
"@types/three": "0.162.0",
|
|
50
|
+
"@vitejs/plugin-react": "^6.3.0",
|
|
51
|
+
"its-fine": "^2.1.1",
|
|
52
|
+
"react": "^19.3.1",
|
|
53
|
+
"react-dom": "^19.3.1",
|
|
54
|
+
"three": "0.162.0",
|
|
55
|
+
"vitest-browser-react": "^2.3.0"
|
|
56
|
+
},
|
|
57
|
+
"peerDependencies": {
|
|
58
|
+
"@cn-map/gaode-react": "^0.0.1",
|
|
59
|
+
"@cn-map/gaode-types": "^2.0.0",
|
|
60
|
+
"@react-three/fiber": "^9.8.1",
|
|
61
|
+
"@types/react": "^19.2.0",
|
|
62
|
+
"@types/three": "<=0.162.0",
|
|
63
|
+
"its-fine": "^2.1.1",
|
|
64
|
+
"react": "^19.2.0",
|
|
65
|
+
"react-dom": "^19.2.0",
|
|
66
|
+
"three": "<=0.162.0"
|
|
21
67
|
},
|
|
22
|
-
"
|
|
23
|
-
"
|
|
68
|
+
"peerDependenciesMeta": {
|
|
69
|
+
"@cn-map/gaode-types": {
|
|
70
|
+
"optional": true
|
|
71
|
+
},
|
|
72
|
+
"@types/react": {
|
|
73
|
+
"optional": true
|
|
74
|
+
},
|
|
75
|
+
"@types/three": {
|
|
76
|
+
"optional": true
|
|
77
|
+
}
|
|
24
78
|
},
|
|
25
|
-
"
|
|
79
|
+
"scripts": {
|
|
80
|
+
"build": "tsdown",
|
|
81
|
+
"dev": "tsdown --watch",
|
|
82
|
+
"check:type": "tsc",
|
|
83
|
+
"test": "vitest run",
|
|
84
|
+
"test:watch": "vitest",
|
|
85
|
+
"test:unit": "vitest run -p unit",
|
|
86
|
+
"test:e2e": "vitest run -p e2e"
|
|
87
|
+
}
|
|
26
88
|
}
|