@trapar-waves/react-three-maplibre 1.1.23 → 2.0.0
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 +40 -67
- package/assets/readme/headers/contributing.svg +15 -0
- package/assets/readme/headers/features.svg +15 -0
- package/assets/readme/headers/getting-started.svg +15 -0
- package/assets/readme/headers/license.svg +15 -0
- package/assets/readme/headers/project-structure.svg +15 -0
- package/assets/readme/headers/tech-stack.svg +15 -0
- package/assets/readme/headers/usage.svg +15 -0
- package/assets/readme/hero.svg +67 -0
- package/environments/.env +2 -1
- package/eslint.config.js +11 -9
- package/lint-staged.config.js +1 -1
- package/package.json +35 -30
- package/pnpm-workspace.yaml +9 -0
- package/readme/README-CN.md +69 -101
- package/readme/README-JP.md +69 -101
- package/readme/README-RU.md +53 -85
- package/rsbuild.config.ts +46 -1
- package/src/app.tsx +217 -0
- package/src/index.tsx +4 -4
- package/tsconfig.json +1 -1
- package/src/App.tsx +0 -91
- /package/src/{env.d.ts → environment.d.ts} +0 -0
package/readme/README-RU.md
CHANGED
|
@@ -9,105 +9,76 @@
|
|
|
9
9
|
|
|
10
10
|
---
|
|
11
11
|
|
|
12
|
-
[English](../README.md) | [中文](
|
|
12
|
+
[English](../README.md) | [中文](./README-CN.md) | [日本語](./README-JP.md)
|
|
13
13
|
|
|
14
14
|
> Библиотека на базе React, интегрирующая Three.js, MapLibre и AntV для расширенной геопространственной 3D-визуализации.
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+

|
|
17
|
+
|
|
18
|
+

|
|
17
19
|
|
|
18
20
|
- **Геопространственная визуализация:** Комбинирует `@antv/l7` и `maplibre-gl` для мощного рендеринга геопространственных данных с пользовательскими слоями карт.
|
|
19
21
|
- **3D-рендеринг с React:** Использует `@react-three/fiber` и `@react-three/drei` для интеграции Three.js в рабочий процесс на базе React для декларативного управления 3D-сценами.
|
|
20
22
|
- **Интеграция настраиваемого UI:** Обеспечивает бесперебойную интеграцию с React (`react`, `react-dom`) для создания интерактивных геопространственных приложений.
|
|
21
23
|
- **Стилизация с упором на утилиты:** Применяет `tailwindcss` для гибкой и быстрой стилизации компонентов и макетов.
|
|
22
|
-
-
|
|
24
|
+
- **Типобезопасность:** Использует TypeScript для обеспечения типобезопасности и улучшения опыта разработчика во время разработки.
|
|
23
25
|
- **Быстрый рабочий процесс разработки:** Использует `rsbuild` для оптимизированных сборок и эффективной производительности сервера разработки.
|
|
24
26
|
- **Богатая библиотека компонентов:** Интегрируется с `three-stdlib` и `@react-three/drei` для переиспользуемых утилит и компонентов Three.js.
|
|
25
27
|
- **Интерактивность карт:** Реализует `react-map-gl` для интерактивных элементов управления картами и навигации на клиентской стороне в геопространственных контекстах.
|
|
26
28
|
- **Дополнения AntV:** Включает `@antv/l7-maps` для дополнительных возможностей слоев карт и инструментов визуализации.
|
|
27
29
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
- **Фреймворк/Библиотека:** React
|
|
31
|
-
- **UI-тулкит/Стилизация:** Tailwind CSS
|
|
32
|
-
- **3D-рендеринг:** Three.js (`@react-three/fiber`, `@react-three/drei`)
|
|
33
|
-
- **Геопространственные библиотеки:** MapLibre GL, AntV L7
|
|
34
|
-
- **Инструмент сборки:** Rsbuild
|
|
35
|
-
- **Язык:** TypeScript
|
|
36
|
-
|
|
37
|
-
Полный список зависимостей см. в [package.json](package.json).
|
|
30
|
+

|
|
38
31
|
|
|
39
|
-
|
|
32
|
+
- **Фреймворк/библиотека:** `React`
|
|
33
|
+
- **UI-тулкит/стилизация:** `Tailwind CSS`
|
|
34
|
+
- **3D-рендеринг:** `Three.js` (`@react-three/fiber`, `@react-three/drei`)
|
|
35
|
+
- **Геопространственные библиотеки:** `MapLibre GL`, `AntV L7`
|
|
36
|
+
- **Инструмент сборки:** `Rsbuild`
|
|
37
|
+
- **Язык:** `TypeScript`
|
|
40
38
|
|
|
41
|
-
|
|
39
|
+
Полный список зависимостей смотрите в [package.json](../package.json).
|
|
42
40
|
|
|
43
|
-
|
|
41
|
+

|
|
44
42
|
|
|
45
|
-
|
|
43
|
+
## Предварительные требования
|
|
46
44
|
|
|
47
|
-
- Node.js (рекомендуется
|
|
48
|
-
-
|
|
49
|
-
|
|
50
|
-
```bash
|
|
51
|
-
node -v
|
|
52
|
-
npm -v
|
|
53
|
-
```
|
|
45
|
+
- Node.js (рекомендуется >= 18.x)
|
|
46
|
+
- Менеджер пакетов (npm, yarn или pnpm)
|
|
54
47
|
|
|
55
48
|
### Установка
|
|
56
49
|
|
|
57
50
|
1. Создайте новый проект с помощью шаблона:
|
|
58
51
|
|
|
59
|
-
```bash
|
|
60
|
-
pnpm create trapar-waves
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
2. Перейдите в каталог вашего проекта и установите зависимости:
|
|
52
|
+
```bash
|
|
53
|
+
pnpm create trapar-waves
|
|
54
|
+
```
|
|
64
55
|
|
|
65
|
-
|
|
66
|
-
cd your-project-name
|
|
67
|
-
pnpm install
|
|
68
|
-
# or
|
|
69
|
-
npm install
|
|
70
|
-
# or
|
|
71
|
-
yarn install
|
|
72
|
-
```
|
|
56
|
+
2. Перейдите в директорию проекта и установите зависимости:
|
|
73
57
|
|
|
74
|
-
|
|
58
|
+
```bash
|
|
59
|
+
pnpm install
|
|
60
|
+
```
|
|
75
61
|
|
|
76
|
-
Запустите сервер
|
|
77
|
-
|
|
78
|
-
```bash
|
|
79
|
-
pnpm dev
|
|
80
|
-
# or
|
|
81
|
-
npm run dev
|
|
82
|
-
# or
|
|
83
|
-
yarn dev
|
|
84
|
-
```
|
|
62
|
+
3. Запустите сервер разработки:
|
|
85
63
|
|
|
86
|
-
|
|
64
|
+
```bash
|
|
65
|
+
pnpm dev
|
|
66
|
+
```
|
|
87
67
|
|
|
88
|
-
|
|
68
|
+

|
|
89
69
|
|
|
90
|
-
Чтобы создать сборку для продакшена:
|
|
91
|
-
|
|
92
|
-
```bash
|
|
93
|
-
pnpm build
|
|
94
|
-
# or
|
|
95
|
-
npm run build
|
|
96
|
-
# or
|
|
97
|
-
yarn build
|
|
98
70
|
```
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
#
|
|
105
|
-
|
|
106
|
-
#
|
|
107
|
-
yarn preview
|
|
71
|
+
├── public/ # Статические ресурсы
|
|
72
|
+
├── src/ # Исходный код
|
|
73
|
+
│ ├── App.tsx # Основной компонент приложения
|
|
74
|
+
│ └── index.tsx # Точка входа
|
|
75
|
+
├── rsbuild.config.ts # Конфигурация Rsbuild
|
|
76
|
+
├── tsconfig.json # Конфигурация TypeScript
|
|
77
|
+
├── eslint.config.js # Конфигурация ESLint
|
|
78
|
+
└── package.json # Зависимости и скрипты проекта
|
|
108
79
|
```
|
|
109
80
|
|
|
110
|
-
|
|
81
|
+

|
|
111
82
|
|
|
112
83
|
Эта библиотека предназначена для использования в качестве шаблона для создания приложений геопространственной 3D-визуализации. Она предоставляет фундаментальную настройку с React, Three.js, MapLibre GL и AntV L7.
|
|
113
84
|
|
|
@@ -142,7 +113,8 @@ const latLon = {
|
|
|
142
113
|
latitude: 31.215175,
|
|
143
114
|
longitude: 121.417463,
|
|
144
115
|
};
|
|
145
|
-
const
|
|
116
|
+
const MAP_STYLE_URL
|
|
117
|
+
= "https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json";
|
|
146
118
|
|
|
147
119
|
function App() {
|
|
148
120
|
const ref = useRef<HTMLDivElement>(null!);
|
|
@@ -187,7 +159,7 @@ function App() {
|
|
|
187
159
|
zoom: 11,
|
|
188
160
|
pitch: 64.88,
|
|
189
161
|
}}
|
|
190
|
-
mapStyle={
|
|
162
|
+
mapStyle={MAP_STYLE_URL}
|
|
191
163
|
onLoad={initL7}
|
|
192
164
|
>
|
|
193
165
|
<Stats className="stats" parent={ref} />
|
|
@@ -211,22 +183,17 @@ export default App;
|
|
|
211
183
|
```
|
|
212
184
|
|
|
213
185
|
Этот пример демонстрирует:
|
|
186
|
+
|
|
214
187
|
- Создание карты MapLibre GL с помощью `react-map-gl`
|
|
215
188
|
- Интеграция AntV L7 для визуализации геопространственных данных
|
|
216
189
|
- Использование React Three Fiber и Drei для 3D-рендеринга
|
|
217
190
|
- Позиционирование 3D-объектов относительно карты с помощью `react-three-map`
|
|
218
191
|
|
|
219
|
-
###
|
|
192
|
+
### Подложка карты
|
|
220
193
|
|
|
221
|
-
|
|
194
|
+
В шаблоне по умолчанию используется стиль Carto GL без API-ключа (`MAP_STYLE_URL` в `src/App.tsx`). Для другого провайдера (например, MapTiler) задайте `mapStyle` и храните ключи в `.env` / CI.
|
|
222
195
|
|
|
223
|
-
|
|
224
|
-
PUBLIC_MAPTILER_KEY=your_maptiler_api_key_here
|
|
225
|
-
```
|
|
226
|
-
|
|
227
|
-
Обязательно добавьте `.env` в ваш `.gitignore`, чтобы ваши ключи оставались в безопасности.
|
|
228
|
-
|
|
229
|
-
## 🤝 Участие в разработке
|
|
196
|
+

|
|
230
197
|
|
|
231
198
|
Вклад в проект приветствуется и очень ценится! Чтобы внести вклад, следуйте этим шагам:
|
|
232
199
|
|
|
@@ -236,15 +203,16 @@ PUBLIC_MAPTILER_KEY=your_maptiler_api_key_here
|
|
|
236
203
|
4. Отправьте изменения в ветку (`git push origin feature/amazing-feature`)
|
|
237
204
|
5. Откройте Pull Request
|
|
238
205
|
|
|
239
|
-
|
|
206
|
+

|
|
207
|
+
|
|
208
|
+
MIT License © 2025 Trapar Waves
|
|
240
209
|
|
|
241
|
-
## 👤
|
|
210
|
+
## 👤 Автор
|
|
242
211
|
|
|
243
|
-
- **Rikka:** (admin@rikka.cc)
|
|
244
|
-
-
|
|
212
|
+
- **Rikka:** [admin@rikka.cc](mailto:admin@rikka.cc)
|
|
213
|
+
- **Профиль GitHub:** [Muromi-Rikka](https://github.com/Muromi-Rikka)
|
|
245
214
|
|
|
246
|
-
## 🔗
|
|
215
|
+
## 🔗 Ссылки
|
|
247
216
|
|
|
248
217
|
- **Репозиторий:** [https://github.com/Trapar-waves/react-three-maplibre](https://github.com/Trapar-waves/react-three-maplibre)
|
|
249
|
-
-
|
|
250
|
-
- **Проблемы:** [https://github.com/Trapar-waves/react-three-maplibre/issues](https://github.com/Trapar-waves/react-three-maplibre/issues)
|
|
218
|
+
- **Issues:** [https://github.com/Trapar-waves/react-three-maplibre/issues](https://github.com/Trapar-waves/react-three-maplibre/issues)
|
package/rsbuild.config.ts
CHANGED
|
@@ -1,11 +1,37 @@
|
|
|
1
1
|
import { defineConfig, loadEnv } from "@rsbuild/core";
|
|
2
|
+
import { pluginLess } from "@rsbuild/plugin-less";
|
|
2
3
|
import { pluginReact } from "@rsbuild/plugin-react";
|
|
4
|
+
import { RsdoctorRspackPlugin } from "@rsdoctor/rspack-plugin";
|
|
3
5
|
import tailwind from "@tailwindcss/postcss";
|
|
6
|
+
import process from "node:process";
|
|
7
|
+
import TurboConsole from "unplugin-turbo-console/rspack";
|
|
4
8
|
|
|
5
9
|
const { publicVars } = loadEnv({ cwd: "./environments" });
|
|
6
10
|
|
|
11
|
+
function normalizeBasePath(): string {
|
|
12
|
+
const raw = process.env.BASE_PATH?.trim();
|
|
13
|
+
if (!raw || raw === "/") {
|
|
14
|
+
return "/";
|
|
15
|
+
}
|
|
16
|
+
const prefixed = raw.startsWith("/") ? raw : `/${raw}`;
|
|
17
|
+
return prefixed.endsWith("/") ? prefixed : `${prefixed}/`;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const basePath = normalizeBasePath();
|
|
21
|
+
const isUseSubpath = basePath !== "/";
|
|
22
|
+
|
|
23
|
+
const isEnableRsdoctor = Boolean(process.env.RSDOCTOR);
|
|
24
|
+
const isEnableTurboConsole = process.env.NODE_ENV === "development";
|
|
25
|
+
|
|
7
26
|
export default defineConfig({
|
|
8
|
-
|
|
27
|
+
...(isUseSubpath && {
|
|
28
|
+
output: { assetPrefix: basePath },
|
|
29
|
+
server: { base: basePath },
|
|
30
|
+
}),
|
|
31
|
+
performance: {
|
|
32
|
+
...(isEnableRsdoctor && { buildCache: false }),
|
|
33
|
+
},
|
|
34
|
+
plugins: [pluginReact(), pluginLess()],
|
|
9
35
|
source: {
|
|
10
36
|
define: publicVars,
|
|
11
37
|
},
|
|
@@ -15,5 +41,24 @@ export default defineConfig({
|
|
|
15
41
|
plugins: [tailwind],
|
|
16
42
|
},
|
|
17
43
|
},
|
|
44
|
+
rspack: [
|
|
45
|
+
(config) => {
|
|
46
|
+
if (isEnableTurboConsole) {
|
|
47
|
+
config.plugins.push(TurboConsole());
|
|
48
|
+
}
|
|
49
|
+
if (isEnableRsdoctor) {
|
|
50
|
+
config.plugins.push(
|
|
51
|
+
new RsdoctorRspackPlugin({
|
|
52
|
+
output: {
|
|
53
|
+
mode: "brief",
|
|
54
|
+
options: {
|
|
55
|
+
type: ["json"],
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
}),
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
],
|
|
18
63
|
},
|
|
19
64
|
});
|
package/src/app.tsx
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import type { MapRef } from "react-map-gl/maplibre";
|
|
3
|
+
import { PointLayer, Scene } from "@antv/l7";
|
|
4
|
+
import { MapLibre } from "@antv/l7-maps";
|
|
5
|
+
import { Box, Stats } from "@react-three/drei";
|
|
6
|
+
import { extend } from "@react-three/fiber";
|
|
7
|
+
import { useRef } from "react";
|
|
8
|
+
import Map from "react-map-gl/maplibre";
|
|
9
|
+
import { Canvas } from "react-three-map/maplibre";
|
|
10
|
+
import { LineMaterial, LineSegments2, LineSegmentsGeometry } from "three-stdlib";
|
|
11
|
+
|
|
12
|
+
// eslint-disable-next-line unicorn/no-top-level-side-effects -- R3F extend must run before render
|
|
13
|
+
extend({ LineMaterial, LineSegments2, LineSegmentsGeometry });
|
|
14
|
+
|
|
15
|
+
declare module "@react-three/fiber" {
|
|
16
|
+
interface ThreeElements {
|
|
17
|
+
lineMaterial: Partial<LineMaterial> & ThreeElements["material"];
|
|
18
|
+
lineSegments2: ThreeElements["object3D"] & { children?: ReactNode };
|
|
19
|
+
lineSegmentsGeometry: ThreeElements["bufferGeometry"];
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const latLon = {
|
|
24
|
+
latitude: 31.215175,
|
|
25
|
+
longitude: 121.417463,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Carto 托管的 MapLibre GL 矢量底图,无需 API Key(使用请遵守 https://carto.com/basemaps/ 条款)。
|
|
30
|
+
*/
|
|
31
|
+
const MAP_STYLE_URL = "https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json";
|
|
32
|
+
|
|
33
|
+
function publicAssetUrl(filename: string): string {
|
|
34
|
+
const base = (import.meta.env.BASE_URL ?? "/").replace(/\/$/, "");
|
|
35
|
+
const path = filename.startsWith("/") ? filename : `/${filename}`;
|
|
36
|
+
return `${base || ""}${path}`.replaceAll("//", "/");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const HOMEPAGE = "https://github.com/Trapar-waves/react-three-maplibre";
|
|
40
|
+
const LOGOS_SET_URL = "https://icon-sets.iconify.design/logos/";
|
|
41
|
+
|
|
42
|
+
interface TechPill {
|
|
43
|
+
iconClass: string;
|
|
44
|
+
id: string;
|
|
45
|
+
label: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const techStack: TechPill[] = [
|
|
49
|
+
{ iconClass: "icon-[logos--react]", id: "react", label: "React 19" },
|
|
50
|
+
{ iconClass: "icon-[logos--typescript-icon]", id: "ts", label: "TypeScript" },
|
|
51
|
+
{ iconClass: "icon-[logos--tailwindcss-icon]", id: "tailwind", label: "Tailwind 4" },
|
|
52
|
+
{ iconClass: "icon-[material-icon-theme--rstack]", id: "rsbuild", label: "Rsbuild" },
|
|
53
|
+
{ iconClass: "icon-[simple-icons--maplibre]", id: "maplibre", label: "MapLibre · react-map-gl" },
|
|
54
|
+
{ iconClass: "icon-[logos--markdown]", id: "l7", label: "AntV L7" },
|
|
55
|
+
{ iconClass: "icon-[logos--threejs]", id: "r3f", label: "R3F · drei · three-stdlib" },
|
|
56
|
+
{ iconClass: "icon-[logos--markdown]", id: "rtmap", label: "react-three-map" },
|
|
57
|
+
{ iconClass: "icon-[logos--eslint]", id: "eslint", label: "ESLint" },
|
|
58
|
+
{ iconClass: "icon-[logos--pnpm]", id: "pnpm", label: "pnpm" },
|
|
59
|
+
{ iconClass: "icon-[logos--github-icon]", id: "github", label: "GitHub Actions" },
|
|
60
|
+
];
|
|
61
|
+
|
|
62
|
+
const readmeFeatures: string[] = [
|
|
63
|
+
"MapLibre 与 AntV L7 组合,自定义地图与点层渲染。",
|
|
64
|
+
"@react-three/fiber、drei 将 Three.js 以声明式接入 React。",
|
|
65
|
+
"react-three-map 与 MapLibre 视口同步;Canvas 使用 overlay 模式使 Three 叠在 L7 点层之上。",
|
|
66
|
+
"Tailwind CSS 快速布局与面板样式。",
|
|
67
|
+
"TypeScript 覆盖地图与场景相关类型。",
|
|
68
|
+
"Rsbuild 开发与生产构建。",
|
|
69
|
+
"react-map-gl 提供交互与导航体验。",
|
|
70
|
+
"three-stdlib 与 drei 复用常用几何与调试组件。",
|
|
71
|
+
"GitHub Pages 支持 BASE_PATH 与 404.html SPA 部署。",
|
|
72
|
+
"默认 Carto Basemaps GL 样式,本地与 CI 均无需地图 API Key。",
|
|
73
|
+
];
|
|
74
|
+
|
|
75
|
+
const readmeTechNote = "演示使用 @react-three/drei 的 Stats;生产可按需移除。若需其他底图,可在 App.tsx 中替换 MAP_STYLE_URL 并自行处理鉴权。";
|
|
76
|
+
|
|
77
|
+
function App() {
|
|
78
|
+
const divReference = useRef<HTMLDivElement>(null!);
|
|
79
|
+
const mapReference = useRef<MapRef>(null!);
|
|
80
|
+
function initL7() {
|
|
81
|
+
if (!mapReference.current) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const scene = new Scene({
|
|
86
|
+
id: "map",
|
|
87
|
+
map: new MapLibre({
|
|
88
|
+
mapInstance: mapReference.current.getMap(),
|
|
89
|
+
}),
|
|
90
|
+
});
|
|
91
|
+
scene.on("loaded", async () => {
|
|
92
|
+
try {
|
|
93
|
+
const response = await fetch(publicAssetUrl("BElVQFEFvpAKzddxFZxJ.txt"));
|
|
94
|
+
if (!response.ok) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
const data = await response.text();
|
|
98
|
+
const pointLayer = new PointLayer({
|
|
99
|
+
blend: "additive",
|
|
100
|
+
})
|
|
101
|
+
.source(data, {
|
|
102
|
+
parser: {
|
|
103
|
+
type: "csv",
|
|
104
|
+
x: "lng",
|
|
105
|
+
y: "lat",
|
|
106
|
+
},
|
|
107
|
+
})
|
|
108
|
+
.size(0.5)
|
|
109
|
+
.color("#080298");
|
|
110
|
+
|
|
111
|
+
scene.addLayer(pointLayer);
|
|
112
|
+
}
|
|
113
|
+
catch {}
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
return (
|
|
117
|
+
<div className="h-screen w-screen relative overflow-hidden" ref={divReference}>
|
|
118
|
+
<aside
|
|
119
|
+
aria-label="模板说明"
|
|
120
|
+
className="pointer-events-none absolute left-4 top-4 z-10 max-w-sm motion-safe:transition motion-safe:duration-200"
|
|
121
|
+
>
|
|
122
|
+
<div
|
|
123
|
+
className="pointer-events-auto max-h-[min(70vh,560px)] overflow-y-auto overscroll-contain rounded-xl border border-white/15 bg-slate-950/80 p-4 text-sm text-slate-100 shadow-xl backdrop-blur-md"
|
|
124
|
+
>
|
|
125
|
+
<p className="font-semibold text-white">react-three-maplibre</p>
|
|
126
|
+
<p className="mt-2 leading-relaxed text-slate-300">
|
|
127
|
+
MapLibre 与 react-three-map 同屏;L7 点层叠加 CSV,Three 方体经
|
|
128
|
+
{" "}
|
|
129
|
+
<code className="rounded bg-slate-800 px-1">overlay</code>
|
|
130
|
+
{" "}
|
|
131
|
+
画布叠在 L7 之上。底图为 Carto 公开 GL 样式,无需地图服务 API Key。技术图标见
|
|
132
|
+
{" "}
|
|
133
|
+
<a
|
|
134
|
+
className="font-medium text-sky-300 underline decoration-sky-700 underline-offset-2 hover:text-sky-200"
|
|
135
|
+
href={LOGOS_SET_URL}
|
|
136
|
+
rel="noreferrer"
|
|
137
|
+
target="_blank"
|
|
138
|
+
>
|
|
139
|
+
Iconify logos
|
|
140
|
+
</a>
|
|
141
|
+
。
|
|
142
|
+
</p>
|
|
143
|
+
<p className="mt-3 text-xs font-medium uppercase tracking-wide text-slate-500">
|
|
144
|
+
技术栈
|
|
145
|
+
</p>
|
|
146
|
+
<div className="mt-2 flex flex-wrap gap-2">
|
|
147
|
+
{techStack.map(item => (
|
|
148
|
+
<span
|
|
149
|
+
className="inline-flex items-center gap-1.5 rounded-lg border border-white/10 bg-slate-900/80 px-2 py-1 text-xs text-slate-200"
|
|
150
|
+
key={item.id}
|
|
151
|
+
>
|
|
152
|
+
<span aria-hidden className={`${item.iconClass} text-base text-sky-300`} />
|
|
153
|
+
{item.label}
|
|
154
|
+
</span>
|
|
155
|
+
))}
|
|
156
|
+
</div>
|
|
157
|
+
<p className="mt-4 text-xs font-medium uppercase tracking-wide text-slate-500">
|
|
158
|
+
README 摘要
|
|
159
|
+
</p>
|
|
160
|
+
<ul className="mt-2 list-disc space-y-1 pl-4 text-slate-400">
|
|
161
|
+
{readmeFeatures.map(line => (
|
|
162
|
+
<li key={line}>{line}</li>
|
|
163
|
+
))}
|
|
164
|
+
</ul>
|
|
165
|
+
<p className="mt-3 text-xs italic leading-relaxed text-slate-500">
|
|
166
|
+
{readmeTechNote}
|
|
167
|
+
</p>
|
|
168
|
+
<p className="mt-3 text-xs leading-relaxed text-slate-500">
|
|
169
|
+
品牌图标来自
|
|
170
|
+
{" "}
|
|
171
|
+
<a className="text-sky-400 underline hover:text-sky-300" href={LOGOS_SET_URL} rel="noreferrer" target="_blank">SVG Logos</a>
|
|
172
|
+
{" "}
|
|
173
|
+
(CC0)。
|
|
174
|
+
</p>
|
|
175
|
+
<a
|
|
176
|
+
className="mt-3 inline-flex min-h-11 w-full items-center justify-center rounded-lg bg-sky-500 px-4 py-2 text-sm font-semibold text-slate-950 hover:bg-sky-400 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white"
|
|
177
|
+
href={HOMEPAGE}
|
|
178
|
+
rel="noreferrer"
|
|
179
|
+
target="_blank"
|
|
180
|
+
>
|
|
181
|
+
查看仓库
|
|
182
|
+
</a>
|
|
183
|
+
</div>
|
|
184
|
+
</aside>
|
|
185
|
+
<Map
|
|
186
|
+
id="map"
|
|
187
|
+
initialViewState={{
|
|
188
|
+
...latLon,
|
|
189
|
+
pitch: 64.88,
|
|
190
|
+
zoom: 11,
|
|
191
|
+
}}
|
|
192
|
+
mapStyle={MAP_STYLE_URL}
|
|
193
|
+
onLoad={initL7}
|
|
194
|
+
ref={mapReference}
|
|
195
|
+
>
|
|
196
|
+
<Stats className="stats" parent={divReference} />
|
|
197
|
+
|
|
198
|
+
<Canvas
|
|
199
|
+
id="trapar-r3f-overlay"
|
|
200
|
+
overlay
|
|
201
|
+
{...latLon}
|
|
202
|
+
>
|
|
203
|
+
<hemisphereLight
|
|
204
|
+
args={["#ffffff", "#60666C"]}
|
|
205
|
+
position={[1, 4.5, 3]}
|
|
206
|
+
/>
|
|
207
|
+
<object3D scale={500}>
|
|
208
|
+
<Box position={[-1.2, 1, 0]} />
|
|
209
|
+
<Box position={[1.2, 1, 0]} />
|
|
210
|
+
</object3D>
|
|
211
|
+
</Canvas>
|
|
212
|
+
</Map>
|
|
213
|
+
</div>
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export default App;
|
package/src/index.tsx
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import ReactDOM from "react-dom/client";
|
|
2
|
-
import App from "./
|
|
2
|
+
import App from "./app";
|
|
3
3
|
import "./global.css";
|
|
4
4
|
|
|
5
|
-
const
|
|
6
|
-
if (
|
|
7
|
-
const root = ReactDOM.createRoot(
|
|
5
|
+
const rootElement = document.querySelector("#root");
|
|
6
|
+
if (rootElement) {
|
|
7
|
+
const root = ReactDOM.createRoot(rootElement);
|
|
8
8
|
root.render(
|
|
9
9
|
<App />,
|
|
10
10
|
);
|
package/tsconfig.json
CHANGED
package/src/App.tsx
DELETED
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
import type { ReactNode } from "react";
|
|
2
|
-
import type { MapRef } from "react-map-gl/maplibre";
|
|
3
|
-
import { PointLayer, Scene } from "@antv/l7";
|
|
4
|
-
import { MapLibre } from "@antv/l7-maps";
|
|
5
|
-
import { Box, Stats } from "@react-three/drei";
|
|
6
|
-
import { extend } from "@react-three/fiber";
|
|
7
|
-
import { useRef } from "react";
|
|
8
|
-
import Map from "react-map-gl/maplibre";
|
|
9
|
-
import { Canvas } from "react-three-map/maplibre";
|
|
10
|
-
import { LineMaterial, LineSegments2, LineSegmentsGeometry } from "three-stdlib";
|
|
11
|
-
|
|
12
|
-
extend({ LineSegmentsGeometry, LineMaterial, LineSegments2 });
|
|
13
|
-
|
|
14
|
-
declare module "@react-three/fiber" {
|
|
15
|
-
interface ThreeElements {
|
|
16
|
-
lineSegmentsGeometry: ThreeElements["bufferGeometry"];
|
|
17
|
-
lineMaterial: ThreeElements["material"] & Partial<LineMaterial>;
|
|
18
|
-
lineSegments2: ThreeElements["object3D"] & { children?: ReactNode };
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const latLon = {
|
|
23
|
-
latitude: 31.215175,
|
|
24
|
-
longitude: 121.417463,
|
|
25
|
-
};
|
|
26
|
-
const MAPTILER_KEY = import.meta.env.PUBLIC_MAPTILER_KEY;
|
|
27
|
-
|
|
28
|
-
function App() {
|
|
29
|
-
const ref = useRef<HTMLDivElement>(null!);
|
|
30
|
-
const mapRef = useRef<MapRef>(null!);
|
|
31
|
-
function initL7() {
|
|
32
|
-
if (mapRef.current) {
|
|
33
|
-
const scene = new Scene({
|
|
34
|
-
id: "map",
|
|
35
|
-
map: new MapLibre({
|
|
36
|
-
mapInstance: mapRef.current.getMap(),
|
|
37
|
-
}),
|
|
38
|
-
});
|
|
39
|
-
scene.on("loaded", () => {
|
|
40
|
-
fetch("/BElVQFEFvpAKzddxFZxJ.txt")
|
|
41
|
-
.then(res => res.text())
|
|
42
|
-
.then((data) => {
|
|
43
|
-
const pointLayer = new PointLayer({
|
|
44
|
-
blend: "additive",
|
|
45
|
-
})
|
|
46
|
-
.source(data, {
|
|
47
|
-
parser: {
|
|
48
|
-
type: "csv",
|
|
49
|
-
y: "lat",
|
|
50
|
-
x: "lng",
|
|
51
|
-
},
|
|
52
|
-
})
|
|
53
|
-
.size(0.5)
|
|
54
|
-
.color("#080298");
|
|
55
|
-
|
|
56
|
-
scene.addLayer(pointLayer);
|
|
57
|
-
});
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
return (
|
|
62
|
-
<div className="h-screen w-screen relative overflow-hidden" ref={ref}>
|
|
63
|
-
<Map
|
|
64
|
-
id="map"
|
|
65
|
-
ref={mapRef}
|
|
66
|
-
initialViewState={{
|
|
67
|
-
...latLon,
|
|
68
|
-
zoom: 11,
|
|
69
|
-
pitch: 64.88,
|
|
70
|
-
}}
|
|
71
|
-
mapStyle={`https://api.maptiler.com/maps/streets/style.json?key=${MAPTILER_KEY}`}
|
|
72
|
-
onLoad={initL7}
|
|
73
|
-
>
|
|
74
|
-
<Stats className="stats" parent={ref} />
|
|
75
|
-
|
|
76
|
-
<Canvas {...latLon}>
|
|
77
|
-
<hemisphereLight
|
|
78
|
-
args={["#ffffff", "#60666C"]}
|
|
79
|
-
position={[1, 4.5, 3]}
|
|
80
|
-
/>
|
|
81
|
-
<object3D scale={500}>
|
|
82
|
-
<Box position={[-1.2, 1, 0]} />
|
|
83
|
-
<Box position={[1.2, 1, 0]} />
|
|
84
|
-
</object3D>
|
|
85
|
-
</Canvas>
|
|
86
|
-
</Map>
|
|
87
|
-
</div>
|
|
88
|
-
);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
export default App;
|
|
File without changes
|