CSS3 Transform Matrix3D Animation Glitching, Why?

893 views Asked by At

I've got it running in this fiddle. I'm trying to make two faces of a cube rotate, however the jump in front of each other, when they pass each other on the z-axis. All I want is to learn the basics of a Matrix 3D transform in the browser.

I should add, I know that it's the z-axis crossing over, but the question is, can a DOM object not spread it's along the z-azis? It seems it can only have one z-position for the whole object

I may have matrix calculations wrong, I'm certainly a 3D graphics noob, but I'm assuming things cannot be too far off, as the behaviour is pretty much as expected, aside from the obvious issue.

Bonus Points:

My z-axis rotation matrix is not behaving as I expected. I'm not sure why. The X and Y work, if you can also help with this, that'd be excellent.

The Code:

HTML:

<div id="world" style="width: 400px, height: 400px">
    <div class="face" id="test" style="background-color: red;"></div>
    <div class="face" id="test2" style="background-color: pink;"></div>    
</div>

CSS:

#world {
    perspective: 100;
    perspective-origin:200px 200px;
    transform-style: preserve-3d;
}

.face{
    position: absolute;
    opacity: 0.9;
    top: 150px;
    left: 150px;
    width: 100px;
    height: 100px;
}

JS:

DEG_TO_RAD = 0.017453292519943295;

function MatrixEl (el) {
    this.el = el;
    this.matrix = this.getMatrix();
    this.x = 0;
    this.y = 0;
    this.z = 0;
    this.xrot = 0;
    this.yrot = 0;
    this.zrot = 0;
    this.current_scale = 1;
}

MatrixEl.prototype.getMatrix = function () {
    return [
    [1,0,0,0],
    [0,1,0,0],
    [0,0,1,0],
    [0,0,0,1],
    ];
};
        
MatrixEl.prototype.scale = function (scale) {
    this.current_scale = scale;
    this.matrix[0][0] *= scale;
    this.matrix[1][1] *= scale;
    this.matrix[2][2] *= scale;
};

MatrixEl.prototype.translate = function (x, y, z) {
    m = this.matrix;
    this.x = x;
    this.y = y;
    this.z = z;
    m[3][0] = this.x;
    m[3][1] = this.y;
    m[3][2] = this.z;
}

MatrixEl.prototype.rotate = function (x, y, z) {
    var m = this.matrix;
    this.xrot = x;
    this.yrot = y;
    this.zrot = z;
    var amount = this.xrot * DEG_TO_RAD;
    m[1][1] *= Math.cos(amount);
    m[1][2] *= Math.sin(-amount);
    m[2][1] *= Math.sin(amount);
    m[2][2] *= Math.cos(amount);    
    
    var amount = this.yrot * DEG_TO_RAD;
    m[0][0] *= Math.cos(amount);
    m[0][2] *= Math.sin(amount);
    m[2][0] *= Math.sin(-amount);
    m[2][2] *= Math.cos(amount);    

    var amount = this.zrot * DEG_TO_RAD;
    m[0][0] *= Math.cos(amount);
    m[0][1] *= Math.sin(-amount);
    m[1][0] *= Math.sin(amount);
    m[1][1] *= Math.cos(amount);
};

MatrixEl.prototype.set = function (x, y, z, xrot, yrot, zrot, scale) {
    var m = this.matrix = this.getMatrix();
    this.rotate(xrot, yrot, zrot);
    this.scale(scale);
    this.translate(x, y, z);
    this.el.style.transform="matrix3d(" + m[0].concat(
            m[1],
            m[2],
            m[3]
        ).join(',') + ')';
};
var yRotVel = 45 / 1000;
var dist = 50;
var time = 0
var square = new MatrixEl(document.getElementById('test'));
var square2 = new MatrixEl(document.getElementById('test2'));

function animate (dt) {
    var angle = dt*yRotVel;
    var z = dist*Math.sin(dt*yRotVel*DEG_TO_RAD);
    var z2 = dist*Math.sin((dt*yRotVel+ 90)*DEG_TO_RAD);
    
    // square.set(x, y, z, xrot, yrot, zrot, scale)
    square.set(0, z, z, angle, 0, 0, 1.0);
    square2.set(0, z2, z2, angle+90, 0, 0, 1);
    requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
1

There are 1 answers

0
SamMorrowDrums On BEST ANSWER

The fix is in this fiddle, my issue was that the browser can't easily do the animations on the individual items.

To fix this I simply constructed a cube, and then animated the whole cube, rather than animating the individual faces and it worked.

The matrix / vector maths needs to be done properly, as if you combine rotations currently it skews objects in ways it shouldn't. I'll try and fix that part of the code soon, but in answer to my original question, you can do this without glitches. The glitches were caused by not animating the shape as a whole.

The code below contains the changed approach:

var yRotVel = 45 / 1000;
var dist = 50;
var time = 0
var square = new MatrixEl(document.getElementById('test'));
var square2 = new MatrixEl(document.getElementById('test2'));
var square3 = new MatrixEl(document.getElementById('test3'));
var square4 = new MatrixEl(document.getElementById('test4'));
var square5 = new MatrixEl(document.getElementById('test5'));
var square6 = new MatrixEl(document.getElementById('test6'));
var cube = new MatrixEl(document.getElementById('world'));

square.set(150, 150, 50, 0, 0, 0, 1.0);
square2.set(150, 100, 0, 90, 0, 0, 1);
square3.set(150, 150, -50, 180, 0, 0, 1);
square4.set(150, 200, 0, 270, 0, 0, 1);
square5.set(100, 150, 0, 0, 90, 0, 1);
square6.set(200, 150, 0, 0, 90, 0, 1);

function animate (dt) {
    var angle = dt*yRotVel;
    var z = dist*Math.sin(dt*yRotVel*DEG_TO_RAD);
    // square.set(x, y, z, xrot, yrot, zrot, scale)
    cube.set(0, 0, 0, 0, angle, 0, 1);
    requestAnimationFrame(animate);
}
requestAnimationFrame(animate);