CSS Graphics Geometry
Guide
Reference
GitHub
Guide
Reference
GitHub
  • Part 0. Math foundations

    • 좌표계와 CSS pixel
    • 점, 벡터, 거리와 기본 용어
    • 각도, 라디안, atan2
  • Part 1. Transform linear algebra

    • translate, scale, rotate
    • CSS matrix(a,b,c,d,e,f)
    • 변환 순서와 transform-origin
    • local, world, screen 좌표계
    • inverse matrix와 포인터 입력
    • 좌표와 transform 디버깅
  • Part 2. Infinite canvas math and architecture

    • viewport와 camera 모델
    • cursor anchored zoom
    • ruler와 tick 계산
    • 주기 함수와 grid step
    • content, overlay, controls layer
    • scene graph와 nested transform
  • Part 3. Editor tool math

    • Pointer Events와 드래그
    • DOMRect와 getBoundingClientRect
    • hit testing과 bounding box
    • selection rectangle과 marquee
    • selection bounds와 handles
    • resize와 rotation handle
    • snapping과 smart guides
    • group, frame, clipping
  • Part 4. CSS graphics properties as math

    • CSS box로 도형 만들기
    • clip-path, radius, shadow
    • 선형보간과 linear-gradient
    • 거리 함수와 radial/conic-gradient
    • stacking context와 합성
    • layout, paint, composite
  • Part 5. Editor state and persistence

    • 레이어 모델과 z-index
    • 이동, 복제, 삭제, 잠금
    • undo/redo command model
    • JSON export/import
  • Part 6. SVG overlay and vector editing

    • SVG viewBox와 좌표계
    • getScreenCTM과 inverse
    • SVG pointer-events와 stroke hit testing
    • cubic bezier path editing
    • mask, clipPath, marker
  • Part 7. Figma to CSS translation

    • Figma node와 CSS DOM 모델
    • Frame constraints와 Auto Layout
    • Fills, strokes, effects를 CSS로
    • Vector와 text 변환
    • gradient 밖의 CSS 수학
    • Figma gradient를 CSS gradient로 변환하기
    • 수학이 필요한 Figma to CSS 속성들
    • Figma VectorNetwork 정리
  • Appendix A. Canvas renderer transition

    • Canvas로 넘어가는 기준
  • Appendix B. Motion and timing

    • 모션 수학과 timing 함수
    • Keyframe timeline 엔진
    • Motion path 수학
    • Bezier curve 길이 구하기

viewport와 camera 모델

이제 드디어 infinite canvas입니다.

무한 캔버스라고 해서 실제 DOM이 무한히 넓어지는 건 아닙니다. 브라우저 화면은 여전히 유한합니다. 대신 우리는 카메라를 움직입니다. 사용자는 문서 세계를 이동한다고 느끼지만, 구현 입장에서는 camera position과 zoom을 바꾸는 일입니다.

말하자면 “세상이 움직인 것처럼 보이게 카메라를 움직이는” 방식입니다.

viewport는 camera다

world 좌표의 점을 화면에 표시하려면 camera 위치를 빼고 zoom을 곱합니다.

screenX = (worldX - cameraX) * zoom
screenY = (worldY - cameraY) * zoom

반대로 화면 좌표에서 world 좌표를 알고 싶으면 zoom으로 나누고 camera를 더합니다.

worldX = screenX / zoom + cameraX
worldY = screenY / zoom + cameraY

이 공식이 viewport의 심장입니다. pan, zoom, ruler, grid, hit testing이 전부 여기에 기대고 있습니다. 꽤 중요한 친구죠.

CSS에서는 content layer를 움직인다

DOM 기반 infinite canvas에서는 보통 실제 오브젝트들을 담는 content layer를 하나 만들고, 여기에 viewport transform을 적용합니다.

<div class="stage-canvas" data-role="viewport">
  <div class="world-layer" data-role="content">
    <div class="shape" data-world-x="80" data-world-y="-20"></div>
  </div>
</div>
.stage-canvas {
  position: relative;
  overflow: hidden;
}

.world-layer {
  position: absolute;
  inset: 0;
  transform-origin: 0 0;
  transform: translate(centerX, centerY)
    scale(zoom)
    translate(-cameraX, -cameraY);
}

.shape {
  position: absolute;
  left: var(--world-x);
  top: var(--world-y);
}

transform-origin: 0 0으로 고정하면 수학 공식과 CSS 결과를 맞추기 쉽습니다. 기본 center origin을 그대로 두면 pan/zoom 계산이 필요 이상으로 요란해집니다.

pan은 camera를 바꾸는 일이고, zoom은 scale을 바꾸는 일입니다. 이때 child object의 world 좌표는 유지합니다. 카메라가 움직였다고 문서 속 오브젝트의 위치가 바뀌면 안 됩니다.

여기서 살짝 장난스러운 포인트가 있습니다. 오브젝트는 가만히 있는데 content layer만 움직입니다. 그래서 DOM을 보면 child의 left/top은 그대로고, 부모인 .world-layer의 transform만 계속 바뀝니다. 화면에서는 세상이 움직이는 것처럼 보이지만, 사실은 카메라 담당 div가 열심히 일하는 중입니다.

데모에서 볼 것

데모에서는 오른쪽 아래 DOM camera stack 패널을 보세요. stage-canvas는 viewport 역할을 하고, world-layer는 content 역할을 합니다. 슬라이더를 움직이면 child shape의 world 좌표는 그대로인데 .world-layer의 matrix 값만 바뀝니다.

마우스를 캔버스 위에서 움직이면 cursor local, centered screen, world 좌표가 같이 갱신됩니다. 기본 wheel은 cursor 기준 zoom이고, Space를 누른 상태의 wheel/trackpad scroll은 pan입니다. 이 세 좌표와 입력 모드가 구분되기 시작하면 viewport는 더 이상 마법 상자가 아닙니다. 그냥 좌표 변환 기계입니다. 말은 좀 무뚝뚝한데, 그래픽 에디터 입장에서는 꽤 든든한 친구입니다.

오늘의 핵심은 infinite canvas를 “큰 DOM”으로 보지 않는 것입니다. 문서는 world에 있고, 사용자는 camera를 통해 봅니다. 이 관점이 잡히면 viewport가 갑자기 단순해집니다.

Edit this page
최근 수정: 26. 5. 14. PM 5:45
Contributors: easylogic
Next
cursor anchored zoom