去掉全局 id="deck-canvas" 路径,改为实例级 canvasRef,修复可能出现的 Uncaught Error: deck.gl: assertion failed 的问题
This commit is contained in:
@@ -127,7 +127,10 @@ const MapComponent: React.FC<MapComponentProps> = ({ children }) => {
|
||||
const MAP_VIEW_STORAGE_KEY = `${MAP_WORKSPACE}_map_view`; // 持久化 key
|
||||
|
||||
const mapRef = useRef<HTMLDivElement | null>(null);
|
||||
const canvasRef = useRef<HTMLCanvasElement | null>(null);
|
||||
const deckLayerRef = useRef<DeckLayer | null>(null);
|
||||
const isDisposingRef = useRef(false);
|
||||
const pendingTimeoutsRef = useRef<number[]>([]);
|
||||
|
||||
const [map, setMap] = useState<OlMap>();
|
||||
const [deckLayer, setDeckLayer] = useState<DeckLayer>();
|
||||
@@ -518,14 +521,37 @@ const MapComponent: React.FC<MapComponentProps> = ({ children }) => {
|
||||
// The map and layer instances are intentionally rebuilt only when workspace or extent changes.
|
||||
useEffect(() => {
|
||||
if (!mapRef.current) return;
|
||||
if (!canvasRef.current) {
|
||||
return;
|
||||
}
|
||||
isDisposingRef.current = false;
|
||||
|
||||
const addTimeout = (callback: () => void, delay: number) => {
|
||||
const timerId = window.setTimeout(() => {
|
||||
pendingTimeoutsRef.current = pendingTimeoutsRef.current.filter(
|
||||
(id) => id !== timerId,
|
||||
);
|
||||
if (isDisposingRef.current) return;
|
||||
callback();
|
||||
}, delay);
|
||||
pendingTimeoutsRef.current.push(timerId);
|
||||
return timerId;
|
||||
};
|
||||
|
||||
const clearPendingTimeouts = () => {
|
||||
pendingTimeoutsRef.current.forEach((id) => clearTimeout(id));
|
||||
pendingTimeoutsRef.current = [];
|
||||
};
|
||||
|
||||
// 缓存 junction、pipe 数据,提供给 deck.gl 提供坐标供标签显示
|
||||
junctionSource.on("tileloadend", (event) => {
|
||||
const handleJunctionTileLoadEnd = (event: any) => {
|
||||
if (isDisposingRef.current) return;
|
||||
try {
|
||||
if (event.tile instanceof VectorTile) {
|
||||
const renderFeatures = event.tile.getFeatures();
|
||||
const data = new Map();
|
||||
|
||||
renderFeatures.forEach((renderFeature) => {
|
||||
renderFeatures.forEach((renderFeature: any) => {
|
||||
const props = renderFeature.getProperties();
|
||||
const featureId = props.id;
|
||||
if (featureId && !junctionDataIds.current.has(featureId)) {
|
||||
@@ -554,14 +580,15 @@ const MapComponent: React.FC<MapComponentProps> = ({ children }) => {
|
||||
} catch (error) {
|
||||
console.error("Junction tile load error:", error);
|
||||
}
|
||||
});
|
||||
pipeSource.on("tileloadend", (event) => {
|
||||
};
|
||||
const handlePipeTileLoadEnd = (event: any) => {
|
||||
if (isDisposingRef.current) return;
|
||||
try {
|
||||
if (event.tile instanceof VectorTile) {
|
||||
const renderFeatures = event.tile.getFeatures();
|
||||
const data = new Map();
|
||||
|
||||
renderFeatures.forEach((renderFeature) => {
|
||||
renderFeatures.forEach((renderFeature: any) => {
|
||||
try {
|
||||
const props = renderFeature.getProperties();
|
||||
const featureId = props.id;
|
||||
@@ -634,7 +661,9 @@ const MapComponent: React.FC<MapComponentProps> = ({ children }) => {
|
||||
} catch (error) {
|
||||
console.error("Pipe tile load error:", error);
|
||||
}
|
||||
});
|
||||
};
|
||||
junctionSource.on("tileloadend", handleJunctionTileLoadEnd);
|
||||
pipeSource.on("tileloadend", handlePipeTileLoadEnd);
|
||||
// 监听 junctionsLayer 的 visible 变化
|
||||
const handleJunctionVisibilityChange = () => {
|
||||
const isVisible = junctionsLayer.getVisible();
|
||||
@@ -748,6 +777,7 @@ const MapComponent: React.FC<MapComponentProps> = ({ children }) => {
|
||||
}
|
||||
// 持久化视图(中心点 + 缩放),防抖写入 localStorage
|
||||
const persistView = debounce(() => {
|
||||
if (isDisposingRef.current) return;
|
||||
try {
|
||||
const view = map.getView();
|
||||
const center = view.getCenter();
|
||||
@@ -765,7 +795,7 @@ const MapComponent: React.FC<MapComponentProps> = ({ children }) => {
|
||||
|
||||
// 监听缩放变化并持久化,同时更新 currentZoom
|
||||
const handleViewChange = () => {
|
||||
setTimeout(() => {
|
||||
addTimeout(() => {
|
||||
const zoom = map.getView().getZoom() || 0;
|
||||
setCurrentZoom(zoom);
|
||||
persistView();
|
||||
@@ -774,7 +804,7 @@ const MapComponent: React.FC<MapComponentProps> = ({ children }) => {
|
||||
map.getView().on("change", handleViewChange);
|
||||
|
||||
// 初始化当前缩放级别并强制触发瓦片加载
|
||||
setTimeout(() => {
|
||||
addTimeout(() => {
|
||||
const initialZoom = map.getView().getZoom() || 11;
|
||||
setCurrentZoom(initialZoom);
|
||||
// 强制触发地图渲染,让瓦片加载事件触发
|
||||
@@ -788,11 +818,11 @@ const MapComponent: React.FC<MapComponentProps> = ({ children }) => {
|
||||
latitude: 0,
|
||||
zoom: 1,
|
||||
},
|
||||
canvas: "deck-canvas",
|
||||
canvas: canvasRef.current,
|
||||
controller: false, // 由 OpenLayers 控制视图
|
||||
layers: [],
|
||||
});
|
||||
const deckLayer = new DeckLayer(deck, {
|
||||
const deckLayer = new DeckLayer(deck, canvasRef.current, {
|
||||
name: "deckLayer",
|
||||
value: "deckLayer",
|
||||
});
|
||||
@@ -802,19 +832,37 @@ const MapComponent: React.FC<MapComponentProps> = ({ children }) => {
|
||||
|
||||
// 清理函数
|
||||
return () => {
|
||||
isDisposingRef.current = true;
|
||||
clearPendingTimeouts();
|
||||
debouncedUpdateDataRef.current?.cancel();
|
||||
persistView.cancel();
|
||||
junctionSource.un("tileloadend", handleJunctionTileLoadEnd);
|
||||
pipeSource.un("tileloadend", handlePipeTileLoadEnd);
|
||||
map.getView().un("change", handleViewChange);
|
||||
junctionsLayer.un("change:visible", handleJunctionVisibilityChange);
|
||||
pipesLayer.un("change:visible", handlePipeVisibilityChange);
|
||||
if (deckLayerRef.current && !deckLayerRef.current.isDisposedLayer()) {
|
||||
try {
|
||||
map.removeLayer(deckLayerRef.current);
|
||||
} catch {
|
||||
// Layer may have already been removed during teardown.
|
||||
}
|
||||
deckLayerRef.current.disposeDeck();
|
||||
}
|
||||
deckLayerRef.current = null;
|
||||
setDeckLayer(undefined);
|
||||
map.setTarget(undefined);
|
||||
map.dispose();
|
||||
deck.finalize();
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [MAP_WORKSPACE, MAP_EXTENT]);
|
||||
|
||||
// 当数据变化时,更新 deck.gl 图层
|
||||
useEffect(() => {
|
||||
if (isDisposingRef.current) return;
|
||||
const deckLayer = deckLayerRef.current;
|
||||
if (!deckLayer) return; // 如果 deck 实例还未创建,则退出
|
||||
if (deckLayer.isDisposedLayer()) return;
|
||||
if (!mergedJunctionData.length) return;
|
||||
if (!mergedPipeData.length) return;
|
||||
const junctionTextLayer = new TextLayer({
|
||||
@@ -964,6 +1012,7 @@ const MapComponent: React.FC<MapComponentProps> = ({ children }) => {
|
||||
|
||||
// 控制流动动画开关
|
||||
useEffect(() => {
|
||||
if (isDisposingRef.current) return;
|
||||
if (pipeText === "flow" && currentPipeCalData.length > 0) {
|
||||
flowAnimation.current = true;
|
||||
} else {
|
||||
@@ -976,6 +1025,7 @@ const MapComponent: React.FC<MapComponentProps> = ({ children }) => {
|
||||
|
||||
// 动画循环
|
||||
const animate = () => {
|
||||
if (isDisposingRef.current || deckLayer.isDisposedLayer()) return;
|
||||
// 动画总时长(秒)
|
||||
const animationDuration = 10;
|
||||
const bufferTime = 2;
|
||||
@@ -1075,7 +1125,7 @@ const MapComponent: React.FC<MapComponentProps> = ({ children }) => {
|
||||
<MapTools />
|
||||
{children}
|
||||
</div>
|
||||
<canvas id="deck-canvas" />
|
||||
<canvas ref={canvasRef} />
|
||||
</MapContext.Provider>
|
||||
</DataContext.Provider>
|
||||
</>
|
||||
|
||||
Reference in New Issue
Block a user