使用JavaScript解析SVG变换属性

20

假设我们有一个SVG变换字符串:

transform = "translate(6,5),scale(3,3)";

有没有一个简洁的正则表达式函数可以用来解析它并将其转换为可用的格式?


你能展示一下你尝试过什么吗? - icedwater
我不会使用正则表达式, @icedwater - 我正在寻求一些能帮忙的人。 - Yarin
如果你知道你需要正则表达式,那么你就知道你需要解析什么...然后就是找到正则表达式的正确语法。在这里,我会谷歌搜索JavaScript正则表达式,然后从那里开始。 - icedwater
我的观点是,如果你知道想要过滤什么,可以尝试自己做 - 然后遇到问题在这里寻求帮助。否则,“流畅”和“易用”由你自己定义,这也不... - icedwater
@Yarin请将被采纳的答案更改为Paul的。使用DOM API是像这样操作的唯一可靠实现。 - masterxilo
我在这里创建了一个正确类型化的解决方案 https://stackoverflow.com/questions/57955159/parse-svg-transform-attribute-with-typescript - masterxilo
7个回答

23

这里是一段简洁的代码片段,可能能帮助你得到需要的结果,在解析的字符串具有不同数量参数的情况下,它应该涵盖大多数场景:

function parse (a)
{
    var b={};
    for (var i in a = a.match(/(\w+\((\-?\d+\.?\d*e?\-?\d*,?)+\))+/g))
    {
        var c = a[i].match(/[\w\.\-]+/g);
        b[c.shift()] = c;
    }
    return b;
}

运行这个

parse('translate(6,5),scale(3,3.5),a(1,1),b(2,23,-34),c(300)');

将导致这样的结果:

{
    translate: [ '6', '5' ],
    scale: [ '3', '3.5' ],
    a: [ '1', '1' ],
    b: [ '2', '23', '-34' ],
    c: [ '300' ]
}

优雅?呸。我看了一眼正则表达式,立刻意识到它不能捕获scale(.5)。 - Tatarize
捕获小数的方法是\d*.?\d+。看起来很奇怪,但是小数点前面可以没有数字,小数点后面必须有数字。但是,在3435的情况下,它是第二部分捕获数字。在.3535的情况下,仍然是第二部分捕获数字。 - Tatarize
你可以查看完整的浮点数正则表达式,因为那就是它的作用:[-+]?[0-9]*.?[0-9]+([eE][-+]?[0-9]+)? -- 从这里我们可以看到,你也无法捕获scale(+2)或scale(1e+2)。 - Tatarize
1
没有问题,我可以为您翻译以下与编程有关的内容。请告知需要翻译哪些文本。 - caub
1
这不应该被接受作为答案,它太脆弱了。请使用 DOM API,参见 Paul 的答案。 - masterxilo
显示剩余3条评论

20

假设您在浏览器中运行,您还可以使用SVG DOM解析和解码变换字符串。

var svgns = "http://www.w3.org/2000/svg";

function getTransformList(transformStr)
{
  // Create a fake <rect> element to set the transform to
  var rect = document.createElementNS(svgns, "rect");
  rect.setAttribute("transform", transformStr);
  // Use the DOM to get the list of decoded transforms
  dumpTransform(rect.transform.baseVal);
}

function dumpTransform(transformList)
{
  for (var i = 0; i < transformList.numberOfItems; i++)
  {
    var transform = transformList.getItem(i);
    var m = transform.matrix;
    switch (transform.type)
    {
      case 2:
        console.log("translate("+m.e+","+m.f+")");
        break;
      case 3:
        console.log("scale("+m.a+","+m.d+")");
        break;
      case 4:
        console.log("rotate("+transform.angle+")");
        // TODO need to also handle rotate(angle, x, y) form
        break;
      case 5:
        // TODO skewX()
        break;
      case 6:
        // TODO skewY(()
        break;
      case 1:
      default:
        console.log("matrix("+m.a+","+m.b+","+m.c+","+m.d+","+m.e+","+m.f+")");
        break;
    }
  }
}

getTransformList("translate(6,5),scale(3,3)");


这应该是被接受的答案。其他的都非常脆弱。 - masterxilo
我在这里创建了一个正确类型化的解决方案版本:https://stackoverflow.com/questions/57955159/parse-svg-transform-attribute-with-typescript - masterxilo
1
通过我的编辑,我已经制作了跨浏览器兼容的代码。现在它甚至可以在IE10中工作。希望你没有任何反对意见。 - Bharata
谢谢大家 - 根据推荐,我把这个回答设为采纳答案。 - Yarin

4

改编自@chernjie的解决方案:

function parse_transform(a) {
    var b = {};
    for (var i in a = a.match(/(\w+)\(([^,)]+),?([^)]+)?\)/gi)) {
        var c = a[i].match(/[\w\.\-]+/g);
        b[c.shift()] = c;
    }
    return b;
}

1
谢谢!在这里工作了! - mrlew

2
var transform = "translate(6,5),scale(3,3)";
var translate  = /translate\(\s*([^\s,)]+)[ ,]([^\s,)]+)/.exec(transform);
var translateX = translate[1]
var translateY = translate[2]
var scale  = /scale\(\s*([^\s,)]+)[ ,]([^\s,)]+)/.exec(transform);
var scaleX = translate[1]
var scaleY = translate[2]

transformValues = {translate:{x:translateX,y:translateY},
scale:{x:scaleX,y:scaleY}}

相当恶心,但是 @icedwater 让我自己做所有的工作,所以...

好的,你比我快了 - 你本可以把它编辑到上面的问题中。我也需要查找JS正则表达式。 - icedwater

0

我来试试,你想要将它解析成一个JSON对象,其中每个属性作为列表中的一个条目。到目前为止,这是我做得最接近的:

function listIntoJSON(theTransformInQuestion) {

    // let's work with transform = "translate(6,5),scale(3,3)" as specified.
    var result = [];  // empty list to be returned
    var splitList = theTransformInQuestion.split(/\W/);
    // now splitList is ["translate", "6", "5", "", "scale", "3", "3", ""]

    for (var t = 0; t < splitList.length; t += 4) {
        var op  = {};
        op.name = splitList[t];
        op.x    = splitList[t + 1];
        op.y    = splitList[t + 2];
        result.push(op);  // add op to the result list
    }
    return result;
}

这将会需要

transform = "translate(6,5),scale(3,3)";

并返回

[
    {name: "translate", x: "6", y: "5"}, 
    {name: "scale", x: "3", y: "3"}
]

并且应该适用于任意数量的转换。这有点像黑客,但现在它可以使用。


它花费的时间比我预想的要长,提醒我稍后再继续工作吧 ;) - icedwater
我这边的情况看起来还不错;当我学会如何将事物转储到对象而不是使用列表时,我可能会再次处理它。随意改进它 :) - icedwater
1
谢谢- 不过必须得承认@chernjie的确太厉害了。 - Yarin
好观点,@fabiopagoti。它也不适用于小数,我刚刚检查了一下。 - icedwater

0

虽然这是一个老问题,但今天我也有同样的需求,并最终使用了这个正则表达式:

const regex = /((\w+)\((,?\s*-?\d*\.?\d+(px|in|px|cm|mm|pt|pc|em|ex|ch|rem|deg|rad|grad|turn)?)+\)\s*)/gi;

这是一个相当长的代码,但它考虑了度量单位。

您可以在此处测试:https://regex101.com/r/M8rRjo/1/


0

这不是正则表达式,但有一个类似的问题(D3 v4中替换d3.transform),提供了一个非常好的、全面的解决方案:

function getTransformation(transform) {
  // Create a dummy g for calculation purposes only. This will never
  // be appended to the DOM and will be discarded once this function 
  // returns.
  var g = document.createElementNS("http://www.w3.org/2000/svg", "g");
  
  // Set the transform attribute to the provided string value.
  g.setAttributeNS(null, "transform", transform);
  
  // consolidate the SVGTransformList containing all transformations
  // to a single SVGTransform of type SVG_TRANSFORM_MATRIX and get
  // its SVGMatrix. 
  var matrix = g.transform.baseVal.consolidate().matrix;
  
  // Below calculations are taken and adapted from the private function
  // transform/decompose.js of D3's module d3-interpolate.
  var {a, b, c, d, e, f} = matrix;   // ES6, if this doesn't work, use below assignment
  // var a=matrix.a, b=matrix.b, c=matrix.c, d=matrix.d, e=matrix.e, f=matrix.f; // ES5
  var scaleX, scaleY, skewX;
  if (scaleX = Math.sqrt(a * a + b * b)) a /= scaleX, b /= scaleX;
  if (skewX = a * c + b * d) c -= a * skewX, d -= b * skewX;
  if (scaleY = Math.sqrt(c * c + d * d)) c /= scaleY, d /= scaleY, skewX /= scaleY;
  if (a * d < b * c) a = -a, b = -b, skewX = -skewX, scaleX = -scaleX;
  return {
    translateX: e,
    translateY: f,
    rotate: Math.atan2(b, a) * 180 / Math.PI,
    skewX: Math.atan(skewX) * 180 / Math.PI,
    scaleX: scaleX,
    scaleY: scaleY
  };
}

console.log(getTransformation("translate(20,30)"));  
console.log(getTransformation("rotate(45) skewX(20) translate(20,30) translate(-5,40)"));


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接