GODRICH
#css애니메이션#버튼#로딩#전환효과#스크롤#대시보드#실전ui#디자인트렌드

css resize handle 9가지 — 모서리를 끌면 상자가 손에 붙어 따라온다

css resize handle 을 resize 속성 하나로만 두면 손잡이 모양은 못 바꾸고, 줄 높이에 안 맞고, 한계를 넘기면 그냥 막힌다 — 09번·02번·06번이 계산으로 푼다.

아홉 가지 크기 조절 손잡이를 화면에서 바로 끌어 본다

자동 재생 · 눌러서 해당 항목으로 · 전부 한 zip

css resize handle 9가지 — 목표 크기·눈금·고정점을 계산해야 손에 붙는다

장면 하나에는 최대 440×252 칸과 손잡이 하나가 들어 있고(05 만 여덟 개), 크림색 원은 가짜 포인터가 손잡이를 잡아 끄는 시연이다. 직접 잡으면 시연은 물러나고 키보드의 방향키로도 크기가 바뀐다.

아홉 장면이 같은 원리를 쓴다. 포인터 위치를 크기로 바로 쓰지 않고 목표로만 두고, 화면에 그리는 값은 그 목표를 향해 따로 계산한다. 계산 방식은 01·04·06 이 스프링, 02·03·07·09 가 지수 접근이고 05·08 은 보간 없이 그 자리에서 값을 구한다. 아홉 파일 모두 CSS transition 과 @keyframes 가 한 줄도 없고 라이브러리·외부 주소 요청도 없다.

css resizable sidebar 가 목적이면 04 부터, css resizable table columns 가 목적이면 03 부터 열어도 된다. 패널 사이를 가르는 분할선은 CSS 화면 분할 패널 9종 — 드래그로 폭이 따라오는이 맡고, 이 편은 상자·열·이미지 가장자리의 손잡이만 다룬다.

css resize handle style 을 기법별 코드로 본다

아래 조각은 각 장면 index.html 의 <script> 에서 이어진 줄만 그대로 잘랐고, 한 조각은 25줄을 넘지 않는다. 07 만 계산이 CSS 에 있어 style.scss 를 실었다.

01모서리 손잡이를 끌면 스프링처럼 따라오는 상자

포인터는 목표 크기 tw·th 만 정하고 화면의 w·h 는 spring 이 속도를 쥔 채 쫓아가서, 손을 놓으면 한 번 넘쳤다가 자리를 잡는다. 카드·위젯 크기 조절에 맞고, 감쇠를 비율 0.5(DAMPING)로 두어 STIFFNESS 170 을 바꿔도 넘침 비율이 같다. 누적면적 78.1%에 움직인 프레임 23/23 으로 아홉 장면 중 화면을 가장 크게 채운다.

모서리 손잡이를 끌면 스프링처럼 따라오는 상자
새 창에서 열기

01 · 모서리 손잡이를 끌면 스프링처럼 따라오는 상자 — 측정 누적면적 78.149% · 움직인 프레임 23/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

twvwSTIFFNESSDAMPING
    function spring(x, v, to, k, c, dt) {
        const n = Math.max(1, Math.ceil(dt / 0.008));
        const h = dt / n;
        for (let i = 0; i < n; i += 1) {
            v += (-k * (x - to) - c * v) * h;
            x += v * h;
        }
        return [x, v];
    }
    function step(dt, now) {
        init();
        if (demo)
            script(now);
        if (dragging) {
            tw = clamp(px + gx, MIN_W, root.clientWidth);
            th = clamp(py + gy, MIN_H, root.clientHeight);
        }
        const c = 2 * DAMPING * Math.sqrt(STIFFNESS);
        const a = spring(w, vw, tw, STIFFNESS, c, dt);
        const b = spring(h, vh, th, STIFFNESS, c, dt);
        w = a[0];
        vw = a[1];
        h = b[0];
        vh = b[1];
    }

02줄 높이 배수로 달라붙는 텍스트 영역 손잡이

끄는 높이 rawH 를 줄 높이 LINE_H(28)로 나눠 가장 가까운 줄 수로 반올림하고, 상자는 그 줄 수의 높이까지 지수로 따라간다. 메모·댓글 입력창에 맞고, 점선은 손이 있는 높이 그대로라 상자와의 어긋남이 보이며 옆의 ROWS 숫자가 줄 수를 센다. 누적면적 50.6%에 움직인 프레임 23/23 이다.

줄 높이 배수로 달라붙는 텍스트 영역 손잡이
새 창에서 열기

02 · 줄 높이 배수로 달라붙는 텍스트 영역 손잡이 — 측정 누적면적 50.593% · 움직인 프레임 23/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

LINE_HROUND_ROWSEASE_TOrows
    function rowsFor(height) {
        const r = Math.round((height - 2 * PAD) / LINE_H / ROUND_ROWS) * ROUND_ROWS;
        return clamp(r, 1, maxRows());
    }
    function step(dt, now) {
        init();
        if (demo)
            script(now);
        if (dragging) {
            rawH = clamp(py + gy, LINE_H + 2 * PAD, root.clientHeight - 12);
            rows = rowsFor(rawH);
        }
        h += (rows * LINE_H + 2 * PAD - h) * (1 - Math.exp(-EASE_TO * dt));
    }

03경계선을 끌면 이웃 열이 남은 폭을 나눠 받는 표

열 경계를 끌면 그만큼 오른쪽 열이 줄어 세 열의 합은 늘 표 너비와 같고, 이웃이 COL_MIN(56px)에 닿으면 경계가 멈추고 포인터만 더 간다. 데이터 표·목록에 맞으며 말줄임은 JS 가 아니라 scss 의 text-overflow: ellipsis 라서 폭이 바뀌면 브라우저가 바로 다시 자른다. 누적면적 11.6%로 아홉 중 가장 작은데 열 폭만 바뀌는 장면이라 면적이 작은 쪽에 든다.

경계선을 끌면 이웃 열이 남은 폭을 나눠 받는 표
새 창에서 열기

03 · 경계선을 끌면 이웃 열이 남은 폭을 나눠 받는 표 — 측정 누적면적 11.608% · 움직인 프레임 23/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

COL_MINFOLLOWmoveBordertext-overflow
    function moveBorder(i, want) {
        const T = total();
        if (i === 0)
            t0w = clamp(want, COL_MIN, t1w - COL_MIN);
        else
            t1w = clamp(want, t0w + COL_MIN, T - COL_MIN);
    }
    function step(dt, now) {
        init();
        if (demo)
            script(now);
        else if (drag >= 0)
            moveBorder(drag, px + gx);
        const k = 1 - Math.exp(-FOLLOW * dt);
        d0 += (t0w - d0) * k;
        d1 += (t1w - d1) * k;
    }

04세 단계 폭에 철컥 걸리는 사이드바 가장자리

끄는 폭이 눈금 56·144·232(DETENTS)의 36px 안에 들면 눈금에 가까울수록 세게 그 쪽으로 끌려가고, 손을 떼면 raw + vel × 0.2 로 0.2초 앞을 내다본 위치에서 가장 가까운 눈금으로 흘러간다. 시연은 170 에서 손을 놓지만 손 속도가 약 305px/s 라 61px 앞인 231 을 보고, 가까운 144 가 아니라 232 로 간다. 관리자 화면 사이드바에 맞고 누적면적 28.3%에 움직인 프레임 18/23 이다.

세 단계 폭에 철컥 걸리는 사이드바 가장자리
새 창에서 열기

04 · 세 단계 폭에 철컥 걸리는 사이드바 가장자리 — 측정 누적면적 28.338% · 움직인 프레임 18/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

DETENTSPULL_RADIUSFLING_VELnearest
    function pulled(x) {
        const d = DETENTS[nearest(x)];
        const dist = Math.abs(x - d);
        if (dist >= PULL_RADIUS)
            return x;
        const t = 1 - dist / PULL_RADIUS;
        return x + (d - x) * t * t * 0.9;
    }
    function release() {
        if (!dragging)
            return;
        dragging = false;
        target = Math.min(DETENTS[nearest(raw + vel * FLING_VEL)], limit());
    }

05비율을 지키며 여덟 방향으로 늘어나는 이미지

어느 손잡이를 잡든 그 반대쪽 점이 앵커로 남고, 모서리는 포인터 벡터를 대각선 (4/3, 1) 위로 투영해 얻은 한 변 s 하나로 w·h 를 정한다. 이미지 편집기에 맞고, 변 손잡이는 비율만 지킨 채 앵커 변의 중점에서 반대 축으로 펼쳐지며 모서리를 끄는 동안은 분홍 점선이 포인터와 투영된 꼭짓점을 잇는다. 누적면적 50.4%에 움직인 프레임 21/23 이다.

비율을 지키며 여덟 방향으로 늘어나는 이미지
새 창에서 열기

05 · 비율을 지키며 여덟 방향으로 늘어나는 이미지 — 측정 누적면적 50.427% · 움직인 프레임 21/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

ASPECTMIN_SIDEresizeTomaxS
    function resizeTo(i, qx, qy) {
        const W = root.clientWidth;
        const H = root.clientHeight;
        const sx = hx(i);
        const sy = hy(i);
        const ax = sx > 0 ? x : sx < 0 ? x + w : x + w / 2;
        const ay = sy > 0 ? y : sy < 0 ? y + h : y + h / 2;
        if (sx !== 0 && sy !== 0) {
            const dx = (qx - ax) * sx;
            const dy = (qy - ay) * sy;
            const maxS = Math.min((sx > 0 ? W - ax : ax) / ASPECT, sy > 0 ? H - ay : ay);
            const s = clamp((dx * ASPECT + dy) / (ASPECT * ASPECT + 1), MIN_SIDE, maxS);
            w = s * ASPECT;
            h = s;
            x = sx > 0 ? ax : ax - w;
            y = sy > 0 ? ay : ay - h;
        }

06한계를 넘기면 저항이 커지다가 되돌아오는 상자

한계는 컨테이너 폭의 0.64배이고, 그 안에서는 손을 그대로 따르다가 넘으면 한계 + 5 × √(넘은 양) 으로 점점 무거워지며, 놓으면 약한 스프링이 한계선으로 되돌린다. 한계가 있는 패널에 맞고, 노드로 계산하면 늘어난 양의 약 31%만큼 한계를 지나쳤다가 0.68초 만에 자리를 잡는다. 누적면적 55.1%에 움직인 프레임 22/23 이다.

한계를 넘기면 저항이 커지다가 되돌아오는 상자
새 창에서 열기

06 · 한계를 넘기면 저항이 커지다가 되돌아오는 상자 — 측정 누적면적 55.13% · 움직인 프레임 22/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

MAX_RATIORESISTSPRING_BACKstretch
    function stretch(r) {
        const m = maxW();
        return r <= m ? r : m + RESIST * Math.sqrt(r - m);
    }
    function step(dt, now) {
        init();
        if (demo)
            script(now);
        if (dragging) {
            if (!demo)
                raw = clamp(px + gx, MIN_W, root.clientWidth);
            target = clamp(stretch(raw), MIN_W, root.clientWidth);
            const a = spring(w, vw, target, 1600, 80, dt);
            w = a[0];
            vw = a[1];
        }
        else {
            const a = spring(w, vw, target, SPRING_BACK * SPRING_BACK, 2 * 0.35 * SPRING_BACK, dt);
            w = a[0];
            vw = a[1];
        }
    }

07폭이 바뀌는 대로 배치가 갈아입는 카드

상자 폭 하나를 JS 가 끌고, 안쪽 카드의 배치는 scss 의 @container 규칙 두 개가 150px 과 250px 에서 바꾼다. 반응형 컴포넌트 검수에 맞고, JS 는 같은 경계로 구간 이름 STACK·ROW·GRID 만 읽어 화면 아래에 적는다. 누적면적 65.5%에 움직인 프레임 23/23 이다.

폭이 바뀌는 대로 배치가 갈아입는 카드
새 창에서 열기

07 · 폭이 바뀌는 대로 배치가 갈아입는 카드 — 측정 누적면적 65.505% · 움직인 프레임 23/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

BREAK_ABREAK_BRANGE_LABELcontainer-type
@container (min-width: 150px) {
  .rh__cards { grid-template-columns: repeat(3, 1fr); grid-auto-rows: auto; }
  .rh__card { flex-direction: column; align-items: flex-start; justify-content: flex-start; }
  .rh__card span { width: 100%; flex: none; }
}
@container (min-width: 250px) {
  .rh__cards { grid-template-columns: 1fr 1fr; grid-template-rows: 1.2fr 1fr; }
  .rh__card:first-child { grid-column: 1 / -1; flex-direction: row; align-items: center; }
  .rh__card:first-child i { width: 40px; height: 40px; }
  .rh__card span { flex: 1; width: auto; }
}

08격자와 이웃 상자에 맞으면 안내선이 번쩍이는 손잡이

가장 가까운 후보(격자선·이웃 상자의 왼쪽과 오른쪽 가장자리)까지 거리가 12px 보다 짧으면 snapEdge 가 값을 후보 쪽으로 가까울수록 세게 당기고, 4.8px 안에서는 안내선을 켠다. 레이아웃 편집기에 맞고, 안내선은 켜진 뒤 시상수 0.35초로 흐려진다. 누적면적 51.2%에 움직인 프레임 23/23 이다.

격자와 이웃 상자에 맞으면 안내선이 번쩍이는 손잡이
새 창에서 열기

08 · 격자와 이웃 상자에 맞으면 안내선이 번쩍이는 손잡이 — 측정 누적면적 51.228% · 움직인 프레임 23/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

GRID_STEPSNAP_DISTGUIDE_FADEsnapEdge
    function snapEdge(r, cands) {
        let best = cands[0];
        for (let i = 1; i < cands.length; i += 1) {
            if (Math.abs(cands[i] - r) < Math.abs(best - r))
                best = cands[i];
        }
        const d = Math.abs(r - best);
        if (d >= SNAP_DIST)
            return [r, 0, best];
        return [best + (r - best) * (d / SNAP_DIST), d < SNAP_DIST * 0.4 ? 1 : 0, best];
    }
    function resolve(dt) {
        const gridX = Math.round(rawR / GRID_STEP) * GRID_STEP;
        const gridY = Math.round(rawB / GRID_STEP) * GRID_STEP;
        const sx = snapEdge(rawR, [gridX, nbLeft(), nbLeft() + nbWidth()]);
        const sy = snapEdge(rawB, [gridY, nbTop() + nbHeight()]);
        bw = Math.max(MIN_SIDE, sx[0] - GRID_STEP);
        bh = Math.max(MIN_SIDE, sy[0] - GRID_STEP);
        if (sx[1] === 1) {
            aV = 1;
            posV = sx[2];
        }
        else
            aV *= Math.exp(-dt / GUIDE_FADE);
        if (sy[1] === 1) {
            aH = 1;
            posH = sy[2];
        }
        else
            aH *= Math.exp(-dt / GUIDE_FADE);
    }

09다가가면 점이 퍼지고 잡으면 가라앉는 손잡이 모양

점 여섯 개가 포인터까지 거리 56px 안에서 가까울수록 커지며 바깥으로 밀리고, 잡으면 패드가 3px 가라앉으며 가로 속도에 따라 기운다. 손잡이 디자인에 맞고, 크기 조절 자체는 그대로인 채 모양만 손의 위치에 반응한다. 누적면적 42.5%에 움직인 프레임 22/23 이다.

다가가면 점이 퍼지고 잡으면 가라앉는 손잡이 모양
새 창에서 열기

09 · 다가가면 점이 퍼지고 잡으면 가라앉는 손잡이 모양 — 측정 누적면적 42.515% · 움직인 프레임 22/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

GRIP_DOTSHOVER_SPREADPRESS_DEPTHTILT_BY_VEL
        const ox = w - INSET - PAD;
        const oy = h - INSET - PAD;
        pad.style.setProperty("--p", press.toFixed(3));
        pad.style.transform = "translate(" + (press * PRESS_DEPTH).toFixed(2) + "px," + (press * PRESS_DEPTH).toFixed(2) + "px) rotate(" + tilt.toFixed(2) + "deg)";
        for (let i = 0; i < GRIP_DOTS; i += 1) {
            const bx = 40 - cols[i] * 14;
            const by = 40 - rowsOf[i] * 14;
            const dx = ox + bx - px;
            const dy = oy + by - py;
            const d = Math.max(1, Math.hypot(dx, dy));
            const f = Math.max(0, 1 - d / HOVER_SPREAD);
            const s = (1 + 1.2 * f * f) * (1 - 0.2 * press);
            const mx = (dx / d) * 6 * f;
            const my = (dy / d) * 6 * f;
            dots[i].style.transform = "translate(" + (bx + mx - 4).toFixed(2) + "px," + (by + my - 4).toFixed(2) + "px) scale(" + s.toFixed(3) + ")";
        }

값이 손맛을 바꾸는 곳 — 강성·감쇠·눈금 반경·격자 간격

값 하나로 손에 닿는 느낌이 달라지는 곳은 셋이다. 아래 숫자는 같은 계산을 노드에서 돌려 센 것이라 브라우저 렌더로 잰 수치와는 구분한다.

강성을 올려도 넘침은 그대로다

01 의 STIFFNESS 를 170 에서 360 으로 올리면 정착에 걸리는 시간이 0.61초에서 0.41초로 줄지만 넘침은 약 16% 로 같다. 감쇠를 계수가 아니라 비율로 두었기 때문이고, 더 단단한 느낌이 필요하면 DAMPING 도 함께 만져야 한다.

눈금 반경을 키우면 구간이 겹친다

04 의 PULL_RADIUS 36 은 끌림 구간 72px 이고 64 는 128px 인데, 눈금 사이가 88px 라 반경 64 에서는 이웃 눈금의 구간과 포개진다. 폭 170 에서 끌려 나온 값은 168.2 에서 161.8 로 144 쪽에 더 붙는다.

격자 간격을 줄이면 안내선이 잦아진다

08 의 GRID_STEP 을 24 에서 16 으로 줄이면 같은 시연 이동에서 가로로 지나는 선이 10개에서 16개, 세로가 5개에서 7개로 늘어난다. 선이 늘수록 안내선은 더 자주 켜지고 손은 어디에 놓아도 가까운 선의 영향을 받는다.

화면에서 직접 렌더해 얻은 측정은 다음 표에 모았다. 10% 아래로 내려간 장면은 없고 가장 작은 것이 03 의 11.6%다. 움직인 프레임이 23 에 못 미치는 장면은 04(18)·05(21)·06(22)·09(22) 넷이다.

항목 누적면적 평균 강도 움직인 프레임
01 스프링 모서리 78.1% 106.8 23/23
02 줄 높이 달라붙기 50.6% 121.4 23/23
03 표 열 경계 11.6% 129.5 23/23
04 사이드바 눈금 28.3% 159.8 18/23
05 비율 고정 여덟 방향 50.4% 83.5 21/23
06 한계 저항 55.1% 128.2 22/23
07 컨테이너 재배치 65.5% 122.1 23/23
08 격자 안내선 51.2% 93.0 23/23
09 손잡이 모양 반응 42.5% 155.6 22/23

어디서 깨지는가 — 좁은 화면·터치 입력·최소 폭 아래일 때

스프링 폭이 컨테이너 밖으로 넘친다

01 은 목표 tw 를 컨테이너 폭으로 자르지만 실제 w 는 스프링이라 한계 근처에서 약 16%만큼 밖으로 나갔다가 돌아온다. w 에는 clamp 가 없으므로 바깥이 잘리는 자리에 붙이면 넘친 순간의 가장자리를 overflow 로 가려야 한다.

표 열이 56px 에서 글자를 잃는다

03 은 56px 에서 칸 안쪽 폭이 padding 을 뺀 32px 뿐이라 MODIFIED 헤더가 잘려 말줄임 기호로 끝난다. 최소 폭은 숫자 하나일 뿐이라 칸에 들어갈 글자가 길면 최소 폭을 함께 올려야 한다.

대각선과 수직으로 끌면 크기가 안 변한다

05 의 모서리는 대각선 방향 성분만 쓰기 때문에 포인터가 대각선과 수직으로 움직이면 s 가 변하지 않는다. 변 손잡이는 2 × min(ay, H - ay) × ASPECT 가 한계라 중심선이 컨테이너 가장자리에 가까울수록 일찍 멈추고, 시연의 E 변은 폭 약 228 에서 멈춘다.

포인터는 한계를 넘기고 키보드는 못 넘긴다

06 은 포인터로 한계를 넘겨 늘릴 수 있지만 방향키는 maxW() 에서 멈춘다. 한계가 컨테이너 폭의 0.64배라 창 크기가 바뀌면 한계선도 따라 움직인다.

경계 숫자가 JS 와 CSS 에 따로 있다

07 의 150 과 250 은 JS 의 BREAK_A·BREAK_B 와 scss 의 @container 두 규칙에 각각 적혀 있어 한쪽만 고치면 화면 아래 구간 이름이 실제 배치와 어긋난다.

터치에는 호버가 없다

09 의 점은 root 위의 pointermove 에서만 퍼지므로 손가락으로는 잡기 전까지 퍼지지 않는다. 포인터가 root 를 벗어나면 pointerleave 가 위치를 치워 퍼짐이 곧바로 사라진다.

창이 줄어든 뒤에 옛 폭이 남는다

초기 크기는 첫 프레임의 컨테이너 크기에 비율을 곱해 잡는다. 창이 줄면 09 는 매 프레임 자르며 따라오지만 03·04 는 다음 입력이 올 때까지 옛 픽셀 값이 남는다.

직접 고쳐 보며 경계를 확인하는 법

01 의 STIFFNESS, 04 의 PULL_RADIUS, 08 의 GRID_STEP 을 하나씩 바꿔 보면 위 숫자가 어디서 바뀌는지 손으로 확인할 수 있다. 파일은 아래 압축을 비밀번호 mayqf5m8 으로 열면 되고, 아홉 장면 소스와 리액트 판이 들어 있다.

변형 세 가지와 접근성

이름 값 느낌
강성 바꾸기 01 STIFFNESS 170 → 360 넘침 비율 약 16% 는 그대로이고 자리 잡는 시간만 0.61초에서 0.41초로 줄어 손에 더 바짝 붙는다.
눈금 반경 바꾸기 04 PULL_RADIUS 36 → 64 끌림 구간이 72px 에서 128px 로 늘어 폭 170 근처에서 눈금 144 쪽으로 더 크게 끌려간다.
격자 간격 바꾸기 08 GRID_STEP 24 → 16 같은 이동에서 맞는 가로선이 10개에서 16개, 세로선이 5개에서 7개로 늘어 안내선이 훨씬 자주 번쩍인다.

세 변형의 숫자는 노드 시뮬레이션으로 센 값이다.

손잡이는 모두 진짜 <button> 이라 탭으로 포커스가 가고, 방향키로 크기를 바꾼다. 방향키를 누를 때 preventDefault 는 그 장면이 쓰는 키일 때만 부른다. 아홉 장면 모두 setPointerCapture 와 touch-action: none 이 있어서 손잡이를 잡은 채 칸 밖으로 나가도 끌림이 이어지고 터치에서 페이지가 같이 밀리지 않는다.

prefers-reduced-motion 이 켜져 있으면 아홉 장면 모두 시연을 끄고 애니메이션 루프도 시작하지 않으며 한 장만 그린다. 입력은 보간 없이 목표 크기를 바로 그리고 06 은 한계를 넘지 않는다. 한계도 있다. 크기가 바뀌어도 aria-live 로 알리는 장면이 아홉 중 하나도 없어서 스크린리더는 새 크기를 읽어 주지 않으니, 실제 화면에 붙일 때는 숫자를 알리는 영역을 따로 달아야 한다.

css resizable 은 resize 속성 하나로 충분한가?

resize 속성은 사용자가 요소를 가로·세로·양방향으로 늘일 수 있는지만 고르는 값이라, 손잡이 모양을 바꾸거나 이웃 열에 폭을 나눠 주거나 줄 높이에 맞추는 일은 맡지 않는다. 그런 동작이 필요해지는 순간부터 이 편처럼 포인터 이벤트를 받아 크기를 직접 계산한다.

장면마다 붙는 자리는 다르다. 01 은 대시보드의 카드, 02 는 댓글 입력창, 03 은 파일 목록 표, 04 는 관리자 사이드바, 05 는 이미지 편집기, 06 은 한도가 있는 설정 패널, 07 은 반응형 카드 검수 화면, 08 은 레이아웃 편집기, 09 는 손잡이 디자인 시안에 맞다. 가장자리를 끄는 입력 방식은 드래그 앤 드롭 구현 9가지 — 손 뗀 뒤 속도까지 잇는 계산과 같은 원리고, 끈 값이 눈금에 붙는 감각은 css input range 슬라이더 — 관성·스냅 계산을 얹은 커스텀 9종에서 이어진다. 놓는 자리가 목록 순서일 때는 드래그 앤 드롭 UI 리스트 — 목표 인덱스 계산을 얹은 9종을 본다. 포인터 이벤트의 기본 동작은 Pointer events에, 07 이 쓰는 구간 규칙은 컨테이너 쿼리에 정리돼 있다.

css resize handle position 을 정하기 전에 자주 묻는 질문

css resizable image 에서 비율을 지키려면 어느 장면을 보나?

05 를 본다. 앵커를 반대쪽 점에 두고 모서리는 대각선 투영 한 변으로, 변은 가로나 세로 한 축으로 크기를 정해서 비율이 늘 4 대 3 으로 남는다. 다른 비율이 필요하면 ASPECT 만 바꾼다.

방향키는 한 번에 얼마나 움직이나?

장면마다 다르다. 01·06·07·09 는 16px, 03 은 12px, 05 는 손잡이 점을 8px 밀고, 02 는 한 줄, 04 는 이웃 눈금, 08 은 격자 한 칸(24px)씩 움직인다.

라이브러리나 이미지가 필요한가?

필요 없다. 아홉 파일 모두 외부 주소 요청이 없고 HTML·CSS·JS 만으로 돌아서, 한 장면 파일만 옮겨도 동작한다.

압축 비밀번호를 입력하세요

비밀번호는 이 글 본문 안에 있습니다. 읽다 보면 나옵니다.