[ JS ] rotate3d 값 얻기
JS를 통해 요소의 rotate3d 프로퍼티를 얻기 위해서 만든 클래스입니다.
1. Constructor
constructor에서는 transform property를 얻고자 하는 요소를 parameter로 두었습니다. 따라서 클래스 성성시 요소를 받아 요소의 matrix3d 데이터를 저장합니다.
class Transform {
constructor(ele) {
this.ele = ele;
this.matrix3d = window
.getComputedStyle(ele)
.getPropertyValue("transform") // column priority
.split("(")[1]
.split(")")[0]
.split(",")
.map((ele) => Number(ele));
}
}
2. Method
1) show()
요소의 matrix3d 데이터를 행렬꼴로 콘솔창에 출력합니다.
show() {
let temp = this.matrix3d.slice().map((ele) => Number(ele.toFixed(2)));
if (temp.length === 6) {
console.log(
`
${temp[0]} / ${temp[2]} / ${temp[4]}
${temp[1]} / ${temp[3]} / ${temp[5]}
0 / 0 / 1
`
);
} else if (temp.length === 16) {
console.log(
`
${temp[0]} / ${temp[4]} / ${temp[8]} / ${temp[12]}
${temp[1]} / ${temp[5]} / ${temp[9]} / ${temp[13]}
${temp[2]} / ${temp[6]} / ${temp[10]} / ${temp[14]}
${temp[3]} / ${temp[7]} / ${temp[11]} / ${temp[15]}
`
);
}
return;
}
2) showAllFactors()
메서드에서는 각 변환 인자를 콘솔창에 출력합니다.
showAllFactors() {
let t = this.getTranslateFactors();
let s = this.getScaleFactors();
let r = this.getRotateFactors();
console.log("\n--------------translation--------------\n");
Object.keys(t).forEach((ele) => console.log(`${ele} : ${t[ele]}`));
console.log("\n---------------scaling-----------------\n");
Object.keys(s).forEach((ele) => console.log(`${ele} : ${s[ele]}`));
console.log("\n---------------rotation----------------\n");
Object.keys(r).forEach((ele) => console.log(`${ele} : ${r[ele]}`));
}
3) getTranslateFactors
tx, ty, tz의 키값을 가지는 객체로 translation에 사용된 인자를 반환합니다. 각 key에 대응하는 value의 자료형은 숫자입니다.
getTranslateFactors() {
if (this.matrix3d.length === 6)
return {
tx: this.matrix3d[4].toFixed(2),
ty: this.matrix3d[5].toFixed(2),
};
if (this.matrix3d.length === 16)
return {
tx: this.matrix3d[12].toFixed(2),
ty: this.matrix3d[13].toFixed(2),
tz: this.matrix3d[14].toFixed(2),
};
}
4) getScaleFactors
sx, sy, sz의 키값을 가지는 객체로 scalling에 사용된 인자를 반환합니다. 각 key에 대응하는 value의 자료형은 숫자입니다.
getScaleFactors() {
if (this.matrix3d.length === 6)
return {
sx: Math.pow(
this.matrix3d[0] ** 2 + this.matrix3d[2] ** 2,
0.5
).toFixed(2),
sy: Math.pow(
this.matrix3d[1] ** 2 + this.matrix3d[3] ** 2,
0.5
).toFixed(2),
};
if (this.matrix3d.length === 16)
return {
sx: Math.pow(
this.matrix3d[0] ** 2 + this.matrix3d[4] ** 2 + this.matrix3d[8] ** 2,
0.5
).toFixed(2),
sy: Math.pow(
this.matrix3d[1] ** 2 + this.matrix3d[5] ** 2 + this.matrix3d[9] ** 2,
0.5
).toFixed(2),
sz: Math.pow(
this.matrix3d[2] ** 2 +
this.matrix3d[6] ** 2 +
this.matrix3d[10] ** 2,
0.5
).toFixed(2),
};
}
(5) getRotateFactors
u, deg를 키값으로 갖는 객체로 rotate 인자를 반환합니다. 앞서 정의된 getScaleFactors()를 이용해 요소의 matrix3d 데이터에서 scaling factor를 제거하며, u와 deg를 구하는 원리는 아래 링크에서 확인 가능합니다.
[Computer Graphics] 3D 변환 행렬과 행렬 분해
1. 3D 변환 행렬의 구성 3D 공간에서의 변환은 보통 4x4 매트릭스를 통해 이루어집니다.(homogenous coordinate라고 표현하며 하나의 행렬을 통해 여러 종류의 연산을 나타낼 수 있다는 장점을 가집니다.)
ojhallae.tistory.com
각 key에 대응하는 value의 자료형은 사용의 편의성을 위해 문자열로 반환합니다.
getRotateFactors() {
let scaleFactors = Object.values(this.getScaleFactors());
let dimension = scaleFactors.length;
let tempMatrix = this.matrix3d.slice();
let deg;
if (dimension === 2) {
[0, 1].forEach((ele) => {
for (let i = 0; i < 2; i++) {
tempMatrix[ele + 2 * i] /= scaleFactors[ele];
}
});
deg = (Math.acos(tempMatrix[0]) * 180) / Math.PI;
return { u: undefined, deg: deg.toFixed(2) + "deg" };
} else if (dimension === 3) {
[0, 1, 2].forEach((ele) => {
for (let i = 0; i < 3; i++) {
tempMatrix[ele + 4 * i] /= scaleFactors[ele];
}
});
let u = [
tempMatrix[6] - tempMatrix[9],
tempMatrix[8] - tempMatrix[2],
tempMatrix[1] - tempMatrix[4],
];
if (u[0].toFixed(2) != 1) {
deg =
(Math.acos((tempMatrix[0] - u[0] ** 2) / (1 - u[0] ** 2)) * 180) /
Math.PI;
} else if (u[1].toFixed(2) != 1) {
deg =
(Math.acos((tempMatrix[5] - u[1] ** 2) / (1 - u[1] ** 2)) * 180) /
Math.PI;
} else if (u[2].toFixed(2) != 1) {
deg =
(Math.acos((tempMatrix[10] - u[2] ** 2) / (1 - u[2] ** 2)) * 180) /
Math.PI;
}
return {
u: u.map((ele) => ele.toFixed(2)).join(","),
deg: deg.toFixed(2) + "deg",
};
}
}
6) setSameTransformTo
클래스 생성시 인자로 전달받은 요소의 trasform property에 적용된 translation, scaling, rotation을 메서드의 인자로 받은 요소에 적용합니다.
setSameTransformTo(ele) {
let t = this.getTranslateFactors();
let s = this.getScaleFactors();
let r = this.getRotateFactors();
console.log(t, s, r);
ele.style.transform = `translate3d(${t["tx"]}px, ${t["ty"]}px, ${
t["tz"]
}px) scale3d(${s["sx"] + "," + s["sy"] + "," + s["sz"]}) rotate3d(${
r["u"]
}, ${r["deg"]})`;
}
3. Full code
class Transform {
constructor(ele) {
this.ele = ele;
this.matrix3d = window
.getComputedStyle(ele)
.getPropertyValue("transform") // column priority
.split("(")[1]
.split(")")[0]
.split(",")
.map((ele) => Number(ele));
}
show() {
let temp = this.matrix3d.slice().map((ele) => Number(ele.toFixed(2)));
if (temp.length === 6) {
console.log(
`
${temp[0]} / ${temp[2]} / ${temp[4]}
${temp[1]} / ${temp[3]} / ${temp[5]}
0 / 0 / 1
`
);
} else if (temp.length === 16) {
console.log(
`
${temp[0]} / ${temp[4]} / ${temp[8]} / ${temp[12]}
${temp[1]} / ${temp[5]} / ${temp[9]} / ${temp[13]}
${temp[2]} / ${temp[6]} / ${temp[10]} / ${temp[14]}
${temp[3]} / ${temp[7]} / ${temp[11]} / ${temp[15]}
`
);
}
return;
}
showAllFactors() {
let t = this.getTranslateFactors();
let s = this.getScaleFactors();
let r = this.getRotateFactors();
console.log("\n--------------translation--------------\n");
Object.keys(t).forEach((ele) => console.log(`${ele} : ${t[ele]}`));
console.log("\n---------------scaling-----------------\n");
Object.keys(s).forEach((ele) => console.log(`${ele} : ${s[ele]}`));
console.log("\n---------------rotation----------------\n");
Object.keys(r).forEach((ele) => console.log(`${ele} : ${r[ele]}`));
}
getTranslateFactors() {
if (this.matrix3d.length === 6)
return {
tx: this.matrix3d[4].toFixed(2),
ty: this.matrix3d[5].toFixed(2),
};
if (this.matrix3d.length === 16)
return {
tx: this.matrix3d[12].toFixed(2),
ty: this.matrix3d[13].toFixed(2),
tz: this.matrix3d[14].toFixed(2),
};
}
getScaleFactors() {
if (this.matrix3d.length === 6)
return {
sx: Math.pow(
this.matrix3d[0] ** 2 + this.matrix3d[2] ** 2,
0.5
).toFixed(2),
sy: Math.pow(
this.matrix3d[1] ** 2 + this.matrix3d[3] ** 2,
0.5
).toFixed(2),
};
if (this.matrix3d.length === 16)
return {
sx: Math.pow(
this.matrix3d[0] ** 2 + this.matrix3d[4] ** 2 + this.matrix3d[8] ** 2,
0.5
).toFixed(2),
sy: Math.pow(
this.matrix3d[1] ** 2 + this.matrix3d[5] ** 2 + this.matrix3d[9] ** 2,
0.5
).toFixed(2),
sz: Math.pow(
this.matrix3d[2] ** 2 +
this.matrix3d[6] ** 2 +
this.matrix3d[10] ** 2,
0.5
).toFixed(2),
};
}
getRotateFactors() {
let scaleFactors = Object.values(this.getScaleFactors());
let dimension = scaleFactors.length;
let tempMatrix = this.matrix3d.slice();
let deg;
if (dimension === 2) {
[0, 1].forEach((ele) => {
for (let i = 0; i < 2; i++) {
tempMatrix[ele + 2 * i] /= scaleFactors[ele];
}
});
deg = (Math.acos(tempMatrix[0]) * 180) / Math.PI;
return { u: undefined, deg: deg.toFixed(2) + "deg" };
} else if (dimension === 3) {
[0, 1, 2].forEach((ele) => {
for (let i = 0; i < 3; i++) {
tempMatrix[ele + 4 * i] /= scaleFactors[ele];
}
});
let u = [
tempMatrix[6] - tempMatrix[9],
tempMatrix[8] - tempMatrix[2],
tempMatrix[1] - tempMatrix[4],
];
if (u[0].toFixed(2) != 1) {
deg =
(Math.acos((tempMatrix[0] - u[0] ** 2) / (1 - u[0] ** 2)) * 180) /
Math.PI;
} else if (u[1].toFixed(2) != 1) {
deg =
(Math.acos((tempMatrix[5] - u[1] ** 2) / (1 - u[1] ** 2)) * 180) /
Math.PI;
} else if (u[2].toFixed(2) != 1) {
deg =
(Math.acos((tempMatrix[10] - u[2] ** 2) / (1 - u[2] ** 2)) * 180) /
Math.PI;
}
return {
u: u.map((ele) => ele.toFixed(2)).join(","),
deg: deg.toFixed(2) + "deg",
};
}
}
setSameTransformTo(ele) {
let t = this.getTranslateFactors();
let s = this.getScaleFactors();
let r = this.getRotateFactors();
console.log(t, s, r);
ele.style.transform = `translate3d(${t["tx"]}px, ${t["ty"]}px, ${
t["tz"]
}px) scale3d(${s["sx"] + "," + s["sy"] + "," + s["sz"]}) rotate3d(${
r["u"]
}, ${r["deg"]})`;
}
}