Merge remote-tracking branch 'origin/sub_laptop_2' into main_laptop_1
This commit is contained in:
@@ -24,13 +24,20 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* 복제를 놓는 판 — 화면(뷰포트) 크기 · fixed 자식의 기준 상자 */
|
||||
/* 복제를 놓는 판 — 화면(뷰포트) 크기 · 움직임은 translate 만(합성층 · 374-1) */
|
||||
.ui-loupe__stage {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100vw;
|
||||
height: 100dvh;
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
/* 배율 상자 — 배율 바꿀 때만 scale · fixed 자식의 기준 상자 */
|
||||
.ui-loupe__scale {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
transform-origin: 0 0;
|
||||
}
|
||||
|
||||
@@ -55,6 +62,8 @@
|
||||
border-radius: 9px;
|
||||
background: var(--color-bg);
|
||||
color: var(--color-accent);
|
||||
/* html 바로 아래라 body 글꼴을 안 물려받음 */
|
||||
font-family: var(--font-body);
|
||||
font-size: 11px;
|
||||
line-height: 18px;
|
||||
box-shadow: 0 0 0 1px var(--color-accent);
|
||||
|
||||
@@ -5,9 +5,10 @@
|
||||
* 켜는 법 둘 — 오른쪽 단추를 0.4 초 넘게 누르고 있는 동안 · Z 키를 누르고 있는 동안 · 떼면 꺼짐.
|
||||
* 켜 있는 동안 휠 = 배율(220-1 · 1.5 ~ 6배 · 한 칸 0.25 · 켤 때마다 2배) · 화면 스크롤 · 그래프 휠 줌은 막음.
|
||||
* 길게 누른 우클릭은 뗀 뒤 메뉴(우리 메뉴 · 브라우저 메뉴)를 안 띄움 · 짧게 누르면 지금 우클릭 그대로.
|
||||
* 그리기 — 켤 때 `#app` 을 한 번 떠서(DOM 복제 + 2D canvas 그림 복사 · SVG 그대로) 원 안에 놓고, 움직일 때 transform 만 바꿈.
|
||||
* 화면 밖 상자는 속을 비우고 크기만 남김 · 켜 있는 동안 화면이 바뀌면 0.5 초 뒤 다시 뜸 · 끄면 복제를 지움.
|
||||
* 스크롤(221-2) — 뜰 때 스크롤 칸마다 scrollTop · Left 를 복제에 옮김 · 켜진 동안 어느 칸이든 스크롤하면 그 화면 틀(rAF 한 번)에 위치를 맞추고 0.5 초 뒤 다시 뜸(비워 둔 화면 밖 칸을 채움).
|
||||
* 그리기 — 켤 때 지금 보이는 화면 전체(`body` · 모달 포함 · 374-1)를 한 번 떠서(DOM 복제 + 2D canvas 그림 복사 · SVG 그대로) 원 안에 놓고,
|
||||
* 움직일 때 바깥 판(합성층) translate 만 바꿈 · 배율은 안쪽 상자 scale. 원 · 배율 글은 `html` 바로 아래(복제 · 관찰에 안 걸림).
|
||||
* 화면 밖 상자는 속을 비우고 크기만 남김 · 켜 있는 동안 화면이 바뀌면 멈춘 뒤 0.5 초에 한 번 다시 뜸(움직이는 동안 안 뜸) · 끄면 복제를 지움.
|
||||
* 스크롤(221-2) — 뜰 때 스크롤 칸마다 scrollTop · Left 를 복제에 옮김 · 켜진 동안 어느 칸이든 스크롤하면 그 화면 틀(rAF 한 번)에 위치를 맞추고 멈춘 뒤 0.5 초에 다시 뜸(비워 둔 화면 밖 칸을 채움).
|
||||
* WebGL canvas 는 그림이 남아 있을 때만 옮겨짐.
|
||||
* CAD 영역(cad_host 상자 · iframe · 221-1) — 커서가 그 위면 켜지 않고 · 켜진 채 들어가면 숨김(나오면 다시) · 휠도 CAD 로 · 복제는 빈칸.
|
||||
* ========================================================================== */
|
||||
@@ -28,7 +29,7 @@ export const LOUPE_ZOOM_STEP = 0.25;
|
||||
const LOUPE_LABEL_H = 18;
|
||||
/** 오른쪽 단추를 이만큼(ms) 넘게 누르면 켬 */
|
||||
export const LOUPE_HOLD_MS = 400;
|
||||
/** 켜 있는 동안 화면이 바뀌면 이만큼(ms) 뒤 다시 뜸 */
|
||||
/** 켜 있는 동안 화면이 바뀌면 멈춘 뒤 이만큼(ms) 지나 다시 뜸 */
|
||||
export const LOUPE_REFRESH_MS = 500;
|
||||
/** 화면 끝과 원 사이 여백(px) — 테두리가 안 잘리게 */
|
||||
export const LOUPE_EDGE = 4;
|
||||
@@ -242,11 +243,12 @@ export function initLoupe(
|
||||
/** 원 · 복제 · 다시 뜨기를 맡음 — `sourceOf()` 가 뜰 영역 */
|
||||
export function createLoupeView(
|
||||
win: Window,
|
||||
sourceOf: () => Element | null = () => win.document.getElementById("app"),
|
||||
sourceOf: () => Element | null = () => win.document.body,
|
||||
): LoupeView {
|
||||
const doc = win.document;
|
||||
let root: HTMLElement | null = null;
|
||||
let stage: HTMLElement | null = null;
|
||||
let stage: HTMLElement | null = null; // 이동만(합성층)
|
||||
let scaler: HTMLElement | null = null; // 배율만 · 복제를 담음
|
||||
let label: HTMLElement | null = null;
|
||||
let zoom = LOUPE_ZOOM;
|
||||
let at: [number, number] = [0, 0]; // 마지막 커서 — 배율만 바뀔 때 다시 놓음
|
||||
@@ -263,23 +265,28 @@ export function createLoupeView(
|
||||
root.style.width = root.style.height = `${LOUPE_DIAMETER}px`;
|
||||
stage = doc.createElement("div");
|
||||
stage.className = "ui-loupe__stage";
|
||||
scaler = doc.createElement("div");
|
||||
scaler.className = "ui-loupe__scale";
|
||||
stage.append(scaler);
|
||||
const cross = doc.createElement("div");
|
||||
cross.className = "ui-loupe__cross";
|
||||
root.append(stage, cross);
|
||||
label = doc.createElement("div");
|
||||
label.className = "ui-loupe__zoom";
|
||||
label.setAttribute("aria-hidden", "true");
|
||||
doc.body.append(root, label);
|
||||
// body 밖 — body 를 뜨고 지켜보므로 원이 자기 복제 · 자기 움직임 관찰에 안 걸림
|
||||
doc.documentElement.append(root, label);
|
||||
};
|
||||
const capture = (): void => {
|
||||
const source = sourceOf();
|
||||
const t0 = win.performance?.now() ?? 0;
|
||||
stage!.replaceChildren();
|
||||
if (source) sync = captureInto(source, stage!, win);
|
||||
scaler!.replaceChildren();
|
||||
if (source) sync = captureInto(source, scaler!, win);
|
||||
root!.dataset.captureMs = String(Math.round((win.performance?.now() ?? 0) - t0));
|
||||
};
|
||||
/** 다시 뜨기 미룸 — 바뀜 · 스크롤 · 움직임마다 처음부터(멈춘 뒤 한 번 · 374-1) */
|
||||
const refresh = (): void => {
|
||||
if (refreshTimer) return;
|
||||
win.clearTimeout(refreshTimer);
|
||||
refreshTimer = win.setTimeout(() => {
|
||||
refreshTimer = 0;
|
||||
if (observer) capture();
|
||||
@@ -316,16 +323,18 @@ export function createLoupeView(
|
||||
move(x, y) {
|
||||
if (!root || !stage || !label) return;
|
||||
at = [x, y];
|
||||
if (refreshTimer) refresh(); // 움직이는 동안 안 뜸
|
||||
const { left, top } = loupeOrigin(x, y, win.innerWidth, win.innerHeight);
|
||||
const r = LOUPE_DIAMETER / 2;
|
||||
root.style.transform = `translate(${left}px, ${top}px)`;
|
||||
stage.style.transform = `translate(${r - zoom * x}px, ${r - zoom * y}px) scale(${zoom})`;
|
||||
stage.style.transform = `translate(${r - zoom * x}px, ${r - zoom * y}px)`;
|
||||
const labelTop = Math.min(top + LOUPE_DIAMETER + 4, win.innerHeight - LOUPE_LABEL_H - 4);
|
||||
label.style.transform = `translate(${left + r}px, ${labelTop}px) translateX(-50%)`;
|
||||
},
|
||||
setZoom(z) {
|
||||
zoom = z;
|
||||
if (label) label.textContent = `×${z}`;
|
||||
if (scaler) scaler.style.transform = `scale(${z})`;
|
||||
view.move(...at);
|
||||
},
|
||||
hide() {
|
||||
@@ -336,7 +345,7 @@ export function createLoupeView(
|
||||
win.cancelAnimationFrame(syncFrame);
|
||||
syncFrame = 0;
|
||||
win.removeEventListener("scroll", onScroll, true);
|
||||
stage?.replaceChildren(); // 복제를 지움
|
||||
scaler?.replaceChildren(); // 복제를 지움
|
||||
if (root) root.hidden = label!.hidden = true;
|
||||
},
|
||||
};
|
||||
|
||||
@@ -370,6 +370,9 @@
|
||||
--color-primary-text: #16121f;
|
||||
--color-accent: var(--color-lavender-wash);
|
||||
--color-focus-ring: var(--color-lavender-wash);
|
||||
|
||||
--color-success: #3fbb6f;
|
||||
--color-danger: #e8695a;
|
||||
--panel-handle-border: #e0b64a;
|
||||
--map-route-locked: #f472b6;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user