공용 줌 부품 wheelZoom 에 "ctrl" 갈래 · 트랙패드 핀치 같은 확대 · 그래프 위 브라우저 확대 막음. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013QyNXY2TUKXCFMwAca2APG
105 lines
4.2 KiB
JavaScript
105 lines
4.2 KiB
JavaScript
/* PLAN 397 단위검증 헬퍼 — B06 횡단 카드 Ctrl+휠 줌.
|
|
* 실제 TS(`B06_Section_UI_Cross_View_Zoom.attachZoomPan` → 공용 `b_svg_zoom_pan`)를 Node 로 돌림.
|
|
* 결과 = 표준출력 JSON 한 벌. 짝 = `test_397_cross_ctrl_wheel.py`.
|
|
*/
|
|
const fs = require("fs");
|
|
const path = require("path");
|
|
const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript"));
|
|
|
|
const ROOT = path.join(__dirname, "..", "..");
|
|
const common = fs.readFileSync(
|
|
path.join(ROOT, "B06_Section", "B06_Section_UI_Section_Common.ts"),
|
|
"utf8",
|
|
);
|
|
const CROSS_PAD = Function(`return ${/export const CROSS_PAD = (\{[^}]*\})/.exec(common)[1]}`)();
|
|
const STUBS = { "./B06_Section_UI_Section_Common": { CROSS_PAD, L: (k) => k } };
|
|
function loadTs(full) {
|
|
const js = ts.transpileModule(fs.readFileSync(full, "utf8"), {
|
|
compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 },
|
|
}).outputText;
|
|
const box = { exports: {} };
|
|
const localRequire = (request) => STUBS[request] ?? {};
|
|
new Function("exports", "module", "require", js)(box.exports, box, localRequire);
|
|
return box.exports;
|
|
}
|
|
globalThis.localStorage = { getItem: () => null }; // 휠 방향 기본(밂 = 확대)
|
|
STUBS["./input_prefs"] = loadTs(path.join(ROOT, "A00_Common", "input_prefs.ts"));
|
|
STUBS["../A00_Common/b_svg_zoom_pan"] = loadTs(path.join(ROOT, "A00_Common", "b_svg_zoom_pan.ts"));
|
|
const zoomMod = loadTs(path.join(ROOT, "B06_Section", "B06_Section_UI_Cross_View_Zoom.ts"));
|
|
|
|
const W = 600;
|
|
const H = 300;
|
|
function fakeSvg() {
|
|
const listeners = {};
|
|
const style = {};
|
|
return {
|
|
style: { setProperty: (k, v) => (style[k] = v) },
|
|
styles: style,
|
|
setAttribute() {},
|
|
classList: { add() {}, remove() {} },
|
|
addEventListener: (type, fn) => (listeners[type] ??= []).push(fn),
|
|
/** 사건을 쏘고 preventDefault 가 불렸는지 돌려줌. */
|
|
fire: (type, event) => {
|
|
let prevented = false;
|
|
(listeners[type] ?? []).forEach((fn) =>
|
|
fn({
|
|
preventDefault: () => (prevented = true),
|
|
stopPropagation() {},
|
|
pointerId: 1,
|
|
ctrlKey: false,
|
|
...event,
|
|
}),
|
|
);
|
|
return prevented;
|
|
},
|
|
setPointerCapture() {},
|
|
releasePointerCapture() {},
|
|
getBoundingClientRect: () => ({ left: 0, top: 0, width: W, height: H }),
|
|
};
|
|
}
|
|
|
|
const round = (v) => Math.round(v * 1e6) / 1e6;
|
|
const svg = fakeSvg();
|
|
let state = { scale: 1, tx: 0, ty: 0 };
|
|
const handle = zoomMod.attachZoomPan(svg, fakeSvg(), W, H, undefined, (s) => (state = s));
|
|
/** 화면 (x, y) 아래 도형 층 좌표 — 마우스 중심 판정. */
|
|
const under = (x, y) => [round((x - state.tx) / state.scale), round((y - state.ty) / state.scale)];
|
|
const out = { touchAction: svg.styles["touch-action"] };
|
|
|
|
const at = [400, 120];
|
|
out.plain = { prevented: svg.fire("wheel", { clientX: 400, clientY: 120, deltaY: -100 }) };
|
|
out.plain.scale = state.scale;
|
|
const before = under(...at);
|
|
out.ctrl = {
|
|
prevented: svg.fire("wheel", { clientX: 400, clientY: 120, deltaY: -100, ctrlKey: true }),
|
|
};
|
|
out.ctrl.scale = round(state.scale);
|
|
out.ctrl.anchorKept = JSON.stringify(under(...at)) === JSON.stringify(before);
|
|
svg.fire("wheel", { clientX: 400, clientY: 120, deltaY: 100, ctrlKey: true });
|
|
out.ctrlOut = round(state.scale);
|
|
svg.fire("wheel", { clientX: 200, clientY: 150, deltaY: -8, ctrlKey: true }); // 트랙패드 핀치 벌림
|
|
out.pinch = round(state.scale);
|
|
handle.zoom(1 / 0.85); // [+] 단추 — 같은 상태에 이어 붙음
|
|
out.button = round(state.scale);
|
|
for (let i = 0; i < 40; i++)
|
|
svg.fire("wheel", { clientX: 1, clientY: 1, deltaY: -100, ctrlKey: true });
|
|
out.max = round(state.scale);
|
|
handle.reset(); // [⤢]
|
|
out.fit = state;
|
|
|
|
// 휠 = 줌 화면(B06 상세 · B05 판 · B08)은 그대로 — 맨 휠 줌
|
|
const plainSvg = fakeSvg();
|
|
let plainState = null;
|
|
STUBS["../A00_Common/b_svg_zoom_pan"].attachSvgZoomPan({
|
|
svg: plainSvg,
|
|
layer: fakeSvg(),
|
|
widthPx: W,
|
|
heightPx: H,
|
|
pad: CROSS_PAD,
|
|
wheelZoom: true,
|
|
onChange: (s) => (plainState = s),
|
|
});
|
|
plainSvg.fire("wheel", { clientX: 300, clientY: 150, deltaY: -100 });
|
|
out.wheelTrue = { scale: round(plainState.scale), touchAction: plainSvg.styles["touch-action"] };
|
|
console.log(JSON.stringify(out));
|