How to read individual transform values in JavaScr

2019-01-22 21:00发布

问题:

Webkit's blog post from last year on 3D transforms explains the various transform 'functions' that can be used in the -webkit-transform property. For example:

#myDiv {
  -webkit-transform: scale(1.1) rotateY(7deg) translateZ(-1px);
}

My question: how do you access individual values in JavaScript? When you read the webkitTransform property of the element, you just get a matrix3d() function with 16 values in it, like this...

matrix3d(0.958684, 0.000000, .....)

Is there a way to just read the value of an individual transform thing, like rotateY()? Or do I have to read it from the matrix3d() string, and how?

回答1:

// Suppose the transformed element is called "cover".
var element = document.getElementById('cover');
computedStyle = window.getComputedStyle(element, null); // "null" means this is not a pesudo style.
// You can retrieve the CSS3 matrix string by the following method.
var matrix = computedStyle.getPropertyValue('transform')
    || computedStyle.getPropertyValue('-moz-transform')
    || computedStyle.getPropertyValue('-webkit-transform')
    || computedStyle.getPropertyValue('-ms-transform')
    || computedStyle.getPropertyValue('-o-transform');

// Parse this string to obtain different attributes of the matrix.
// This regexp matches anything looks like this: anything(1, 2, 3, 4, 5, 6);
// Hence it matches both matrix strings:
// 2d: matrix(1,2,3,4,5,6)
// 3d: matrix3d(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16);
var matrixPattern = /^\w*\((((\d+)|(\d*\.\d+)),\s*)*((\d+)|(\d*\.\d+))\)/i;
var matrixValue = [];
if (matrixPattern.test(matrix)) { // When it satisfy the pattern.
    var matrixCopy = matrix.replace(/^\w*\(/, '').replace(')', '');
    console.log(matrixCopy);
    matrixValue = matrixCopy.split(/\s*,\s*/);
}

Hope this helps! Note that I did not use another library except plain DOM API and native Javascript RegExp function. Hence, this should work universally cross browsers and application.



回答2:

I think, as syockit says, iterating through the stylesheets is the only way to go, you can use webkitMatchesSelector to discover rules which match your element:

var theRules = new Array();
var theStylesheet = document.styleSheets;
if (document.styleSheets[0].cssRules)
        theRules = document.styleSheets[0].cssRules
else if (document.styleSheets[0].rules)
        theRules = document.styleSheets[0].rules

var elem = document.getElementById("myDiv");

for (var i=0; i < theRules.length; i++) {
    if (elem.webkitMatchesSelector(theRules[i].selectorText)) {
        var theStyles = theRules[i].style;
        var j = theStyles.cssText.indexOf('-webkit-transform:');
        if (j>-1) {
            var s = theStyles.cssText.substring(j,theStyles.cssText.length).indexOf(';'); 
            document.getElementById("output").innerHTML=theStyles.cssText.substring(j+18,s);
        }
    }
}

This assumes markup something like this, I added some extra rules and values to make sure I was pulling out the right values. If you have more than one stylesheet then you need to adjust the first part to iterate through all the stylesheets too, and you'll probably have to deal with specificity if your -webkit-transform appears in more than one rule:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Get style</title>
    <style>
    div {
        margin: 2em;
        padding: 2em;
    }
    #myDiv {
        -webkit-transform: scale(1.1) rotateY(7deg) translateZ(-1px);
        border: 1px solid;
    }
    </style>
</head>
<body>
    <div id="myDiv">
        Just testing.
    </div>
    <div id="output">
    </div>
</body>
</html>


回答3:

I ran into this issue this morning. It appears that JavaScript can't read an element's style.webkitTransform property until it's been explicitly set in the element's style attribute (either inline in the HTML or procedurally via JavaScript). As kludgy as this sounds, if you need JS to be able to read CSS transform properties, you might be better off defining their initial values with JavaScript when the DOM is ready.

Example, using jQuery:

$(document).ready(function(){
    $('.transform').css('webkitTransform', 'translateX(0)');
});

From this point forward, you'll be able to read the element's transform string and parse through it for the needed values.



回答4:

Old but interesting question and no previous response has attempted to answer it.

Unfortunately there's no two-liner solution for the general case and we don't know about what variety of transform steps (rotate, translate etc.) need to be reverse engineered; the fewer the easier.

In recent webkit based browsers, one can simply query the previously assigned property:

var element = document.querySelector('div.slipping');
element.style.webkitTransform 
  -> "translate(-464px, 0px)"

Let's continue with the assumption that the computed style needs to be used. The need arises, for example, if the element is in the middle of a CSS transition or CSS animation, i.e. the current transformation is not one that was set directly.

The matrix descriptor string returned by getComputedStyle can indeed be parsed via a regex, but not all versions above are reliable, and they're too opaque. A straightforward way to break down the string, unless it's in a very tight loop to warrant a single-pass regex, is this:

var matrixString = window.getComputedStyle(element).webkitTransform;
var matrix = matrixString
             .split('(')[1]
             .split(')')[0]
             .split(',')
             .map(parseFloat);

But another way is to simply use this, which wasn't mentioned before:

var cssMatrix = new WebKitCSSMatrix(matrixString);

The benefit of the latter approach is that you get back a 4x4 matrix as well, which is the standard representation for affine transformations, and the drawback is that it's of course WebKit specific. Should you continue to work with the tuple of six values as in the question, it's defined in this part of the CSS standard.

Then the transform, e.g. rotation needs to be reverse engineered. There can be many simple transforms directly supported by CSS, e.g. translate, scale, rotate, skew, and perspective. If only one of them is applied, then you just need to revert the process of computing the transform matrix.

An alternative is to find or translate code which does this for you in JS, e.g. the same document or Section 7.1 of the CSS standard contains such annotated algorithms. The benefit is that the unmatrix approach is able to, with some limitations, return the 'atomic' transforms even if more than one of these (translate, rotate etc.) is applied. Since it's not possible to guarantee the successful reverse engineering of the transform steps for the general case, it's useful to think about what types of transforms are possibly applied, and whether degenerate or other troublesome cases have been avoided. I.e. you need to build the solution as you see fit for your problem.

Specifically, the matrix versions of all 2D CSS transforms are documented here as well, below the meaning of the six values in the CSS 2D transform vector.

Another caveat is that there are other things that influence the visual outcome in terms of geometric operations, for example, transform-origin.



回答5:

This link from Apple Dev Reference might shed more light on the subject:

The webkitTransform property is a string representation of a list of transform operations. Usually this list contains a single matrix transform operation. For 3D transforms, the value is "matrix3d(...)" with the 16 values of the 4x4 homogeneous matrix between the parentheses. For 2D transforms, the value is a "matrix(...)" string containing the 6 vector values.



回答6:

Since you only get the final matrix value from the computed style, you might have to check the element's inline style or stylesheet rules. If element.style.webkitTransform gives you nothing, you might to iterate through the document's stylesheets, and see which one matches your element. Then you can regex the webkitTransform property to get/set the value.



回答7:

you can use regex to get a map of property-value:

if variable transformstyle contains the style value

  //get all transform declarations
 (transformstyle.match(/([\w]+)\(([^\)]+)\)/g)||[]) 
      //make pairs of prop and value         
     .map(function(it){return it.replace(/\)$/,"").split(/\(/)})
     //convert to key-value map/object         
     .reduce(function(m,it){return m[it[0]]=it[1],m},{})

for:

var transformstyle="-webkit-transform: scale(1.1) rotateY(7deg) translateZ(-1px)"

you would get:

{scale: "1.1", rotateY: "7deg", translateZ: "-1px"}


回答8:

Just because I didn´t see any working Javascript one Line solutions to convert the matrix code, here is mine. Quick and easy:

First get all the transform Values in a matrix:

var yourDivsCssValues= window.getComputedStyle(yourDiv, null);
transformValues = testDivCSS.getPropertyValue('transform');

To extract transform-y as an Integer:

var transformValueY = parseInt((yourVariable1.replace (/,/g, "")).split(" ")[5]);

To extract transform-x as an Integer:

var transformValuetX = parseInt((yourVariable2.replace (/,/g, "")).split(" ")[4]);

Accessing the rotation value is quite difficult, but there is a good guide, if you want to do it: https://css-tricks.com/get-value-of-css-rotation-through-javascript/



回答9:

I thought of one possibility. If you're prepared to parse strings in JavaScript, use

data=document.getElementById("myDiv").getAttribute("-webkit-transform");

then interpret data.



回答10:

Easy way to extract individual values from a complex transform value:

function parseComplexStyleProperty( str ){
   var regex = /(\w+)\((.+?)\)/g,
       transform = {},
       match;

   while( match = regex.exec(str) )
       transform[match[1]] = match[2];
  
   return transform;
}

//////////////////////////////////////////

var dummyString = "translateX(-50%) scale(1.2)",
    transformObj = parseComplexStyleProperty(dummyString);

console.log(transformObj);