feat(D 예정노선): 줌아웃 한도 인자 · 느린 조작 시간 표식 (PLAN 600-3 · 600-4 · 요구 463-27)
- 휠 부품 하한 인자 minScale(안 주면 0.5 = B05 그대로) · 예정노선 지도만 화면 폭 약 600 km(한반도)까지 뺌 - performance 표식 perfTime — 지도 다시 그림 · GL 카메라 · 바탕 · 위 그림 · 마우스 움직임(__aisloPerf 켰을 때만) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -47,6 +47,8 @@ export interface RouteEditNavigationParams {
|
||||
leftPan?: boolean;
|
||||
/** 왼쪽이 옮기기 버튼일 때 페이지가 그 누름을 잡았나(노드 · 마커) — 안 주면 포인터를 잡은 겹이 있으면 잡음 */
|
||||
claimed?: () => boolean;
|
||||
/** 배율 하한 — 안 주면 0.5(B05 그대로). 예정노선 지도만 한반도까지 빼려고 낮은 값을 줌(600-3). */
|
||||
minScale?: () => number;
|
||||
/** Ctrl+휠 = 돌리기(390-3) — 안 주면 Ctrl+휠도 확대(추천 지도). 손가락 · 트랙패드 벌림은 늘 확대. */
|
||||
rotateWheel?: (sign: 1 | -1) => void;
|
||||
draw: () => void;
|
||||
@@ -81,7 +83,10 @@ export function bindRouteEditNavigation(params: RouteEditNavigationParams): void
|
||||
MAX_SCALE_FALLBACK,
|
||||
);
|
||||
const factor = wheelZoomsIn(event) ? ZOOM_IN_FACTOR : ZOOM_OUT_FACTOR;
|
||||
const scale = Math.min(maxScale, Math.max(MIN_SCALE, view.scale * factor));
|
||||
const scale = Math.min(
|
||||
maxScale,
|
||||
Math.max(params.minScale?.() ?? MIN_SCALE, view.scale * factor),
|
||||
);
|
||||
const ratio = scale / view.scale;
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
// 커서 자리를 **화면 중심 기준**으로 잡는다 — 그래야 그 지점이 제자리에 남는다.
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
* 모두 `view` 를 바꾸고 `draw()` → 한 화면 한 번 `glCamera` 로 MapLibre 를 맞춘 뒤 캔버스를 그림.
|
||||
* ========================================================================== */
|
||||
|
||||
import { perfTime } from "@ui/ui_template_gl_map";
|
||||
import { createMapTools, mapToolButton } from "@ui/ui_template_map_tools";
|
||||
import { routeColor, themeColor } from "@ui/ui_template_palette";
|
||||
import type { VWorldMeta } from "../B04_PreProcess/B04_PreProcess_Api_Fetch";
|
||||
@@ -60,6 +61,9 @@ export function planColors(): {
|
||||
};
|
||||
}
|
||||
|
||||
/** 줌아웃 한도(600-3) — 화면 폭 약 600 km(한반도 · 위성 z6). 휠 하한 배율 = 이 폭이 화면에 드는 값(B05 는 0.5 그대로). */
|
||||
export const MAP_MAX_VIEW_WIDTH_M = 600_000;
|
||||
|
||||
/** 위성이 없을 때 맞출 점 둘레로 둘 가짜 도엽 여유(m). */
|
||||
const FAKE_MARGIN_M = 600;
|
||||
|
||||
@@ -406,9 +410,11 @@ export function createPlanMap(events: PlanMapEvents): PlanMap {
|
||||
function flush(): void {
|
||||
cancelAnimationFrame(frame);
|
||||
frame = 0;
|
||||
if (baseDirty) glCamera();
|
||||
if (baseDirty) paint(baseContext, true);
|
||||
if (topDirty) paint(context, false);
|
||||
perfTime("plan:flush", () => {
|
||||
if (baseDirty) perfTime("plan:glCamera", glCamera);
|
||||
if (baseDirty) perfTime("plan:paintBase", () => paint(baseContext, true));
|
||||
if (topDirty) perfTime("plan:paintTop", () => paint(context, false));
|
||||
});
|
||||
baseDirty = topDirty = false;
|
||||
}
|
||||
/** 다음 화면에 한 번 — 바탕까지(화면 · 겹이 바뀜) 또는 위만(마우스). */
|
||||
@@ -455,10 +461,12 @@ export function createPlanMap(events: PlanMapEvents): PlanMap {
|
||||
});
|
||||
canvas.addEventListener("pointermove", (event) => {
|
||||
if (!meta || event.buttons & 4) return; // 가운데 단추 옮기는 중
|
||||
const [px, py] = at(event);
|
||||
const map = screenMap();
|
||||
for (const layer of layers) layer.hover?.(px, py, map);
|
||||
drawTop();
|
||||
perfTime("plan:pointermove", () => {
|
||||
const [px, py] = at(event);
|
||||
const map = screenMap();
|
||||
for (const layer of layers) layer.hover?.(px, py, map);
|
||||
drawTop();
|
||||
});
|
||||
});
|
||||
canvas.addEventListener("pointerleave", () => {
|
||||
for (const layer of layers) layer.leave?.();
|
||||
@@ -502,6 +510,13 @@ export function createPlanMap(events: PlanMapEvents): PlanMap {
|
||||
getMeta: () => meta,
|
||||
unrotateDelta: rotation.unrotateDelta,
|
||||
rotateWheel: rotation.wheel,
|
||||
minScale: () =>
|
||||
meta && view.mapRect.width > 0
|
||||
? Math.min(
|
||||
0.5,
|
||||
(view.width * meta.width_meters) / (view.mapRect.width * MAP_MAX_VIEW_WIDTH_M),
|
||||
)
|
||||
: 0.5,
|
||||
draw,
|
||||
});
|
||||
|
||||
|
||||
@@ -65,6 +65,17 @@ export function satelliteTileSource(projectId: string): SourceSpecification {
|
||||
};
|
||||
}
|
||||
|
||||
/** 느린 조작 잼(600-4) — `globalThis.__aisloPerf = true` 일 때만 `performance.measure(이름)` 으로 걸린 ms 를 남김(꺼져 있으면 그냥 실행). */
|
||||
export function perfTime<T>(name: string, run: () => T): T {
|
||||
if (!(globalThis as { __aisloPerf?: boolean }).__aisloPerf) return run();
|
||||
const start = performance.now();
|
||||
try {
|
||||
return run();
|
||||
} finally {
|
||||
performance.measure(name, { start, end: performance.now() });
|
||||
}
|
||||
}
|
||||
|
||||
export function createGlMap<K extends string>(opts: {
|
||||
/** 겹 차례(아래 → 위)와 꼴 — 밝음 · 어두움이 바뀌면 다시 부름. */
|
||||
specs: () => GlSpec<K>[];
|
||||
@@ -190,8 +201,8 @@ export function createGlMap<K extends string>(opts: {
|
||||
camera(center, zoom, bearingDeg) {
|
||||
if (!map) return;
|
||||
if (ready && themeKey() !== theme) apply();
|
||||
map.jumpTo({ center, zoom, bearing: bearingDeg });
|
||||
if (ready) map.redraw(); // 캔버스와 같은 화면(다음 화면으로 미루지 않음)
|
||||
perfTime("gl:jumpTo", () => map!.jumpTo({ center, zoom, bearing: bearingDeg }));
|
||||
if (ready) perfTime("gl:redraw", () => map!.redraw()); // 캔버스와 같은 화면(다음 화면으로 미루지 않음)
|
||||
},
|
||||
resize: () => map?.resize(),
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user