How can I make the side elements spin on the x-axis in this animation?
Tags: javascript,css3,animation,greensock,tweenlite
Problem :
I'm trying to build a rotating cube animation whose sides also rotate on their respective axis. Here's what I have so far:
http://codepen.io/Chevex/pen/pxtwj
The following CSS rules govern the position of the sides before each animation:
.front {
transform: translateZ(100px);
background-color: #0f0;
}
.back {
transform: rotateY(180deg) translateZ(100px);
background-color: #f00;
}
.right {
transform: rotateY(90deg) translateZ(100px);
background-color: #00f;
}
.left {
transform: rotateY(-90deg) translateZ(100px);
background-color: #ff0;
}
.top {
transform: rotateX(90deg) translateZ(100px);
background-color: #0ff;
}
.bottom {
transform: rotateX(-90deg) translateZ(100px);
background-color: #f0f;
}
This works great and positions the elements accordingly. Next I use the GSAP animation library to rotate the entire cube:
TweenMax.to($('.cube'), 7, { rotationY: 360, repeat: -1, ease: Linear.easeNone });
After that I animate the front and back sides so they spin in relation to the Z axis:
TweenMax.to($('.front, .back'), 2, { rotationZ: 360, repeat: -1, ease: Linear.easeNone });
Next the top and bottom on the Y axis:
TweenMax.to($('.top, .bottom'), 2, { rotationY: 360, repeat: -1, ease: Linear.easeNone });
So far so good. Now the left and right sides need to rotate on the X axis:
TweenMax.to($('.right, .left'), 2, { rotationX: 360, repeat: -1, ease: Linear.easeNone });
Now there is a problem. For some reason they appear to be rotating on the Z axis. I've tried rotating them on all 3 axis but they never appear to spin like a rolling coin the way the other sides did just fine. I thought it was a GSAP bug but I applied the following CSS animations to those sides manually and the same issue exists.
.right {
transform: rotateY(90deg) translateZ(100px);
background-color: #00f;
-webkit-animation: spinRight 2s linear infinite;
}
.left {
transform: rotateY(-90deg) translateZ(100px);
background-color: #ff0;
-webkit-animation: spinLeft 2s linear infinite;
}
@-webkit-keyframes spinRight {
from {
transform: rotateY(90deg) translateZ(100px) rotateX(0deg);
}
to {
transform: rotateY(90deg) translateZ(100px) rotateX(360deg);
}
}
@-webkit-keyframes spinLeft {
from {
transform: rotateY(-90deg) translateZ(100px) rotateX(0deg);
}
to {
transform: rotateY(-90deg) translateZ(100px) rotateX(360deg);
}
}
Any ideas why this is? How can I get the left and right sides of the cube to rotate the way all the others are rotating?
Solution :
Yes, you are gimbal locked, the rotateX value needs to be placed before the rotateY. Order is important for transforms.
What happens in your case is that the 90 degree (positive or negative) Y rotations rotate the X-axis into the Z (or -Z-axis). Usually when you have all 3, ordering X, Y, and Z will keep things straight for X, but Z will gimbal lock when Y is +/- 90 or 270. By putting X after the Y, you are rotating it into the Z axis (in that ordering it is 'inside' the Y-axis).
Try this:
@-webkit-keyframes spinRight {
from {
transform: rotateX(0deg) rotateY(90deg) translateZ(100px);
}
to {
transform: rotateX(360deg) rotateY(90deg) translateZ(100px);
}
}
@-webkit-keyframes spinLeft {
from {
transform: rotateX(0deg) rotateY(-90deg) translateZ(100px);
}
to {
transform: rotateX(360deg) rotateY(-90deg) translateZ(100px);
}
}
If this does not do the trick, try nesting a div and rotating that div's Z-axis. CSS has a limited 3d ability as gimbal lock is a huge problem. Also, the rotate3d is limited due to the 360 degree periods of sine and cosine waves (has to do with the maths determining the matrix). For matrix maths Sylvester works, or you can check out three.js which has its own features to deal with said limitations (I believe Google used three for its Rubik's cube doodle).
Edit from question author:
This answer was correct. The Greensock folks wanted me to nest my side elements in parent elements and modify the axis of the parent. That may have worked but my initial attempts were just causing other issues. After reading up a bit on Gimbal Lock I realized that it's the way the axis are being modified that causes the locking. I had an idea to get to the same position, but by manipulating the axis in a different way. It worked and this is the result:
http://codepen.io/Chevex/pen/pxtwj
Instead of:
TweenMax.set('.right', { rotationY: 90, x: '100px' });
TweenMax.set('.left', { rotationY: -90, x: '100px' });
I sat down and followed the axis in my head and came up with a new path to get the element to the same place.
TweenMax.set('.right', { rotationZ: 90, rotationY: 90, rotationX: 90, x: '100px' });
TweenMax.set('.left', { rotationZ: -90, rotationY: -90, rotationX: 90, x: '100px' });
After that I was able to animate the Y axis and get the intended result now that the Y axis is pointing the direction I had originally expected the X axis to point.
TweenMax.to('.right, .left', gearSpeed, { rotationY: 360, repeat: -1, ease: Linear.easeNone });