roxy-cobewebgl 1.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/src/index.js ADDED
@@ -0,0 +1,169 @@
1
+ /**
2
+ * Cobe 风格点阵地球 — npm 库入口(ESM)
3
+ */
4
+
5
+ import { createFallbackGlobe } from './fallback.js';
6
+
7
+ /** 默认地图(精简 GeoJSON)在包内的绝对 URL,用于 mapUrl 回退 */
8
+ export function getDefaultMapMinUrl() {
9
+ return new URL('../globe.min.json', import.meta.url).href;
10
+ }
11
+
12
+ /** 完整 globe.json 在包内的绝对 URL */
13
+ export function getDefaultMapFullUrl() {
14
+ return new URL('../globe.json', import.meta.url).href;
15
+ }
16
+
17
+ const DEFAULT_PARAMS = {
18
+ phi: 0.3,
19
+ theta: 0.15,
20
+ baseColor: [0.06, 0.1, 0.2],
21
+ glowColor: [0.15, 0.4, 0.85],
22
+ dotColor: [0.4, 0.8, 1.0],
23
+ arcColor: [1.0, 0.4, 0.2],
24
+ dots: 800,
25
+ dotSize: 0.008,
26
+ globeRadius: 0.55,
27
+ glowOn: 1.0,
28
+ debug: 0.0,
29
+ opacity: 0.9,
30
+ autoRotate: true,
31
+ rotationSpeed: 0.003,
32
+ };
33
+
34
+ function normalizeArcs(arcs) {
35
+ return (arcs || []).map((a, i) => ({
36
+ order: a.order != null ? a.order : i + 1,
37
+ startLat: a.startLat,
38
+ startLng: a.startLng,
39
+ endLat: a.endLat,
40
+ endLng: a.endLng,
41
+ color: a.color,
42
+ }));
43
+ }
44
+
45
+ function reportError(onError, err) {
46
+ if (onError) {
47
+ onError(err instanceof Error ? err : new Error(String(err)));
48
+ } else {
49
+ console.error('[cobewebgl]', err);
50
+ }
51
+ }
52
+
53
+ function createWorkerGlobe(canvas, {
54
+ map,
55
+ mapUrl,
56
+ arcsData,
57
+ params: initialParams,
58
+ onError,
59
+ }) {
60
+ const worker = new Worker(new URL('./globe.worker.js', import.meta.url), { type: 'module' });
61
+ const offscreen = canvas.transferControlToOffscreen();
62
+
63
+ const dpr = Math.min(2, window.devicePixelRatio || 1);
64
+ offscreen.width = Math.floor(canvas.clientWidth * dpr);
65
+ offscreen.height = Math.floor(canvas.clientHeight * dpr);
66
+
67
+ worker.onerror = (e) => reportError(onError, new Error(e.message || 'Worker error'));
68
+ worker.onmessage = (e) => {
69
+ if (e.data && e.data.type === 'error') reportError(onError, new Error(e.data.msg));
70
+ };
71
+
72
+ worker.postMessage({
73
+ type: 'init',
74
+ canvas: offscreen,
75
+ map: map ?? undefined,
76
+ mapUrl: map != null ? undefined : String(mapUrl),
77
+ arcsData,
78
+ params: initialParams,
79
+ }, [offscreen]);
80
+
81
+ /** 仅包含用户通过 setState 覆盖的字段;phi 等由 Worker 内部推进,避免定时 flush 把旋转打回初始值 */
82
+ const userState = {};
83
+ let paramsDirty = false;
84
+
85
+ function onResize() {
86
+ const d = Math.min(2, window.devicePixelRatio || 1);
87
+ const w = Math.floor(canvas.clientWidth * d);
88
+ const h = Math.floor(canvas.clientHeight * d);
89
+ worker.postMessage({ type: 'resize', width: w, height: h });
90
+ }
91
+ window.addEventListener('resize', onResize);
92
+
93
+ function flushParams() {
94
+ if (!paramsDirty) return;
95
+ paramsDirty = false;
96
+ worker.postMessage({ type: 'params', params: { ...userState } });
97
+ }
98
+ const flushTimer = setInterval(flushParams, 16);
99
+
100
+ let destroyed = false;
101
+
102
+ return {
103
+ setState(partial) {
104
+ Object.assign(userState, partial);
105
+ paramsDirty = true;
106
+ },
107
+ resize() {
108
+ onResize();
109
+ },
110
+ destroy() {
111
+ if (destroyed) return;
112
+ destroyed = true;
113
+ clearInterval(flushTimer);
114
+ window.removeEventListener('resize', onResize);
115
+ worker.postMessage({ type: 'destroy' });
116
+ worker.terminate();
117
+ },
118
+ };
119
+ }
120
+
121
+ /**
122
+ * 创建点阵地球实例(优先 OffscreenCanvas + Worker,否则主线程 WebGL)
123
+ *
124
+ * @param {HTMLCanvasElement} canvas
125
+ * @param {CreateGlobeOptions} [options]
126
+ * @returns {Promise<GlobeInstance|null>}
127
+ */
128
+ export async function createGlobe(canvas, options = {}) {
129
+ const {
130
+ map,
131
+ mapUrl,
132
+ preferWorker = true,
133
+ arcs,
134
+ onError,
135
+ debugShowTexture,
136
+ ...userParams
137
+ } = options;
138
+
139
+ const initialParams = { ...DEFAULT_PARAMS, ...userParams };
140
+ const arcsData = normalizeArcs(arcs);
141
+ const defaultMinUrl = getDefaultMapMinUrl();
142
+ const resolvedMapUrl = map != null ? null : (mapUrl != null ? String(mapUrl) : defaultMinUrl);
143
+
144
+ const canWorker = preferWorker && typeof canvas.transferControlToOffscreen === 'function';
145
+
146
+ if (canWorker) {
147
+ try {
148
+ return createWorkerGlobe(canvas, {
149
+ map,
150
+ mapUrl: resolvedMapUrl,
151
+ arcsData,
152
+ params: initialParams,
153
+ onError: (e) => reportError(onError, e),
154
+ });
155
+ } catch (e) {
156
+ reportError(onError, e);
157
+ }
158
+ }
159
+
160
+ const fb = await createFallbackGlobe(canvas, {
161
+ map,
162
+ mapUrl: resolvedMapUrl,
163
+ arcsData,
164
+ params: initialParams,
165
+ debugShowTexture,
166
+ onError,
167
+ });
168
+ return fb;
169
+ }