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
How to Find Which JavaScript Is Changing an Element's Style
How to Make a Div Always Float on the Screen in Top Right Corner
Set Scrolltop and Scrollleft Without JavaScript
Changing the Default Font Family in Tinymce
How to Detect Linked PDF on a Page and Show Message to Download Adobe Reader Using Jquery
Cannot Apply Any Bootstrap Style in Using React-Bootstrap Library
Getting Values of Global Stylesheet in Jquery
How to Disable JavaScript/CSS Minification in ASP.NET MVC 4 Beta
Select Element by CSS Style (All with Given Style)
Export SASS/Scss Variables to JavaScript Without Exporting Them to CSS
Apply Multiple Styles with .Style() Method in D3.Js
Ie Thumbnail Pixelation When High Resolution Image Is Set to Small Size
Using Media Queries to Only Include Js Files on Mobile
Use JavaScript to Click on a Pseudo-Element
CSS Dropdown Menu: Add Delay on Mouse Out
Syncing Column Width of Between Tables in Two Different Frames, etc
How to Display Selected Image Without Sending Data to Server