更新 deckLayer 类

This commit is contained in:
JIANG
2025-11-24 15:09:37 +08:00
parent 15e80f105e
commit b4aef962dd
5 changed files with 309 additions and 227 deletions

View File

@@ -49,6 +49,7 @@ interface DataContextType {
showPipeText?: boolean; // 是否显示管道文本
setShowJunctionText?: React.Dispatch<React.SetStateAction<boolean>>;
setShowPipeText?: React.Dispatch<React.SetStateAction<boolean>>;
setShowContourLayer?: React.Dispatch<React.SetStateAction<boolean>>;
junctionText: string;
pipeText: string;
setJunctionText?: React.Dispatch<React.SetStateAction<string>>;
@@ -86,7 +87,7 @@ export const useData = () => {
const MapComponent: React.FC<MapComponentProps> = ({ children }) => {
const mapRef = useRef<HTMLDivElement | null>(null);
const deckRef = useRef<Deck | null>(null);
const deckFlowRef = useRef<Deck | null>(null);
const deckLayerRef = useRef<DeckLayer | null>(null);
const [map, setMap] = useState<OlMap>();
// currentCalData 用于存储当前计算结果
@@ -111,10 +112,10 @@ const MapComponent: React.FC<MapComponentProps> = ({ children }) => {
const [showPipeText, setShowPipeText] = useState(false); // 控制管道文本显示
const [showJunctionTextLayer, setShowJunctionTextLayer] = useState(true); // 控制节点文本图层显示
const [showPipeTextLayer, setShowPipeTextLayer] = useState(true); // 控制管道文本图层显示
const [showContourLayer, setShowContourLayer] = useState(true); // 控制等高线图层显示
const [junctionText, setJunctionText] = useState("pressure");
const [pipeText, setPipeText] = useState("flow");
const flowAnimation = useRef(false); // 添加动画控制标志
const waterflowUserVisible = useRef<boolean>(true); // 用户设置的水流图层可见性
const [currentZoom, setCurrentZoom] = useState(11); // 当前缩放级别
// 防抖更新函数
@@ -571,6 +572,7 @@ const MapComponent: React.FC<MapComponentProps> = ({ children }) => {
controls: [],
});
setMap(map);
// 恢复上次视图;如果没有则适配 MAP_EXTENT
try {
const stored = localStorage.getItem(MAP_VIEW_STORAGE_KEY);
@@ -650,37 +652,13 @@ const MapComponent: React.FC<MapComponentProps> = ({ children }) => {
layers: [],
});
deckRef.current = deck;
const deckLayer = new DeckLayer(deck);
// deckLayer.setZIndex(1000); // 确保在最上层
const deckLayer = new DeckLayer(deck, {
name: "deckLayer",
value: "deckLayer",
});
deckLayerRef.current = deckLayer;
map.addLayer(deckLayer);
// 初始化水流动画的 deck.gl
const deckFlow = new Deck({
initialViewState: {
longitude: 0,
latitude: 0,
zoom: 1,
},
canvas: "deck-flow-canvas",
controller: false,
layers: [],
});
deckFlowRef.current = deckFlow;
const deckFlowLayer = new DeckLayer(deckFlow, {
name: "水流动画",
value: "waterflow",
type: "animation",
});
// 初始化用户可见性状态(默认为 true
deckFlowLayer.initUserVisibility("waterflowLayer", true);
// 设置可见性变化回调,同步更新 waterflowUserVisible
deckFlowLayer.setVisibilityChangeCallback((layerId, visible) => {
if (layerId === "waterflowLayer") {
waterflowUserVisible.current = visible;
}
});
map.addLayer(deckFlowLayer);
// 清理函数
return () => {
junctionsLayer.un("change:visible", handleJunctionVisibilityChange);
@@ -688,85 +666,119 @@ const MapComponent: React.FC<MapComponentProps> = ({ children }) => {
map.setTarget(undefined);
map.dispose();
deck.finalize();
deckFlow.finalize();
};
}, []);
// 当数据变化时,更新 deck.gl 图层
useEffect(() => {
const deck = deckRef.current;
if (!deck) return; // 如果 deck 实例还未创建,则退出
const newLayers = [
new TextLayer({
id: "junctionTextLayer",
zIndex: 10,
data: showJunctionText ? junctionData : [],
getPosition: (d: any) => d.position,
fontFamily: "Monaco, monospace",
getText: (d: any) =>
d[junctionText] ? (d[junctionText] as number).toFixed(3) : "",
getSize: 18,
fontWeight: "bold",
getColor: [0, 0, 0],
getAngle: 0,
getTextAnchor: "middle",
getAlignmentBaseline: "center",
getPixelOffset: [0, -10],
visible:
showJunctionTextLayer && currentZoom >= 15 && currentZoom <= 24,
extensions: [new CollisionFilterExtension()],
collisionTestProps: {
sizeScale: 3, // 增加碰撞检测的尺寸以提供更大间距
},
// 可读性设置
characterSet: "auto",
fontSettings: {
sdf: true,
fontSize: 64,
buffer: 6,
},
// outlineWidth: 10,
// outlineColor: [242, 244, 246, 255],
}),
new TextLayer({
id: "pipeTextLayer",
zIndex: 10,
data: showPipeText ? pipeData : [],
getPosition: (d: any) => d.position,
fontFamily: "Monaco, monospace",
getText: (d: any) =>
d[pipeText] ? Math.abs(d[pipeText] as number).toFixed(3) : "",
getSize: 18,
fontWeight: "bold",
getColor: [0, 0, 0],
getAngle: (d: any) => d.angle || 0,
getPixelOffset: [0, -8],
getTextAnchor: "middle",
getAlignmentBaseline: "bottom",
visible: showPipeTextLayer && currentZoom >= 15 && currentZoom <= 24,
extensions: [new CollisionFilterExtension()],
collisionTestProps: {
sizeScale: 3, // 增加碰撞检测的尺寸以提供更大间距
},
// 可读性设置
characterSet: "auto",
fontSettings: {
sdf: true,
fontSize: 64,
buffer: 6,
},
// outlineWidth: 10,
// outlineColor: [242, 244, 246, 255],
}),
];
deck.setProps({ layers: newLayers });
const deckLayer = deckLayerRef.current;
if (!deckLayer) return; // 如果 deck 实例还未创建,则退出
if (!junctionData.length) return;
if (!pipeData.length) return;
console.log(pipeData);
console.log(pipeText);
const junctionTextLayer = new TextLayer({
id: "junctionTextLayer",
zIndex: 10,
data: showJunctionText ? junctionData : [],
getPosition: (d: any) => d.position,
fontFamily: "Monaco, monospace",
getText: (d: any) =>
d[junctionText] ? (d[junctionText] as number).toFixed(3) : "",
getSize: 18,
fontWeight: "bold",
getColor: [0, 0, 0],
getAngle: 0,
getTextAnchor: "middle",
getAlignmentBaseline: "center",
getPixelOffset: [0, -10],
visible: showJunctionTextLayer && currentZoom >= 15 && currentZoom <= 24,
extensions: [new CollisionFilterExtension()],
collisionTestProps: {
sizeScale: 3, // 增加碰撞检测的尺寸以提供更大间距
},
// 可读性设置
characterSet: "auto",
fontSettings: {
sdf: true,
fontSize: 64,
buffer: 6,
},
// outlineWidth: 10,
// outlineColor: [242, 244, 246, 255],
});
const pipeTextLayer = new TextLayer({
id: "pipeTextLayer",
zIndex: 10,
data: showPipeText ? pipeData : [],
getPosition: (d: any) => d.position,
fontFamily: "Monaco, monospace",
getText: (d: any) =>
d[pipeText] ? Math.abs(d[pipeText] as number).toFixed(3) : "",
getSize: 18,
fontWeight: "bold",
getColor: [0, 0, 0],
getAngle: (d: any) => d.angle || 0,
getPixelOffset: [0, -8],
getTextAnchor: "middle",
getAlignmentBaseline: "bottom",
visible: showPipeTextLayer && currentZoom >= 15 && currentZoom <= 24,
extensions: [new CollisionFilterExtension()],
collisionTestProps: {
sizeScale: 3, // 增加碰撞检测的尺寸以提供更大间距
},
// 可读性设置
characterSet: "auto",
fontSettings: {
sdf: true,
fontSize: 64,
buffer: 6,
},
// outlineWidth: 10,
// outlineColor: [242, 244, 246, 255],
});
if (deckLayer.getDeckLayerById("junctionTextLayer")) {
// 传入完整 layer 实例以保证 clone/替换时保留 layer 类型和方法
deckLayer.updateDeckLayer("junctionTextLayer", junctionTextLayer);
} else {
deckLayer.addDeckLayer(junctionTextLayer);
}
if (deckLayer.getDeckLayerById("pipeTextLayer")) {
deckLayer.updateDeckLayer("pipeTextLayer", pipeTextLayer);
} else {
deckLayer.addDeckLayer(pipeTextLayer);
}
console.log(deckLayer.getDeckLayers());
}, [
junctionData,
pipeData,
currentZoom,
showJunctionText,
showPipeText,
showJunctionTextLayer,
showPipeTextLayer,
showContourLayer,
junctionText,
pipeText,
]);
// 控制流动动画开关
useEffect(() => {
if (pipeText === "flow" && currentPipeCalData.length > 0) {
flowAnimation.current = true;
} else {
flowAnimation.current = false;
}
const deckLayer = deckLayerRef.current;
if (!deckLayer) return; // 如果 deck 实例还未创建,则退出
let animationFrameId: number; // 保存 requestAnimationFrame 的 ID
// 动画循环
const animate = () => {
if (!deck || !flowAnimation.current) return; // 添加检查,防止空数据或停止旧循环
if (!deckRef.current || !flowAnimation.current) return; // 添加检查,防止空数据或停止旧循环
// 动画总时长(秒)
if (pipeData.length === 0) {
requestAnimationFrame(animate);
animationFrameId = requestAnimationFrame(animate);
return;
}
const animationDuration = 10;
@@ -787,119 +799,31 @@ const MapComponent: React.FC<MapComponentProps> = ({ children }) => {
getColor: [0, 220, 255],
opacity: 0.8,
visible:
waterflowUserVisible.current &&
flowAnimation.current &&
currentZoom >= 12 &&
currentZoom <= 24,
flowAnimation.current && currentZoom >= 12 && currentZoom <= 24,
widthMinPixels: 5,
jointRounded: true, // 拐角变圆
// capRounded: true, // 端点变圆
trailLength: 2, // 水流尾迹淡出时间
currentTime: currentTime,
});
// 获取当前除 waterflowLayer 之外的所有图层
const otherLayers = deck.props.layers.filter(
(layer: any) => layer && layer.id !== "waterflowLayer"
);
deck.setProps({
layers: [...otherLayers, waterflowLayer],
});
if (deckLayer.getDeckLayerById("waterflowLayer")) {
deckLayer.updateDeckLayer("waterflowLayer", waterflowLayer);
} else {
deckLayer.addDeckLayer(waterflowLayer);
}
// 继续请求动画帧,每帧执行一次函数
requestAnimationFrame(animate);
};
animate();
}, [
flowAnimation,
junctionData,
pipeData,
currentZoom,
showJunctionText,
showPipeText,
showJunctionTextLayer,
showPipeTextLayer,
junctionText,
pipeText,
]);
// 控制流动动画开关
useEffect(() => {
if (pipeText === "flow" && currentPipeCalData.length > 0) {
flowAnimation.current = true;
} else {
flowAnimation.current = false;
}
}, [currentPipeCalData, pipeText]);
// 水流动画循环
useEffect(() => {
const deckFlow = deckFlowRef.current;
if (!deckFlow || !flowAnimation.current || pipeData.length === 0) {
// 如果不需要动画,清空图层
if (deckFlow) {
deckFlow.setProps({ layers: [] });
}
return;
}
let animationId: number;
const animate = () => {
if (!deckFlow || !flowAnimation.current) return;
if (pipeData.length === 0) {
animationId = requestAnimationFrame(animate);
return;
}
// 动画总时长(秒)
const animationDuration = 10;
// 缓冲时间(秒)
const bufferTime = 2;
// 完整循环周期
const loopLength = animationDuration + bufferTime;
// 确保时间范围与你的时间戳数据匹配
const currentTime = (Date.now() / 1000) % loopLength; // (0,12) 之间循环
const waterflowLayer = new TripsLayer({
id: "waterflowLayer",
data: pipeData,
getPath: (d) => d.path,
getTimestamps: (d) => {
return d.timestamps; // 这些应该是与 currentTime 匹配的数值
},
getColor: [0, 220, 255],
opacity: 0.8,
visible:
waterflowUserVisible.current &&
flowAnimation.current &&
currentZoom >= 12 &&
currentZoom <= 24,
widthMinPixels: 5,
jointRounded: true, // 拐角变圆
// capRounded: true, // 端点变圆
trailLength: 2, // 水流尾迹淡出时间
currentTime: currentTime,
});
deckFlow.setProps({
layers: [waterflowLayer],
});
// 继续请求动画帧,每帧执行一次函数
animationId = requestAnimationFrame(animate);
animationFrameId = requestAnimationFrame(animate);
};
animate();
// 清理函数
// 清理函数:取消动画帧
return () => {
if (animationId) {
cancelAnimationFrame(animationId);
}
if (deckFlow) {
deckFlow.setProps({ layers: [] });
if (animationFrameId) {
cancelAnimationFrame(animationFrameId);
}
};
}, [flowAnimation, pipeData, currentZoom]);
}, [currentZoom, currentPipeCalData, pipeText, pipeData.length]);
// 计算值更新时,更新 junctionData 和 pipeData
useEffect(() => {
const junctionProperties = junctionText;
@@ -965,6 +889,7 @@ const MapComponent: React.FC<MapComponentProps> = ({ children }) => {
setCurrentPipeCalData,
setShowJunctionText,
setShowPipeText,
setShowContourLayer,
setJunctionText,
setPipeText,
showJunctionText,
@@ -980,7 +905,6 @@ const MapComponent: React.FC<MapComponentProps> = ({ children }) => {
{children}
</div>
<canvas id="deck-canvas" />
<canvas id="deck-flow-canvas" />
</MapContext.Provider>
</DataContext.Provider>
</>