Jak łączyć rotateX(50deg) rotateY(20deg) rotateZ(15deg)
w skrócie rotate3d()
?
Jak łączyć rotateX(50deg) rotateY(20deg) rotateZ(15deg)
w skrócie rotate3d()
?
Odpowiedzi:
rotateX(50deg)
jest równa rotate3d(1, 0, 0, 50deg)
rotateY(20deg)
jest równa rotate3d(0, 1, 0, 20deg)
rotateZ(15deg)
jest równa rotate3d(0, 0, 1, 15deg)
Więc...
rotateX(50deg) rotateY(20deg) rotateZ(15deg)
jest równa
rotate3d(1, 0, 0, 50deg) rotate3d(0, 1, 0, 20deg) rotate3d(0, 0, 1, 15deg)
W przypadku generycznego rotate3d(x, y, z, α)
masz macierz
gdzie
Otrzymujesz teraz macierze dla każdej z 3 rotate3d
transformacji i mnożysz je. A wynikowa macierz jest macierzą odpowiadającą wynikowej pojedynczej rotate3d
. Nie jestem pewien, jak łatwo jest wyodrębnić z niego wartości rotate3d
, ale z pewnością łatwo jest wyodrębnić te dla pojedynczego matrix3d
.
W pierwszym przypadku ( rotateX(50deg)
lub rotate3d(1, 0, 0, 50deg)
) masz:
x = 1
, y = 0
,z = 0
,α = 50deg
Czyli pierwszy wiersz macierzy w tym przypadku to 1 0 0 0
.
Drugi to 0 cos(50deg) -sin(50deg) 0
.
Trzeci 0 sin(50deg) cos(50deg) 0
.
I oczywiście czwarty 0 0 0 1
.
W drugim przypadku, masz x = 0
, y = 1
, z = 0
,α = 20deg
.
Pierwszy rząd: cos(20deg) 0 sin(20deg) 0
.
Drugi rząd: 0 1 0 0
.
Trzeci rząd: -sin(20) 0 cos(20deg) 0
.
Czwarty: 0 0 0 1
W trzecim przypadku, masz x = 0
, y = 0
, z = 1
,α = 15deg
.
Pierwszy rząd: cos(15deg) -sin(15deg) 0 0
.
Drugi rząd sin(15deg) cos(15deg) 0 0
.
A trzeci i czwarty rząd to odpowiednio 0 0 1 0
i 0 0 0 1
.
Uwaga : być może zauważyłeś, że znaki wartości grzechu dla transformaty rotateY są inne niż dla pozostałych dwóch transformacji. To nie jest błąd w obliczeniach. Powodem tego jest to, że na ekranie oś Y jest skierowana w dół, a nie w górę.
Więc to są trzy 4x4
macierze, które musisz pomnożyć, aby uzyskać 4x4
macierz dla wynikowej pojedynczej rotate3d
transformacji. Jak powiedziałem, nie jestem pewien, jak łatwo można uzyskać 4 wartości, ale 16 elementów w macierzy 4x4 to dokładnie 16 parametrów matrix3d
odpowiednika połączonej transformacji.
EDYCJA :
Właściwie okazuje się, że to całkiem proste ... Obliczasz ślad (sumę elementów przekątnych) macierzy dla rotate3d
macierzy.
4 - 2*2*(1 - cos(α))/2 = 4 - 2*(1 - cos(α)) = 2 + 2*cos(α)
Następnie obliczasz ślad iloczynu trzech 4x4
macierzy, zrównujesz wynik z 2 + 2*cos(α)
wyodrębnieniem α
. Następnie należy obliczyć x
, y
, z
.
W tym konkretnym przypadku, jeśli obliczyłem poprawnie, ślad macierzy wynikający z iloczynu trzech 4x4
macierzy będzie wyglądał następująco:
T =
cos(20deg)*cos(15deg) +
cos(50deg)*cos(15deg) - sin(50deg)*sin(20deg)*cos(15deg) +
cos(50deg)*cos(20deg) +
1
Więc cos(α) = (T - 2)/2 = T/2 - 1
to znaczy, że α = acos(T/2 - 1)
.
[x,y,z]
wektor jest znormalizowany, to znaczy tylko wtedy, gdy długość wektora Math.sqrt(x*x + y*y + z*z)
wynosi jeden. Jeżeli nie jest to znormalizowane, można łatwo przekształcić w znormalizowanej drugim, nurkowanie każdego x
, y
oraz z
ich długość.
Składnia:
rotate3d(x, y, z, a)
Wartości:
x
To <number>
opis współrzędnej x wektora oznaczającego oś obrotu.y
To <number>
opis współrzędnej y wektora oznaczającego oś obrotu.z
Jest to <number>
opis współrzędnej z wektora oznaczającego oś obrotu.a
Jest <angle>
reprezentowanie kąta obrotu. Kąt dodatni oznacza obrót w prawo, a kąt ujemny w kierunku przeciwnym do ruchu wskazówek zegara.Jak w :
.will-distort{
transform:rotate3d(10, 10, 10, 45deg);
}
rotate3d
, a nie do definicji rotate3d
.
W zależności od tego, co próbujesz zrobić, ten „hack” może ci pomóc. Załóżmy, że robisz animację i chcesz dodać transformację po transformacji i tak dalej, i nie chcesz, aby CSS wyglądał tak, jakby wykonywał setki transformacji:
Działa to w chrome: 1. Zastosuj dowolną transformację do elementu. 2. Następnym razem, gdy będziesz chciał dodać transformację, dodaj ją do obliczonej transformacji: „window.getComputedStyle (element) .transform” - ale pamiętaj, aby umieścić nową transformację po lewej stronie. 3. Teraz twoja transformacja będzie wyglądać jak "rotateZ (30deg) matrix3d (......). 4. Następnym razem, gdy chcesz dodać kolejną transformację, powtórz proces - Chrome zawsze redukuje transformacje do notacji matrix3d.
TL; DR - zastosuj dowolne transformacje, a następnie uzyskaj obliczoną transformację matrix3d.
Ta sztuczka pozwala również szybko (to znaczy bez wykonywania żadnych działań matematycznych samodzielnie) wykonać funkcję, która obraca obiekt w dowolnym kierunku względem ramki odniesienia. Zobacz przykład poniżej:
EDYCJA : Dodałem również tłumaczenia xyz. Korzystając z tego, bardzo łatwo byłoby umieszczać obiekty w określonych lokalizacjach 3D, mając na uwadze określone orientacje. Albo ... wyobraź sobie sześcian, który odbija się i zmienia swoją oś obrotu przy każdym odbiciu w zależności od tego, jak wyląduje!
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>