Get 3D CSS Rotation Value from Matrix3D() with JavaScript

Get 3D CSS rotation value from matrix3d() with JavaScript

Ok, got it!

var _getTransform = function($element) {

var matrix = $element.css('transform'),
rotateX = 0,
rotateY = 0,
rotateZ = 0;

if (matrix !== 'none') {

// do some magic
var values = matrix.split('(')[1].split(')')[0].split(','),
pi = Math.PI,
sinB = parseFloat(values[8]),
b = Math.round(Math.asin(sinB) * 180 / pi),
cosB = Math.cos(b * pi / 180),
matrixVal10 = parseFloat(values[9]),
a = Math.round(Math.asin(-matrixVal10 / cosB) * 180 / pi),
matrixVal1 = parseFloat(values[0]),
c = Math.round(Math.acos(matrixVal1 / cosB) * 180 / pi);

rotateX = a;
rotateY = b;
rotateZ = c;

}

return {
rotateX: rotateX,
rotateY: rotateY,
rotateZ: rotateZ
};

}

Getting CSS transform rotateX angle from matrix3d

Figured it out myself. You have to use a = values[5] and b = values[4] for rotateX.

get css transform 3D from matrix3d

What you are searching for is called "decomposing a matrix". For a 3d-matrix you have to consider the order of the rotations (e.g. Euler-XYZ, or ZXY). Take a look at:
some Matrix3D code written in TypeScript. Take a look at the decompose() method.

E.g. extract scaling (column major):

var scaleX = Math.sqrt(this.m11 * this.m11 + this.m12 * this.m12 + this.m13 * this.m13);
var scaleY = Math.sqrt(this.m21 * this.m21 + this.m22 * this.m22 + this.m23 * this.m23);
var scaleZ = Math.sqrt(this.m31 * this.m31 + this.m32 * this.m32 + this.m33 * this.m33);

Javascript/Css matrix3d onscroll function shrinks element when rotating

Matrix rotation is not just a linear interpolation of the values.

You already have a correct implementation of single axis rotation, implying modifying both the scale and skew values:

function rotateZ(angle) {
const theta = (Math.PI / 180) * angle;
const matrix = identity();

matrix[0] = matrix[5] = Math.cos(theta).toFixed(6);
matrix[1] = matrix[4] = Math.sin(theta).toFixed(6);
matrix[4] *= -1;

return matrix;
}

So your algebra is all broken there, which will mess up the scale values and will shrink your element.

But you already have all you need, just use it.

const el = document.querySelector('.box');
const updateScroll = () => {
const scrollPos = window.scrollY; const progress = Math.min(scrollPos / 700, 1); // create a new Matrix, correctly translated and rotated const t1 = translateY(scrollPos); const r1 = rotateZ( 360 * progress ); const matrix = multiply(t1, r1); setTransform(el, toString(matrix));}
const init = () => { window.addEventListener('scroll', updateScroll);}
const setTransform = (el, transform) => { el.style.transform = transform; el.style.WebkitTransform = transform;};
/** * REMATRIX Functions* https://github.com/jlmakes/rematrix**/

function translateY(distance) { const matrix = identity(); matrix[13] = distance; return matrix;}
function toString(source) { return `matrix3d(${format(source).join(', ')})`;}
function rotateZ(angle) { const theta = (Math.PI / 180) * angle; const matrix = identity();
matrix[0] = matrix[5] = Math.cos(theta).toFixed(6); matrix[1] = matrix[4] = Math.sin(theta).toFixed(6); matrix[4] *= -1;
return matrix;}
function format(source) { if (source.constructor !== Array) { throw new TypeError('Expected array.'); } if (source.length === 16) { return source; } if (source.length === 6) { const matrix = identity(); matrix[0] = source[0]; matrix[1] = source[1]; matrix[4] = source[2]; matrix[5] = source[3]; matrix[12] = source[4]; matrix[13] = source[5]; return matrix; } throw new RangeError('Expected array with either 6 or 16 values.');}
function identity() { const matrix = []; for (let i = 0; i < 16; i++) { i % 5 == 0 ? matrix.push(1) : matrix.push(0); } return matrix;}
function multiply(m, x) { const fm = format(m); const fx = format(x); const product = [];
for (let i = 0; i < 4; i++) { const row = [fm[i], fm[i + 4], fm[i + 8], fm[i + 12]]; for (let j = 0; j < 4; j++) { const k = j * 4; const col = [fx[k], fx[k + 1], fx[k + 2], fx[k + 3]]; const result = row[0] * col[0] + row[1] * col[1] + row[2] * col[2] + row[3] * col[3];
product[i + k] = result; } }
return product;}
init();
body{  height: 400vh;  min-height: 700px;}
.box{ position:absolute; top:0; left:0; right:0; bottom:0; margin: auto; width: 100px; height: 100px; background: green;}
<div class="box"></div>

Controlling CSS cube rotation(transform) and extracting values from 3d matrix

The problem with the attempt 2 is that rotateAxisAngle does the matrix multiplication in the oposite order of what you want. And, worse still, there is no function in the class to do the multiplication in the order that you want.

As an alternate way, I have choose to use the browser itself to do the math.
I create a div that will be hidden, and where I will apply the transforms to get the new matrix.

With this approach, the javascript gets even shorter:

function applyTransform (transform) {

var cubeCalculator = $('.cubecalculator');
var cube = $('#cube');

var matrix = cubeCalculator.css('webkitTransform');
var composite = transform + ' ' + matrix;
cubeCalculator.get(0).style.webkitTransform = composite;

matrix = cubeCalculator.css('webkitTransform');
cube.get(0).style.webkitTransform = matrix;
}

// rotate using arrow keys
$(document).keyup(function(e) {

e.preventDefault();

var key = e.which,
arrow = {left: 37, up: 38, right: 39, down: 40},
t;

switch(key) {
case arrow.left:
t = 'rotateY(-90deg)';
break;

case arrow.right:
t = 'rotateY(90deg)';
break;

case arrow.up:
t = 'rotateX(90deg)';
break;

case arrow.down:
t = 'rotateX(-90deg)';
break;
}

applyTransform (t);

});

I think that the code is quite self explanatory: I apply the transform to the element as a composite of the new transform and the current transform (you don't need to extract the values from the matrix, can be applied as is)

demo

(I don't know why, it didn't work in codepen. have moved it to fiddle ...)

Finally I got the * Firefox to behave !

function applyTransform (transform1, transform2) {
var matrix, composite1, composite2;
var cubeCalculator = $('.cubecalculator');
var cube = $('#cube');

matrix = cubeCalculator.css('transform');
composite1 = transform1 + ' ' + matrix;
composite2 = transform2 + ' ' + matrix;
cubeCalculator.get(0).style.transform = composite2;
cube.get(0).style.transition = 'none';
cube.get(0).style.transform = composite1;

window.setTimeout (function() {
cube.get(0).style.transform = composite2;
cube.get(0).style.transition = 'transform 1s';
}, 10 );
}

// rotate using arrow keys
$(document).keyup(function(e) {

e.preventDefault();

var key = e.which,
arrow = {left: 37, up: 38, right: 39, down: 40},
t1, t2;

switch(key) {
case arrow.left:
t1 = 'rotateY(0deg)';
t2 = 'rotateY(-90deg)';
break;

case arrow.right:
t1 = 'rotateY(0deg)';
t2 = 'rotateY(90deg)';
break;

case arrow.up:
t1 = 'rotateX(0deg)';
t2 = 'rotateX(90deg)';
break;

case arrow.down:
t1 = 'rotateX(0deg)';
t2 = 'rotateX(-90deg)';
break;
}

applyTransform (t1, t2);

});

A little bit more complex code, but makes to the browser perfectly clear what you want it to do ...
Works fine as long as you wait till the transition is over.

Getting rotate3d values with Javascript / jQuery

I should have payed a bit more attention to it.
After a while I realized this can not be solved mathematically. Not if I want to get the exact absolute degrees and not the relative ones.

The matrix we get with a transformation of 370 degrees is exactly the same one we get with a transformation of 10 degrees.
Therefore it is impossible to get two different resulting values for alpha with the exact same input.

with transform: rotate3d(1, 0, 0, 10deg); we get:

matrix3d(1, 0, 0, 0, 0, 0.984808, 0.173648, 0, 0, -0.173648, 0.984808, 0, 0, 0, 0, 1)

And with transform: rotate3d(1, 0, 0, 370deg); the exact same one:

matrix3d(1, 0, 0, 0, 0, 0.984808, 0.173648, 0, 0, -0.173648, 0.984808, 0, 0, 0, 0, 1)

Reproduction online



Related Topics



Leave a reply



Submit