Python内核代码移除,建模界面缩放bug修复
This commit is contained in:
1 parent
743663e3a6
commit
0dcb465d84
27 files changed
+354
-6092
No files matched your search
+68
-44
@@ -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}
|
||||
|
||||
Reference in new issue
Block a user