Python内核代码移除,建模界面缩放bug修复

This commit is contained in:
ljz committed 2026-09-10 11:57:39 +08:00
1 parent 743663e3a6
commit 0dcb465d84
27 files changed
+354 -6092

No files matched your search

+68 -44
View File
@@ -74,7 +74,6 @@ import {
useEdgesState,
useNodesState,
useReactFlow,
useStore,
useStoreApi,
useUpdateNodeInternals,
} from "@xyflow/react";
@@ -90,6 +89,7 @@ import {
type SimulationResultsSnapshot,
} from "./SimulationResultsView";
import { AutoFitView } from "./AutoFitView";
import { useScrollbarViewport } from "./useScrollbarViewport";
import { DockedSimulationConsole } from "./DockedSimulationConsole";
import {
ComponentSymbol,
@@ -1760,7 +1760,6 @@ function SimulationComponentNodeImpl({
selected,
}: SimulationComponentNodeProps) {
const updateNodeInternals = useUpdateNodeInternals();
const viewportZoom = useStore((flowState) => flowState.transform[2]);
const symbolContainerRef = useRef<HTMLDivElement>(null);
const rotation = normalizeNodeRotation(data.rotation);
const mirrored = Boolean(data.mirrored);
@@ -1817,7 +1816,6 @@ function SimulationComponentNodeImpl({
rotation,
)
: [];
const inverseViewportScale = viewportZoom > 0 ? 1 / viewportZoom : 1;
const nodeLayoutStyle = {
width: renderedSize.width,
height: renderedSize.height,
@@ -1905,8 +1903,7 @@ function SimulationComponentNodeImpl({
style={{
left: x,
top: y,
transform: `scale(${inverseViewportScale}) translate(-50%, -50%)`,
transformOrigin: "0 0",
transform: "translate(-50%, -50%)",
}}
title={`${port.name} (${port.domain})`}
type={portHandleType(port)}
@@ -1916,10 +1913,6 @@ function SimulationComponentNodeImpl({
className="port-handle-glyph"
preserveAspectRatio="xMidYMid meet"
shapeRendering="geometricPrecision"
style={{
transform: `scale(${viewportZoom})`,
transformOrigin: "center",
}}
viewBox="0 0 6 6"
>
{port.kind === "physical" ? (
@@ -3752,11 +3745,14 @@ function FlowViewportScrollbars({
nodes: SimulationNode[];
}) {
const reactFlow = useReactFlow<SimulationNode, SimulationEdge>();
const transform = useStore((flowState) => flowState.transform);
const canvasWidth = useStore((flowState) => flowState.width);
const canvasHeight = useStore((flowState) => flowState.height);
const {
transform,
width: canvasWidth,
height: canvasHeight,
} = useScrollbarViewport();
const horizontalRef = useRef<HTMLDivElement>(null);
const verticalRef = useRef<HTMLDivElement>(null);
const synchronizedScrollRef = useRef({ left: 0, top: 0 });
const [scrollbarViewport, setScrollbarViewport] = useState({
horizontalWidth: 0,
verticalHeight: 0,
@@ -3903,6 +3899,12 @@ function FlowViewportScrollbars({
if (vertical && Math.abs(vertical.scrollTop - expectedScrollTop) > 0.5) {
vertical.scrollTop = expectedScrollTop;
}
// 浏览器可能对滚动位置取整或裁切;记录真正写入的位置,避免稍后
// 到达的原生 scroll 事件被当作用户拖动,反向覆盖刚恢复/缩放的视口。
synchronizedScrollRef.current = {
left: horizontal?.scrollLeft ?? 0,
top: vertical?.scrollTop ?? 0,
};
}, [expectedScrollLeft, expectedScrollTop, horizontalContentWidth, verticalContentHeight]);
const setHorizontalViewportFromScroll = (nextScrollLeft: number) => {
@@ -3911,10 +3913,10 @@ function FlowViewportScrollbars({
(horizontalScrollRange > 0
? (nextScrollLeft / horizontalScrollRange) * horizontalWorldRange
: 0);
const currentViewport = reactFlow.getViewport();
void reactFlow.setViewport({
x: -nextVisibleLeft * zoom,
y: transform[1],
zoom,
...currentViewport,
x: -nextVisibleLeft * currentViewport.zoom,
});
};
const setVerticalViewportFromScroll = (nextScrollTop: number) => {
@@ -3923,10 +3925,10 @@ function FlowViewportScrollbars({
(verticalScrollRange > 0
? (nextScrollTop / verticalScrollRange) * verticalWorldRange
: 0);
const currentViewport = reactFlow.getViewport();
void reactFlow.setViewport({
x: transform[0],
y: -nextVisibleTop * zoom,
zoom,
...currentViewport,
y: -nextVisibleTop * currentViewport.zoom,
});
};
@@ -3941,7 +3943,7 @@ function FlowViewportScrollbars({
className="flow-viewport-scrollbar flow-viewport-scrollbar-horizontal nowheel nopan"
onScroll={(event) => {
const nextScrollLeft = event.currentTarget.scrollLeft;
if (Math.abs(nextScrollLeft - expectedScrollLeft) <= 0.5) {
if (Math.abs(nextScrollLeft - synchronizedScrollRef.current.left) <= 0.5) {
return;
}
setHorizontalViewportFromScroll(nextScrollLeft);
@@ -3992,7 +3994,7 @@ function FlowViewportScrollbars({
className="flow-viewport-scrollbar flow-viewport-scrollbar-vertical nowheel nopan"
onScroll={(event) => {
const nextScrollTop = event.currentTarget.scrollTop;
if (Math.abs(nextScrollTop - expectedScrollTop) <= 0.5) {
if (Math.abs(nextScrollTop - synchronizedScrollRef.current.top) <= 0.5) {
return;
}
setVerticalViewportFromScroll(nextScrollTop);
@@ -4117,12 +4119,11 @@ function PortGeometryRefresh({
nodeIds: string[];
requestKey: number;
}) {
const zoom = useStore((state) => state.transform[2]);
const updateNodeInternals = useUpdateNodeInternals();
const lastRefreshKeyRef = useRef("");
useLayoutEffect(() => {
const refreshKey = `${requestKey}:${zoom}:${nodeIds.join("\u001f")}`;
const refreshKey = `${requestKey}:${nodeIds.join("\u001f")}`;
if (lastRefreshKeyRef.current === refreshKey || nodeIds.length === 0) {
return;
}
@@ -4131,7 +4132,7 @@ function PortGeometryRefresh({
updateNodeInternals(nodeIds);
});
return () => window.cancelAnimationFrame(frame);
}, [nodeIds, requestKey, updateNodeInternals, zoom]);
}, [nodeIds, requestKey, updateNodeInternals]);
return null;
}
@@ -4257,6 +4258,7 @@ function FlowWorkbench() {
const pendingPasteRef = useRef<PendingPaste | null>(null);
const portConnectionDraftRef = useRef<PortConnectionDraft | null>(null);
const modelingViewportRef = useRef<Viewport>({ x: 0, y: 0, zoom: 1 });
const modelingCanvasRef = useRef<HTMLElement>(null);
const lastFlowPointerRef = useRef<{ x: number; y: number } | null>(null);
const pendingPasteMiddlePointerRef = useRef<PendingPasteMiddlePointer | null>(
null,
@@ -4983,6 +4985,9 @@ function FlowWorkbench() {
return;
}
if (view !== "modeling") {
if (modelingCanvasRef.current) {
modelingViewportRef.current = reactFlow.getViewport();
}
cancelPortConnection();
}
setActiveView(view);
@@ -4994,7 +4999,7 @@ function FlowWorkbench() {
window.history.pushState({ workspaceView: view }, "", nextHash);
}
},
[appendConsoleEntry, cancelPortConnection],
[appendConsoleEntry, cancelPortConnection, reactFlow],
);
const loadSimulationResultsSnapshot = useCallback(
@@ -8862,27 +8867,34 @@ function FlowWorkbench() {
"flow-canvas",
activePortConnection?.mode === "click" ? "connection-planning" : "",
].filter(Boolean).join(" ")}
onClickCapture={handleFlowClickCapture}
onDragOver={(event) => {
if (pendingPasteRef.current) {
event.dataTransfer.dropEffect = "none";
return;
ref={modelingCanvasRef}
style={
{
"--port-handle-hit-scale":
1 / Math.max(modelingViewportRef.current.zoom, 0.01),
} as CSSProperties
}
event.preventDefault();
event.dataTransfer.dropEffect = "move";
}}
onDrop={onDrop}
onPointerCancelCapture={() => {
pendingPasteMiddlePointerRef.current = null;
setNodeDragActive(false);
contactDragSessionRef.current = null;
clearContactSnapCandidate();
handleTightSelectionPointerCancel();
}}
onPointerDownCapture={handleFlowPointerDownCapture}
onPointerMoveCapture={handleFlowPointerMoveCapture}
onPointerUpCapture={handleFlowPointerUpCapture}
>
onClickCapture={handleFlowClickCapture}
onDragOver={(event) => {
if (pendingPasteRef.current) {
event.dataTransfer.dropEffect = "none";
return;
}
event.preventDefault();
event.dataTransfer.dropEffect = "move";
}}
onDrop={onDrop}
onPointerCancelCapture={() => {
pendingPasteMiddlePointerRef.current = null;
setNodeDragActive(false);
contactDragSessionRef.current = null;
clearContactSnapCandidate();
handleTightSelectionPointerCancel();
}}
onPointerDownCapture={handleFlowPointerDownCapture}
onPointerMoveCapture={handleFlowPointerMoveCapture}
onPointerUpCapture={handleFlowPointerUpCapture}
>
<ReactFlow
colorMode="light"
connectionMode={ConnectionMode.Loose}
@@ -8919,6 +8931,18 @@ function FlowWorkbench() {
modelingViewportRef.current = { ...viewport };
syncPendingPasteToPointer();
}}
onMoveEnd={() => {
const canvas = modelingCanvasRef.current;
if (!canvas) return;
// 较早的平移结束事件可能晚于下一次缩放到达,读取当前视口,
// 避免旧事件覆盖最新缩放或页面切换时保存的位置。
const viewport = reactFlow.getViewport();
modelingViewportRef.current = viewport;
canvas.style.setProperty(
"--port-handle-hit-scale",
String(1 / Math.max(viewport.zoom, 0.01)),
);
}}
onPaneClick={handleModelingPaneClick}
onSelectionChange={handleModelingSelectionChange}
onSelectionEnd={finishTightSelection}