[TypeScript] 변환 원점 찾기

Table of contents

잠깐!
transform
에 대해 아직 잘 모른다면 [CSS] 변환 행렬을 먼저 읽어보세요!
상황에 따라 동적으로 뷰포트에 대한 transform-origin
의 좌표를 구해야 할 수 있습니다. 대상이 변환이 수행되지 않은 경우라면 getBoundingClientRect
를 사용해 쉽게 구할 수 있습니다. 하지만 변환이 수행된 경우는 생각보다 까다롭습니다.
차근차근 살펴보도록 하겠습니다. 우선 변환이 고려되지 않은 상황, 그중에서도 대상 영역에 대한 transform-origin
의 상대 좌표를 가져오는 방법은 아래와 같습니다.
const [
transformOriginX,
transformOriginY
] = getComputedStyle(target)
.transformOrigin
.split(" ")
.map(p => parseFloat(p));
style
속성은 지정된 경우에만 가져올 수 있을뿐더러 단위에 대한 일관성도 없기 때문에 실제 계산된 결과를 가져와야 합니다. 이는 getComputedStyle
을 통해 비교적 간단히 수행할 수 있습니다.
이제 transform-origin
대한 각 꼭짓점의 상대 좌표를 얻을 차례입니다. 영역에 대한 transform-origin
의 상대 좌표를 \((O_x, O_y)\)라 하고 영역의 너비를 \(w\), 높이를 \(h\)라 할 때 각 꼭짓점의 좌표는 아래와 같습니다.
$$\begin{aligned} & A(-O_x, -O_y) \\ & B(w - O_x, -O_y) \\ & C(w - O_x, h - O_y) \\ & D(-O_x, h - O_y) \end{aligned}$$
그리고 변환 행렬은 함수로 나타낼 때 \(m: (x, y) \mapsto (ax + cy + e, bx + dy + f)\) 이므로 좌표는 각각 아래와 같이 변환됩니다.
$$\begin{aligned} x & = -O_x \\ x' & = ax + cy + e \\ \\ y & = -O_y \\ y' & = bx + dy + f \end{aligned}$$
라 하면
$$\begin{aligned} A(x, y) & \xrightarrow{m} A'(x', y') \\ B(x + w, y) & \xrightarrow{m} B'(x' + aw, y' + bw) \\ C(x + w, y + h) & \xrightarrow{m} C'(x' + aw + ch, y' + bw + dh) \\ D(x, y + h) & \xrightarrow{m} D'(x' + ch, y' + dh) \end{aligned}$$
이제 다음은 무엇을 해야 할까요? 새로운 영역의 한 꼭짓점 좌표를 구해야 합니다. 이 좌표와 getBoundingClientRect
를 통해 얻을 수 있는 실제 좌표의 차를 구하면 초기 변환 원점 즉, 변환 이전 transform-origin
의 뷰포트에 대한 상대 좌표를 구할 수 있습니다.
바로 가봅시다. 어느 꼭짓점이라도 상관없지만 가장 조건이 단순한 점은 좌측 상단일 것입니다. 모든 축에 대해 가장 작은 경우이니 말이지요.
$$\begin{aligned} x_{min} & = x' + min(min(aw, ch), min(0, aw + ch)) \\ & = ax + cy + e + min(min(aw, ch), min(0, aw + ch))\\ \\ y_{min} & = y' + min(min(bw, dh), min(0, bw + dh)) \\ & = bx + dy + f + min(min(bw, dh), min(0, bw + dh)) \end{aligned}$$
따라서 getBoundingClientRect
로 얻은 좌측 상단 좌표를 \((l, t)\)라 할 때,
$$\begin{aligned} x_{transformOrigin} & = l - x_{min} \\ & = l - (ax + cy + e + min(min(aw, ch), min(0, aw + ch)))\\ \\ y_{transformOrigin} & = t - y_{min} \\ & = t - (bx + dy + f + min(min(bw, dh), min(0, bw + dh))) \end{aligned}$$
결국 변환 후 transform-origin
은 아래와 같습니다.
$$\begin{aligned} x'_{transformOrigin} & = x_{transformOrigin} + e \\ & = l - (ax + cy + e + min(min(aw, ch), min(0, aw + ch))) + e \\ & = l - (ax + cy + min(min(aw, ch), min(0, aw + ch))) \\ \\ y'_{transformOrigin} & = y_{transformOrigin} + f \\ & = t - (bx + dy + f + min(min(bw, dh), min(0, bw + dh))) + f \\ & = t - (bx + dy + min(min(bw, dh), min(0, bw + dh))) \end{aligned}$$
이를 코드로 나타내면 아래와 같습니다.
interface Point {
x: number;
y: number;
}
function findTransformOrigin(target: HTMLElement): Point {
const [ x, y ] = getComputedStyle(target)
.transformOrigin
.split(" ")
.map(p => -parseFloat(p));
const { a, b, c, d } = new DOMMatrix(getComputedStyle(target).transform);
const { offsetWidth: w, offsetHeight: h } = box;
const { left: l, top: t } = box.getBoundingClientRect();
return {
x: l - (
a * x + c * y + Math.min(
Math.min(a * w, c * h),
Math.min(0, a * w + c * h)
)
),
y: t - (
b * x + d * y + Math.min(
Math.min(b * w, d * h),
Math.min(0, b * w + d * h)
)
)
};
}
읽어주셔서 감사합니다!
묻고 답하기
개인적인 판단에 의해 적절하다고 여겨지는 경우, 모두가 볼 수 있도록 이곳에 문답이 추가됩니다. 그렇지 않더라도 질문에 대한 답변은 별도로 이루어집니다.
Subscribe to my newsletter
Read articles from 고라니드로 directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
