如何在 JavaScript 中读取/解析各个变换样式值?

发布于 2024-09-13 11:53:00 字数 489 浏览 9 评论 0 原文

Webkit 去年关于 3D 变换 的博客文章解释了各种变换“功能”可以在 -webkit-transform 属性中使用。例如:

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

我的问题:如何访问 JavaScript 中的各个值?当您读取元素的 webkitTransform 属性时,您只会得到一个包含 16 个值的 Matrix3d() 函数,就像这样......

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

有没有办法只读取单个变换事物的值,例如rotateY()?或者我必须从matrix3d()字符串中读取它,如何读取?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

丶视觉 2024-09-20 11:53:00
// 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*/);
}

希望这有帮助!
请注意,除了纯 DOM API 和本机 Javascript RegExp 函数之外,我没有使用其他库。因此,这应该可以跨浏览器和应用程序普遍工作。

// 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.

白昼 2024-09-20 11:53:00

老而有趣的问题,之前没有任何回应试图回答它。

不幸的是,对于一般情况没有双线解决方案,并且我们不知道需要逆向工程哪些变换步骤(旋转、平移等);越少越容易。

在最近基于 webkit 的浏览器中,可以简单地查询先前分配的属性:

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

让我们继续假设需要使用计算的样式。例如,如果元素位于 CSS 过渡或 CSS 动画的中间,即当前变换不是直接设置的变换,就会出现这种需要。

getComputedStyle 返回的矩阵描述符字符串确实可以通过正则表达式进行解析,但并非上述所有版本都是可靠的,而且它们太不透明了。分解字符串的一种直接方法是这样的,除非它处于非常紧密的循环中以保证单遍正则表达式:

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

但另一种方法是简单地使用它,之前没有提到:

var cssMatrix = new WebKitCSSMatrix(matrixString);

后一种方法的好处是你也会得到一个 4x4 矩阵,这是仿射变换的标准表示,并且缺点是它当然是 WebKit 特定的。如果您继续使用问题中的六个值的元组,则其定义为 CSS 标准的这一部分

然后需要对变换(例如旋转)进行逆向工程。 CSS 可以直接支持许多简单的变换,例如平移、缩放、旋转、倾斜和透视。如果只应用其中之一,那么你只需要恢复计算过程变换矩阵

另一种方法是在 JS 中查找或翻译可以为您执行此操作的代码,例如 同一文档第 7.1 节CSS 标准包含此类带注释的算法。好处是 unmatrix 方法能够在一些限制下返回“原子”变换即使应用了其中一种以上(平移、旋转等)。由于不可能保证一般情况下转换步骤的逆向工程成功,因此考虑可能应用哪些类型的转换以及是否避免了退化或其他麻烦的情况是有用的。即,您需要构建您认为适合您的问题的解决方案。

具体来说,所有 2D CSS 变换的矩阵版本也记录在此处,下面是CSS 2D变换向量中六个值的含义。

另一个需要注意的是,还有其他因素会影响几何操作方面的视觉结果,例如 变换原点

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.

眼眸里的快感 2024-09-20 11:53:00

从复杂的转换字符串中提取键/值对:

使用regex.exec 方法

如果您的正则表达式使用“g”标志,您可以多次使用 exec() 方法来查找同一字符串中的连续匹配项。执行此操作时,搜索会从正则表达式的 lastIndex 属性指定的 str 子字符串开始(test() 也会提前 lastIndex财产)。请注意,搜索不同字符串时,lastIndex 属性不会重置,它将从现有的 lastIndex 处开始搜索。

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

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


/// USAGE: ///////////////////////////////////////

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

console.log(transformObj)

注意 - 请注意,这也是一个有效的 CSS 转换值:

translateX(-50%) translateX(100px);

更复杂的情况:

function parseTransform(s){ 
  var keys = ['matrix3d', 'matrix', 'perspective', 'rotate3d', 'rotateX', 'rotateY', 'rotateZ', 'rotate', 'translate3d', 'translateX', 'translateY', 'translateZ', 'translate', 'scale3d', 'scaleX', 'scaleY', 'scaleZ', 'scale', 'skewX', 'skewY',  'skew'];

  return (s+'    ').replace(  new RegExp( '\\b' + keys.join('|') + '\\b','gi'), m => '    ' +m)
  .split(')    ').reduce((acc, p) => {
    p = p.trim()

    var name = p.slice(0, p.indexOf('(')),
        value = p.slice(p.indexOf('(') + 1, 999)

    if(!p) return acc
   
    if( acc[name] ) 
      acc[name].push(value)
    else 
      acc[name] = [value]

    return acc
  }, {})
}


console.log(
parseTransform('scale(1.2 0.5) rotate(var(--c1) var(--c2)) translateX(-50%) translateX(100px)')
)

Extract key/value pairs from a complex transform string:

using regex.exec method

If your regular expression uses the "g" flag, you can use the exec() method multiple times to find successive matches in the same string. When you do so, the search starts at the substring of str specified by the regular expression's lastIndex property (test() will also advance the lastIndex property). Note that the lastIndex property will not be reset when searching a different string, it will start its search at its existing lastIndex.

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

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


/// USAGE: ///////////////////////////////////////

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

console.log(transformObj)

Note - mind that this is also a valid CSS transform value:

translateX(-50%) translateX(100px);

More Complex Cases:

function parseTransform(s){ 
  var keys = ['matrix3d', 'matrix', 'perspective', 'rotate3d', 'rotateX', 'rotateY', 'rotateZ', 'rotate', 'translate3d', 'translateX', 'translateY', 'translateZ', 'translate', 'scale3d', 'scaleX', 'scaleY', 'scaleZ', 'scale', 'skewX', 'skewY',  'skew'];

  return (s+'    ').replace(  new RegExp( '\\b' + keys.join('|') + '\\b','gi'), m => '    ' +m)
  .split(')    ').reduce((acc, p) => {
    p = p.trim()

    var name = p.slice(0, p.indexOf('(')),
        value = p.slice(p.indexOf('(') + 1, 999)

    if(!p) return acc
   
    if( acc[name] ) 
      acc[name].push(value)
    else 
      acc[name] = [value]

    return acc
  }, {})
}


console.log(
parseTransform('scale(1.2 0.5) rotate(var(--c1) var(--c2)) translateX(-50%) translateX(100px)')
)

诗笺 2024-09-20 11:53:00

我认为,正如 syockit 所说,迭代样式表是唯一的方法,您可以使用 webkitMatchesSelector 来发现与您的元素匹配的规则:

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);
        }
    }
}

这假设标记是这样的,我添加了一些额外的规则和值以确保我提取出正确的值。如果您有多个样式表,那么您需要调整第一部分以迭代所有样式表,并且如果您的 -webkit-transform 出现在多个样式表中,您可能必须处理特殊性一条规则:

<!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>

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>
诗笺 2024-09-20 11:53:00

我今天早上遇到了这个问题。看来 JavaScript 无法读取元素的 style.webkitTransform 属性,除非在元素的 style 属性中显式设置该属性(在 HTML 中内联或通过 JavaScript 程序化)。尽管这听起来很笨拙,但如果您需要 JS 能够读取 CSS 转换属性,那么您最好在 DOM 准备就绪时使用 JavaScript 定义它们的初始值。

例如,使用 jQuery:

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

从现在开始,您将能够读取元素的转换字符串并解析它以获取所需的值。

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.

梦途 2024-09-20 11:53:00

这个 来自 Apple Dev Reference 的链接可能会更清楚地说明该主题:

webkitTransform 属性是
列表的字符串表示
变换操作。通常这个
列表包含单个矩阵
变换操作。对于 3D
变换后,其值为
"matrix3d(...)" 的 16 个值
之间的 4x4 齐次矩阵
括号。对于 2D 变换,
值是一个“matrix(...)”字符串
包含 6 个向量值。

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.

旧城烟雨 2024-09-20 11:53:00

由于您只能从计算的样式中获得最终的矩阵值,因此您可能必须检查元素的内联样式或样式表规则。如果 element.style.webkitTransform 没有给您任何信息,您可能会迭代文档的样式表,看看哪一个与您的元素匹配。然后,您可以对 webkitTransform 属性进行正则表达式来获取/设置该值。

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.

扬花落满肩 2024-09-20 11:53:00

你可以使用正则表达式来获取属性值的映射:

如果变量transformstyle包含样式值

  //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)"

你会得到:

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

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"}
西瑶 2024-09-20 11:53:00

只是因为我没有看到任何有效的 Javascript 单行解决方案来转换矩阵代码,这是我的。快速而简单:

首先获取矩阵中的所有变换值:

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

将变换 y 提取为整数:

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

将变换 x 提取为整数:

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

访问旋转值相当困难,但如果您愿意,这里有一个很好的指南这样做:
https://css-tricks.com/get-value- of-css-rotation-through-javascript/

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/

抚你发端 2024-09-20 11:53:00

如果你想要完整的转换,你确实可以使用getCompulatedStyle(element).transform

但无论如何,只需使用 DOMMatrix 构造函数:new DOMMatrix(element.style.transform)。你就完成了。

If you want the complete transformation, you can indeed use getComputedStyle(element).transform.

But anyway, just use DOMMatrix constructor: new DOMMatrix(element.style.transform). And you're done.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文