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 길이 구하기

선형보간과 linear-gradient

이번에는 CSS linear-gradient를 보겠습니다. 이름은 “그라디언트”라서 디자인 기능처럼 보이지만, 안쪽을 열어보면 꽤 정직한 수학 함수입니다.

브라우저는 박스 안의 각 픽셀을 보고 이렇게 묻습니다.

“너는 gradient 축 위에서 어디쯤 있니?”

그리고 그 위치에 맞춰 색을 섞습니다. 그러니까 linear-gradient는 색상 목록이 아니라, 픽셀을 축에 투영하고 color stop 사이를 보간하는 함수입니다. 색칠 공부 같지만 속은 좌표 계산입니다. 은근히 야무집니다.

CSS 각도는 조금 특이하다

CSS에서 linear-gradient(0deg, ...)는 오른쪽이 아니라 위쪽을 향합니다. 일반적인 수학 좌표계에 익숙하면 처음에 살짝 배신감이 듭니다.

0deg = to top
90deg = to right

그래서 각도 theta에서 gradient axis는 이렇게 잡을 수 있습니다.

axis = (sin(theta), -cos(theta))

theta = 0deg이면 (0, -1)이 됩니다. 화면에서 y가 아래로 증가하니까 위쪽은 음수 y입니다. 브라우저 좌표계가 여기서 또 슬쩍 등장합니다. 지난 시간 내용이 몰래 따라왔죠.

gradient line은 보이는 대각선이 아니다

여기서 중요한 함정이 하나 있습니다.

gradient line은 박스 중심을 지나지만, “박스 안에서 실제로 보이는 선분”과 같은 길이가 아닙니다. 브라우저는 박스의 네 꼭짓점을 gradient 축에 투영했을 때 전체 범위를 덮도록 선의 길이를 잡습니다.

그래야 박스의 한쪽 끝은 첫 색에, 반대쪽 끝은 마지막 색에 안정적으로 닿습니다.

L = width * |axis.x| + height * |axis.y|
L = width * |sin(theta)| + height * |cos(theta)|

예를 들어 200 x 100 박스에서 90deg라면 axis는 오른쪽을 봅니다. 길이는 200입니다. 그런데 45deg라면 가로와 세로가 둘 다 영향을 줍니다.

L = width * |sin(45deg)| + height * |cos(45deg)|

이 값은 단순한 대각선 길이와 다릅니다. “대각선이면 피타고라스 아닌가요?” 하고 묻고 싶어질 수 있습니다. 좋은 질문입니다. 하지만 여기서는 박스 내부의 한 선분 길이가 아니라, 모든 픽셀을 축에 투영했을 때 필요한 전체 투영 범위를 구하는 중입니다.

linear gradient axis projection diagramvisible chordaxis = (sin theta, -cos theta)L = W|axis.x| + H|axis.y|t = dot(pixel - center, axis)u = (t + L / 2) / L
linear-gradient는 박스의 모든 꼭짓점을 축에 투영해서 필요한 전체 길이 L을 만들고, 각 픽셀을 그 축 위의 u 값으로 샘플링합니다.

각 픽셀은 축 위로 투영된다

박스 중심을 center라고 하고, 픽셀 위치를 pixel이라고 하겠습니다. 픽셀이 gradient axis 위에서 얼마나 떨어져 있는지는 dot product로 계산합니다.

t = dot(pixel - center, axis)

이 값을 0..1 범위로 바꾸면 color stop을 샘플링할 수 있습니다.

u = (t + L / 2) / L
start = center - axis * L / 2
end = center + axis * L / 2

u = 0이면 시작 색, u = 1이면 끝 색입니다. 중간이면 두 색을 섞습니다.

color = color0 * (1 - u) + color1 * u

물론 실제 CSS 색상 보간은 색 공간에 따라 더 복잡해질 수 있습니다. sRGB에서 섞느냐, OKLCH 같은 공간에서 섞느냐에 따라 중간색이 달라집니다. 하지만 기본 뼈대는 “축 위의 위치를 구하고, 그 위치로 색을 샘플링한다”입니다.

에디터에서는 무엇을 저장할까

디자인 에디터에서 gradient를 만들 때는 CSS 문자열을 그대로 모델로 삼기보다, 보통 이런 값을 저장하는 편이 다루기 쉽습니다.

  • 중심점
  • 축 방향
  • stop 목록
  • 각 stop의 위치 0..1
  • 각 stop의 색

사용자가 gradient handle을 드래그하면 시작점과 끝점으로 axis를 만들고, stop marker는 그 축 위의 위치값으로 배치합니다. CSS로 내보낼 때만 linear-gradient(...) 문자열로 직렬화하면 됩니다.

데모의 초록 선은 CSS가 색상 보간에 사용하는 gradient line 길이 L을 보여줍니다. 박스 안에서 중심을 통과하며 실제로 보이는 교차선 길이는 아래처럼 따로 계산할 수 있습니다.

visibleChord = min(width / |axis.x|, height / |axis.y|)

이 둘을 구분해야 gradient editor의 핸들이 “왜 저 위치에 있지?” 하는 얄미운 버그를 피할 수 있습니다. 수학이 조금 정확하면 UI가 훨씬 덜 삐걱댑니다.

데모에서 볼 것

angle과 stop position을 바꿔보면서 픽셀 색이 축 투영값으로 결정된다는 점을 확인합니다.

오늘의 핵심은 이것입니다. linear-gradient는 “예쁜 배경 문법”이 아니라, 박스 위에 놓인 1차원 좌표계를 색으로 읽는 함수입니다.

Edit this page
최근 수정: 26. 5. 14. PM 5:45
Contributors: easylogic
Prev
clip-path, radius, shadow
Next
거리 함수와 radial/conic-gradient