- 줌 버튼을 셋(줌인·줌아웃·초기화)으로 줄임. 세로 배율·창 이동 버튼 제거 - 배율 1 = 기본값이자 축소 한계 — 한계에 닿은 버튼은 흐리게 죽임 - 세로는 보이는 누가거리 구간의 지반·계획선 범위로 자동(공통 함수 windowElevationRange, B05·B06 종단이 함께 씀). 스크롤이 멈춘 뒤 0.16초에 갱신 - 계획고 편집 버튼을 누르고 있는 동안 Y 축 고정, 손을 떼면 다시 맞춤 - 유토곡선 Y 도 같은 창 기준(전 구간 ±200㎥ 고정 해제) - B06 종단이 쓰던 공통 Y 스케일(calculateYScale) 제거 — 횡단 카드는 원래 안 쓰던 값 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
81 lines
3.6 KiB
TypeScript
81 lines
3.6 KiB
TypeScript
/* =============================================================================
|
||
* B05_Profile_UI_Profile_Zoom.ts
|
||
* 종단면도 줌 조작구 — 버튼 셋(줌인·줌아웃·초기화), 2026-09-04 사용자 확정.
|
||
*
|
||
* 공사 범위가 넓으면 종단 그래프가 눌려 읽히지 않는다. 사람이 맞출 것은 **가로 하나**다.
|
||
*
|
||
* X (가로) — **폭 배수**다. SVG transform 으로 늘리면 그래프만 커지고 측점 테이블·
|
||
* 계획고 편집 버튼층·구조물 알약 레인이 어긋난다(넷이 같은 `chainageMapper`
|
||
* 를 쓴다). 캔버스 폭 자체를 키우고 가로 스크롤로 훑는다.
|
||
* Y (세로) — **프로그램이 자동으로 맞춘다**. 보이는 구간의 지반·계획선 범위에 맞춰
|
||
* 잡으므로(`windowElevationRange`) 세로 배율·창 이동 버튼이 필요 없어졌다.
|
||
* 옛 `⇕+`·`⇕−`·`▲`·`▼` 네 버튼은 그래서 없앴다.
|
||
*
|
||
* **배율 1 = 기본값이자 축소 한계**(사용자 확정) — 폭맞춤보다 더 줄이면 측점이 겹쳐
|
||
* 읽을 수 없다. 한계에 닿은 버튼은 흐리게 죽인다.
|
||
*
|
||
* 배율은 페이지가 들고 있다 — 편집·재계산으로 다시 그려도 유지된다(B06 `cardZoomStates` 규칙).
|
||
* ========================================================================== */
|
||
|
||
/** 한 번 누를 때 배율 배수. 횡단도 줌(1/0.85)보다 성글게 — 폭 배수라 한 칸이 크게 느껴진다. */
|
||
const ZOOM_STEP = 1.25;
|
||
/** 가로 폭 배수 상한 — 이 이상은 캔버스가 수만 px이 되어 브라우저가 버겁다. */
|
||
const MAX_X = 8;
|
||
|
||
export interface ProfileZoomState {
|
||
/** 가로 폭 배수(1 = 현행 폭맞춤 = 기본값·축소 한계). */
|
||
x: number;
|
||
}
|
||
|
||
export interface ProfileZoom {
|
||
/** 절·성토 요약줄 오른쪽 끝에 붙는 버튼 묶음(한 번 만들어 계속 쓴다). */
|
||
bar: HTMLElement;
|
||
state: () => ProfileZoomState;
|
||
}
|
||
|
||
const clamp = (value: number, min: number, max: number): number =>
|
||
Math.min(max, Math.max(min, value));
|
||
|
||
export function createProfileZoom(onChange: () => void): ProfileZoom {
|
||
const state: ProfileZoomState = { x: 1 };
|
||
|
||
const bar = document.createElement("div");
|
||
bar.className = "b05-profile__zoom";
|
||
|
||
function add(label: string, title: string, action: () => void): HTMLButtonElement {
|
||
const button = document.createElement("button");
|
||
button.type = "button";
|
||
button.className = "b05-profile__zoom-btn";
|
||
button.textContent = label;
|
||
button.title = title;
|
||
button.addEventListener("click", (event) => {
|
||
// 카드·측점 선택으로 번지면 그래프를 다시 그리며 방금 맞춘 배율이 날아간다.
|
||
event.stopPropagation();
|
||
action();
|
||
syncDisabled();
|
||
onChange();
|
||
});
|
||
bar.append(button);
|
||
return button;
|
||
}
|
||
|
||
const zoomIn = add("+", "가로 확대 — 측점 간격을 넓혀 폅니다 (세로는 자동으로 맞춥니다)", () => {
|
||
state.x = clamp(state.x * ZOOM_STEP, 1, MAX_X);
|
||
});
|
||
const zoomOut = add("−", "가로 축소 — 기본 폭(화면 맞춤)까지만 줄어듭니다", () => {
|
||
state.x = clamp(state.x / ZOOM_STEP, 1, MAX_X);
|
||
});
|
||
add("⤢", "기본 상태로 — 가로 폭맞춤, 세로 자동", () => {
|
||
state.x = 1;
|
||
});
|
||
|
||
/** 한계에 닿은 버튼은 눌러도 변화가 없다 — 흐리게 죽여 그 사실을 보인다. */
|
||
function syncDisabled(): void {
|
||
zoomOut.disabled = state.x <= 1 + 1e-9;
|
||
zoomIn.disabled = state.x >= MAX_X - 1e-9;
|
||
}
|
||
syncDisabled();
|
||
|
||
return { bar, state: () => ({ ...state }) };
|
||
}
|