Як поєднувати rotateX(50deg) rotateY(20deg) rotateZ(15deg)
в стенографії rotate3d()
?
Як поєднувати rotateX(50deg) rotateY(20deg) rotateZ(15deg)
в стенографії rotate3d()
?
Відповіді:
rotateX(50deg)
еквівалентно rotate3d(1, 0, 0, 50deg)
rotateY(20deg)
еквівалентно rotate3d(0, 1, 0, 20deg)
rotateZ(15deg)
еквівалентно rotate3d(0, 0, 1, 15deg)
Так...
rotateX(50deg) rotateY(20deg) rotateZ(15deg)
еквівалентно
rotate3d(1, 0, 0, 50deg) rotate3d(0, 1, 0, 20deg) rotate3d(0, 0, 1, 15deg)
Для загального у rotate3d(x, y, z, α)
вас є матриця
де
Тепер ви отримуєте матриці для кожного з 3 rotate3d
перетворень і множите їх. І результуюча матриця - це матриця, відповідна отриманому одиничному rotate3d
. Не впевнений, як легко це витягнути значення для rotate3d
цього, але це просто витягнути значення для одного matrix3d
.
У першому випадку ( rotateX(50deg)
або rotate3d(1, 0, 0, 50deg)
) у вас є:
x = 1
, y = 0
, z = 0
,α = 50deg
Отже, перший рядок матриці в цьому випадку є 1 0 0 0
.
Другий - це 0 cos(50deg) -sin(50deg) 0
.
Третій 0 sin(50deg) cos(50deg) 0
.
І четвертий - очевидно 0 0 0 1
.
У другому випадку, у вас є x = 0
, y = 1
, z = 0
, α = 20deg
.
Перший ряд: cos(20deg) 0 sin(20deg) 0
.
Другий ряд: 0 1 0 0
.
Третій ряд: -sin(20) 0 cos(20deg) 0
.
Четверте: 0 0 0 1
У третьому випадку, у вас є x = 0
, y = 0
, z = 1
, α = 15deg
.
Перший ряд: cos(15deg) -sin(15deg) 0 0
.
Другий ряд sin(15deg) cos(15deg) 0 0
.
І третій і четвертий ряд є 0 0 1 0
і 0 0 0 1
відповідно.
Примітка : Ви могли помітити, що ознаки значень sin для перетворення rotateY відрізняються, ніж для інших двох перетворень. Це не обчислювальна помилка. Причиною цього є те, що для екрана вісь y спрямована вниз, а не вгору.
Отже, це три 4x4
матриці, які вам потрібно помножити, щоб отримати 4x4
матрицю для отриманого одиночного rotate3d
перетворення. Як я вже говорив, я не впевнений, наскільки легко можна отримати 4 значення, але 16 елементів у матриці 4x4 - це точно 16 параметрів, matrix3d
еквівалентних ланцюгового перетворення.
РЕДАГУВАТИ :
Насправді, виявляється, це досить просто ... Ви обчислюєте трасування (суму діагональних елементів) матриці для rotate3d
матриці.
4 - 2*2*(1 - cos(α))/2 = 4 - 2*(1 - cos(α)) = 2 + 2*cos(α)
Потім обчислюється відстеження для добутку трьох 4x4
матриць, результат вирівнюється з 2 + 2*cos(α)
витягом α
. Потім обчислити x
, y
, z
.
У цьому конкретному випадку, якщо я правильно розрахував, слід матриці, що є результатом добутку трьох 4x4
матриць, буде таким:
T =
cos(20deg)*cos(15deg) +
cos(50deg)*cos(15deg) - sin(50deg)*sin(20deg)*cos(15deg) +
cos(50deg)*cos(20deg) +
1
Отже cos(α) = (T - 2)/2 = T/2 - 1
, що означає α = acos(T/2 - 1)
.
[x,y,z]
вектор нормалізований, тобто лише якщо довжина вектора Math.sqrt(x*x + y*y + z*z)
одна. Якщо його не нормувати, його можна легко перетворити на нормований, зануривши кожногоx
, y
та z
за їх довжиною.
Синтаксис:
rotate3d(x, y, z, a)
Цінності:
x
Являє собою <number>
опис координати x вектора, що позначає вісь обертання.y
Являє собою <number>
опис координати y вектора, що позначає вісь обертання.z
Це <number>
опис z-координати вектора, що позначає вісь обертання.a
Являє собою <angle>
кут повороту, що представляє. Позитивний кут означає поворот за годинниковою стрілкою, негативний кут проти годинникової стрілки.Як у:
.will-distort{
transform:rotate3d(10, 10, 10, 45deg);
}
rotate3d
, а не для визначення rotate3d
.
Залежно від того, що ви намагаєтесь зробити, цей "хак" може вам допомогти. Скажімо, ви робите анімацію, і ви хочете додати перетворення після перетворення і так далі, і ви не хочете, щоб CSS виглядав так, ніби він робить 100 перетворень:
Це працює в chrome: 1. Застосуйте будь-яке перетворення, яке ви хочете, до елемента. 2. Наступного разу, коли ви захочете додати перетворення, додайте його до обчислюваного перетворення: "window.getComputedStyle (element) .transform" - але обов’язково поставте нове перетворення ліворуч. 3. Тепер ваше перетворення буде виглядати як "rotateZ (30deg) matrix3d (......). 4. Наступного разу, коли ви захочете додати ще одне перетворення, повторіть процес - Chrome завжди зводить перетворення до позначень matrix3d.
TL; DR - застосуйте будь-які перетворення, які ви хочете, а потім отримайте обчислене перетворення matrix3d.
Цей трюк також дозволяє швидко (тобто, не роблячи жодної математики самостійно) зробити функціонал, який обертає об’єкт відносно вашої системи відліку в будь-якому напрямку. Дивіться зразок нижче:
EDIT : Я також додав переклади xyz. Використовуючи це, було б дуже легко розмістити об’єкти в конкретних 3d місцях з урахуванням конкретної орієнтації. Або ... уявіть собі куб, який підскакує і змінює свою вісь обертання з кожним відскоком залежно від того, як він потрапляє!
var boxContainer = document.querySelector('.translator'),
cube = document.getElementById('cube'),
optionsContainer = document.getElementById('options');
var dims = ['x', 'y', 'z'];
var currentTransform;
var currentTranslate;
var init = function () {
optionsContainer.querySelector('.xRotation input')
.addEventListener('input', function (event) {
if (currentTransform != 'none') {
var newTransform = 'rotateX(' + (360 - event.target.value) + 'deg) ' + currentTransform;
} else {
var newTransform = 'rotateX(' + (360 - event.target.value) + 'deg)';
}
cube.style.transform = newTransform;
}, false);
optionsContainer.querySelector('.yRotation input')
.addEventListener('input', function (event) {
if (currentTransform != 'none') {
var newTransform = 'rotateY(' + (360 - event.target.value) + 'deg) ' + currentTransform;
} else {
var newTransform = 'rotateY(' + (360 - event.target.value) + 'deg)';
}
cube.style.transform = newTransform;
}, false);
optionsContainer.querySelector('.zRotation input')
.addEventListener('input', function (event) {
if (currentTransform != 'none') {
var newTransform = 'rotateZ(' + (360 - event.target.value) + 'deg) ' + currentTransform;
} else {
var newTransform = 'rotateZ(' + (360 - event.target.value) + 'deg)';
}
cube.style.transform = newTransform;
}, false);
optionsContainer.querySelector('.xTranslation input')
.addEventListener('input', function (event) {
if (currentTranslate != 'none') {
var newTransform = 'translateX(' + (100 - event.target.value) + 'px) ' + currentTranslate;
} else {
var newTransform = 'translateX(' + (100 - event.target.value) + 'px)';
}
boxContainer.style.transform = newTransform;
}, false);
optionsContainer.querySelector('.yTranslation input')
.addEventListener('input', function (event) {
if (currentTranslate != 'none') {
var newTransform = 'translateY(' + (100 - event.target.value) + 'px) ' + currentTranslate;
} else {
var newTransform = 'translateY(' + (100 - event.target.value) + 'px)';
}
boxContainer.style.transform = newTransform;
}, false);
optionsContainer.querySelector('.zTranslation input')
.addEventListener('input', function (event) {
if (currentTranslate != 'none') {
var newTransform = 'translateZ(' + (500 - event.target.value) + 'px) ' + currentTranslate;
} else {
var newTransform = 'translateZ(' + (500 - event.target.value) + 'px)';
}
boxContainer.style.transform = newTransform;
}, false);
reset();
};
function reset() {
currentTransform = window.getComputedStyle(cube).transform;
currentTranslate = window.getComputedStyle(boxContainer).transform;
optionsContainer.querySelector('.xRotation input').value = 360;
optionsContainer.querySelector('.yRotation input').value = 360;
optionsContainer.querySelector('.zRotation input').value = 360;
optionsContainer.querySelector('.xTranslation input').value = 100;
optionsContainer.querySelector('.yTranslation input').value = 100;
optionsContainer.querySelector('.zTranslation input').value = 500;
}
window.addEventListener('DOMContentLoaded', init, false);
document.addEventListener('mouseup', reset, false);
.translator
{
height: 200px;
position: absolute;
width: 200px;
transform-style: preserve-3d;
}
.threeSpace
{
height: 200px;
moz-perspective: 1200px;
o-perspective: 1200px;
perspective: 200px;
position: absolute;
transform-origin: 50px 50px 100px;
webkit-perspective: 1200px;
width: 100px;
perspective-origin: 100px 25px;
transform-style: preserve-3d;
}
#pointer{
position:relative;
height:2px;
width:2px;
top:25px;
left:100px;
background:blue;
z-index:9999;
}
#cube
{
height: 100%;
moz-transform-origin: 90px 110px 0px;
moz-transform-style: preserve-3d;
o-transform-origin: 90px 110px 0px;
o-transform-style: preserve-3d;
position: absolute;
transform-origin: 90px 110px 0px;
transform-style: preserve-3d;
webkit-transform-origin: 90px 110px 0px;
webkit-transform-style: preserve-3d;
width: 100%;
}
#cube .midPoint{
position:absolute;
top:48px;
left:48px;
height:1px;
width:1px;
background:green;
}
#cube figure
{
border: 2px solid black;
color: white;
display: block;
font-size: 60px;
font-weight: bold;
height: 96px;
line-height: 96px;
position: absolute;
text-align: center;
width: 96px;
/* transform-style: preserve-3d; */
}
#cube .front
{
background: hsl(0, 100%, 50%);
}
#cube .back
{
background: hsl(60, 100%, 50%);
}
#cube .right
{
background: hsl(120, 100%, 50%);
}
#cube .left
{
background: hsl(180, 100%, 50%);
}
#cube .top
{
background: hsl(240, 100%, 50%);
}
#cube .bottom
{
background: hsl(300, 100%, 50%);
}
#cube .front
{
moz-transform: translateZ(50px);
o-transform: translateZ(50px);
transform: translateZ(50px);
webkit-transform: translateZ(50px);
}
#cube .back
{
moz-transform: rotateX(-180deg) translateZ(50px);
o-transform: rotateX(-180deg) translateZ(50px);
transform: rotateX(-180deg) translateZ(50px);
webkit-transform: rotateX(-180deg) translateZ(50px);
}
#cube .right
{
moz-transform: rotateY(90deg) translateZ(50px);
o-transform: rotateY(90deg) translateZ(50px);
transform: rotateY(90deg) translateZ(50px);
webkit-transform: rotateY(90deg) translateZ(50px);
}
#cube .left
{
moz-transform: rotateY(-90deg) translateZ(50px);
o-transform: rotateY(-90deg) translateZ(50px);
transform: rotateY(-90deg) translateZ(50px);
webkit-transform: rotateY(-90deg) translateZ(50px);
}
#cube .top
{
moz-transform: rotateX(90deg) translateZ(50px);
o-transform: rotateX(90deg) translateZ(50px);
transform: rotateX(90deg) translateZ(50px);
webkit-transform: rotateX(90deg) translateZ(50px);
}
#cube .bottom
{
moz-transform: rotateX(-90deg) translateZ(50px);
o-transform: rotateX(-90deg) translateZ(50px);
transform: rotateX(-90deg) translateZ(50px);
webkit-transform: rotateX(-90deg) translateZ(50px);
}
#options{
position:absolute;
width:80%;
top:40%;
}
#options input
{
width: 60%;
}
<body>
<div class="threeSpace">
<div id="pointer"></div>
<div class="translator">
<div id="cube">
<figure class="front"><div class='midPoint'></div></figure>
<figure class="back"></figure>
<figure class="right"></figure>
<figure class="left"></figure>
<figure class="top"></figure>
<figure class="bottom"></figure>
</div>
</div>
</div>
<section id="options">
<p class="xRotation">
<label>xRotation</label>
<input type="range" min="0" max="720" value="360" data-units="deg" />
</p>
<p class="yRotation">
<label>yRotation</label>
<input type="range" min="0" max="720" value="360" data-units="deg" />
</p>
<p class="zRotation">
<label>zRotation</label>
<input type="range" min="0" max="720" value="360" data-units="deg" />
</p>
<p class="xTranslation">
<label>xTranslation</label>
<input type="range" min="0" max="200" value="100" data-units="deg" />
</p>
<p class="yTranslation">
<label>yTranslation</label>
<input type="range" min="0" max="200" value="100" data-units="deg" />
</p>
<p class="zTranslation">
<label>zTranslation</label>
<input type="range" min="0" max="1000" value="500" data-units="deg" />
</p>
</section>
</body>