I made CSS cube that I'm rotating using up/down and left/right keys but I'm having problems regarding rotation direction.
Attempt #1
DEMO
Using this article I managed to bind keys and apply rotation to the cube. My first problem was that CSS transform
function rotates elements axes so when, ie. I press up, Y and Z axis change place. I adjusted original code for that case, but another problem is, since axes are vectors, when I press up 2 times X and Z are back in place but vectors are inverted (left key starts rotating cube to the right and vice-versa), so now I must rotate cube in opposite direction to get desired result and I don't have idea how to detect wheather axis are inverted.
JavaScript
var xAngle = 0,
yAngle = 0,
zAngle = 0,
cube = $("#cube");
$(document).keydown(function(e) { //keyup maybe better?
e.preventDefault();
var key = e.which,
arrow = {left: 37, up: 38, right: 39, down: 40},
x = xAngle/90,
y = yAngle/90;
switch(key) {
case arrow.left:
if (x%2 == 0)
yAngle -= 90;
else
zAngle += 90;
break;
case arrow.up:
if (y%2 == 0)
xAngle += 90;
else
zAngle -= 90;
break;
case arrow.right:
if (x%2 == 0)
yAngle += 90;
else
zAngle -=90;
break;
case arrow.down:
if (y%2 == 0)
xAngle -= 90;
else
zAngle += 90;
break;
}
var rotate = "rotateX(" + xAngle + "deg) rotateY(" + yAngle + "deg) rotateZ(" + zAngle + "deg)";
cube.css({"transform":rotate});
});
Attempt #2
DEMO
I made another version using methods from this article which tries to solve same problem by decomposing and then updating css 3d matrix but it has other problems. After repeatedly pressing arrows in random directions cube changes viewing angle (more than one side is visible at once).
It would be great when I could get back rotated values or vector direction from 3d matrix but none of solutions I found seems to work. I'm guessing because 3d matrix is derived by multipling values from all functions that are passed (rotateX, rotateY and translateZ) and that kind of math is way over my head to figure out.
JavaScript
var Vector = function(x, y, z) {
this.x = x;
this.y = y;
this.z = z;
}
WebKitCSSMatrix.prototype.transformVector = function(v) {
var xOut = this.m11*v.x + this.m12*v.y + this.m13*v.z;
var yOut = this.m21*v.x + this.m22*v.y + this.m23*v.z;
var zOut = this.m31*v.x + this.m32*v.y + this.m33*v.z;
return new Vector(xOut, yOut, zOut);
};
function applyRotation(vector, angle) {
var cube = $('#cube');
var matrix = new WebKitCSSMatrix(cube.css('webkitTransform'));
var vector = matrix.transformVector(vector);
var newMatrix = matrix.rotateAxisAngle(vector.x, vector.y, vector.z, angle);
cube.get(0).style.webkitTransform = newMatrix;
}
// rotate using arrow keys
$(document).keyup(function(e) {
e.preventDefault();
var key = e.which,
arrow = {left: 37, up: 38, right: 39, down: 40},
v,
a;
switch(key) {
case arrow.left:
v = new Vector(0,1,0),
a = -90;
break;
case arrow.right:
v = new Vector(0,1,0),
a = 90;
break;
case arrow.up:
v = new Vector(1,0,0),
a = 90;
break;
case arrow.down:
v = new Vector(1,0,0),
a = -90;
break;
}
applyRotation(v, a);
});
Attempt #3
DEMO
Third version I made rotates each side seperately and changes classes after rotation so I always just rotate X and Y in right direction, but while rotation is happening cube gets decomposed and I think up and down rotation is wrong (plus code is kinda bloated and ugly). Only plus side of this approach is bigger cross-browser compatibility for browsers that don't support preserve-3d
property.
JavaScript
$(document).keyup(function(e) {
e.preventDefault();
var key = e.which,
arrow = {left: 37, up: 38, right: 39, down: 40},
front = "rotateX(0deg) translateZ(100px)",
back = "rotateX(180deg) translateZ(100px)",
right = "rotateY(90deg) translateZ(100px)",
left = "rotateY(-90deg) translateZ(100px)",
top = "rotateX(90deg) translateZ(100px)",
bottom = "rotateX(-90deg) translateZ(100px)";
switch(key) {
case arrow.left:
$(".front").css({"transform":left});
$(".back").css({"transform":right});
$(".left").css({"transform":back});
$(".right").css({"transform":front});
var front = $(".front");
var back = $(".back");
var left = $(".left");
var right = $(".right");
front.removeClass("front").addClass("left");
back.removeClass("back").addClass("right");
right.removeClass("right").addClass("front");
left.removeClass("left").addClass("back");
break;
case arrow.up:
$(".front").css({"transform":top});
$(".back").css({"transform":bottom});
$(".top").css({"transform":back});
$(".bottom").css({"transform":front});
var front = $(".front");
var back = $(".back");
var top = $(".top");
var bottom = $(".bottom");
front.removeClass("front").addClass("top");
back.removeClass("back").addClass("bottom");
top.removeClass("top").addClass("back");
bottom.removeClass("bottom").addClass("front");
break;
case arrow.right:
$(".front").css({"transform":right});
$(".back").css({"transform":left});
$(".left").css({"transform":front});
$(".right").css({"transform":back});
var front = $(".front");
var back = $(".back");
var left = $(".left");
var right = $(".right");
front.removeClass("front").addClass("right");
back.removeClass("back").addClass("left");
right.removeClass("right").addClass("back");
left.removeClass("left").addClass("front");
break;
case arrow.down:
$(".front").css({"transform":bottom});
$(".back").css({"transform":top});
$(".top").css({"transform":front});
$(".bottom").css({"transform":back});
var front = $(".front");
var back = $(".back");
var top = $(".top");
var bottom = $(".bottom");
front.removeClass("front").addClass("bottom");
back.removeClass("back").addClass("top");
top.removeClass("top").addClass("front");
bottom.removeClass("bottom").addClass("back");
break;
}
});
REFERENCE MATERIAL:
- Creating basic CSS cube
- MDN docs on
transform
property - W3.org documentation on 3D transforms
3dmatrix
documentationwebkitCSSMatrix
documentation