- 토량 분배(전체 맞춤) 중에는 유토곡선 ↔ 종단 스크롤 동기화를 끊음 — 0 으로 되돌던 것 없앰 - 횡단 카드는 사면 교차점 · 배수관 세트 폭으로 줌아웃하지 않음 · 밖은 가운데 버튼 팬 · ⤢ 는 보기값으로 - 줌인해도 중심 십자선 길이 그대로(도형 레이어 배율을 십자선 중심에서 되돌림) - 횡단 보기 반폭 기본 12 → 10 m(계산 반폭과 따로) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019EMfGERKUg5nMMNQsyVZ2j
102 lines
4.4 KiB
TypeScript
102 lines
4.4 KiB
TypeScript
/* =============================================================================
|
||
* B06_Section_UI_Cross_View_Zoom.ts
|
||
* 횡단 카드 그래프의 **확대·축소·팬** 조작구.
|
||
*
|
||
* ⚠ 줌·팬 **본문은 공용 조각**(`A00_Common/b_svg_zoom_pan.ts`)으로 옮겼다
|
||
* (2026-09-12 사용자 지시 「공용화해줘」) — 계획노선 편집 횡단이 같은 코드를 쓴다.
|
||
* 여기에는 **B06 것**만 남는다: 카드 여백(`CROSS_PAD`)을 씌운 감싸개, 측점별 배율 기억,
|
||
* 우측 상단 버튼 묶음. 동작 규칙(휠은 줌이 아님)은 그대로다 — 카드가 수십 장 깔리는
|
||
* 화면에서 휠을 줌에 묶으면 목록을 훑을 수가 없다(2026-08-02 사용자 확정).
|
||
* ========================================================================== */
|
||
|
||
import {
|
||
attachSvgZoomPan,
|
||
type ContentBounds,
|
||
type ZoomPanHandle,
|
||
type ZoomPanState,
|
||
} from "../A00_Common/b_svg_zoom_pan";
|
||
import { CROSS_PAD, L } from "./B06_Section_UI_Section_Common";
|
||
|
||
export type { ContentBounds, ZoomPanHandle, ZoomPanState };
|
||
|
||
/**
|
||
* 카드 표시 반폭 조작(2026-08-23 사용자 지시). 카드 하단 ◀/▶/↺을 없애고 우측 상단
|
||
* 줌 버튼에 합쳤다 — 원배율에서 −는 폭 넓히기, ⤢는 보기값 되돌림이다.
|
||
*/
|
||
export interface CrossWidthActions {
|
||
/** 표시 반폭을 deltaM(m)만큼 조절한다. 계산 반폭을 넘으면 페이지가 재생성까지 처리. */
|
||
step: (deltaM: number) => void;
|
||
/**
|
||
* 초기화 — 표시 반폭을 전역 보기값으로 되돌린다(PLAN 79-2 · 사면 교차점은 팬으로 봄).
|
||
*/
|
||
fit: () => void;
|
||
}
|
||
|
||
/** 측점별 줌·팬 상태 — 카드를 다시 만들어도 배율이 살아남는다(2026-08-22 사용자 ③). */
|
||
export const cardZoomStates = new Map<string, ZoomPanState>();
|
||
|
||
/** 카드 SVG 에 줌·팬을 붙인다 — 여백만 씌워 공용 조각에 넘긴다. 부르는 꼴은 그대로다. */
|
||
export function attachZoomPan(
|
||
svg: SVGSVGElement,
|
||
plotLayer: SVGGElement,
|
||
widthPx: number,
|
||
heightPx: number,
|
||
/** 이전 카드의 줌·팬 상태 — 조정창 조작으로 카드가 다시 그려져도 배율 유지. */
|
||
initial?: ZoomPanState,
|
||
/** 상태가 바뀔 때마다 부른다 — 카드 밖(뷰)이 담아 뒀다가 다음 렌더에 되돌린다. */
|
||
onChange?: (state: ZoomPanState) => void,
|
||
/** 도형이 실제로 그려진 범위(캐시 보유 폭). 확대 상태에서 팬 한계가 여기까지 늘어난다. */
|
||
content?: ContentBounds,
|
||
): ZoomPanHandle {
|
||
return attachSvgZoomPan({
|
||
svg,
|
||
layer: plotLayer,
|
||
widthPx,
|
||
heightPx,
|
||
pad: CROSS_PAD,
|
||
initial,
|
||
onChange,
|
||
content,
|
||
// 휠은 카드 목록을 훑는 몫이다 — 줌에 안 묶는다.
|
||
wheelZoom: false,
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 그래프 우측 상단 줌 버튼(확대/축소/핏). 휠을 대신하는 조작구다.
|
||
*
|
||
* `width`를 넘기면 **원배율에서만** 버튼이 표시 반폭을 조절한다(2026-08-23 사용자 지시):
|
||
* + : 배율 확대(항상)
|
||
* − : 배율>1이면 축소, 원배율이면 표시 반폭 +1m(계산 반폭 20m를 넘으면 백엔드 재계산)
|
||
* ⤢ : 배율 원복 + 표시 반폭 초기화(전역 보기값)
|
||
*/
|
||
export function buildZoomControls(handle: ZoomPanHandle, width?: CrossWidthActions): HTMLElement {
|
||
const bar = document.createElement("div");
|
||
bar.className = "b06-cross-card__zoom";
|
||
const add = (label: string, title: string, action: () => void): void => {
|
||
const button = document.createElement("button");
|
||
button.type = "button";
|
||
button.className = "b06-cross-card__zoom-btn";
|
||
button.textContent = label;
|
||
button.title = title;
|
||
button.addEventListener("click", (event) => {
|
||
// 카드 선택으로 번지면 그래프가 다시 그려져 방금 맞춘 배율이 날아간다.
|
||
event.stopPropagation();
|
||
action();
|
||
});
|
||
bar.append(button);
|
||
};
|
||
// +는 언제나 배율 확대다 — 원배율에서 폭 좁히기로 바꿔 놨더니 배율이 1에서 못 올라가
|
||
// 가운데 버튼 팬(배율>1에서만 열림)까지 막혔다(2026-08-23 사용자 보고).
|
||
add("+", L("B06_Profile_View_ZoomIn"), () => handle.zoom(1 / 0.85));
|
||
add("−", L("B06_Profile_View_ZoomOut"), () => {
|
||
if (!width || handle.scale() > 1 + 1e-6) handle.zoom(0.85);
|
||
else width.step(1);
|
||
});
|
||
add("⤢", L("B06_Profile_View_ZoomFit"), () => {
|
||
handle.reset();
|
||
width?.fit();
|
||
});
|
||
return bar;
|
||
}
|