使用打字稿解析SVG变换属性 [英] Parse SVG transform attribute with typescript
问题描述
如何使用Typescript解析svg元素的transform属性?
How do I parse the transform attribute of svg elements using typescript?
也就是说,如何解析svg.g字符串中的所有数字和运算在以下内容中进行转换:
That is, how can I parse all the numbers and operations in the string at svg.g.transform in the following:
<svg viewBox="-40 0 150 100" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g fill="grey"
transform="rotate(-10 50 100)
translate(-36 45.5)
skewX(40)
scale(1 0.5)">
<path id="heart" d="M 10,30 A 20,20 0,0,1 50,30 A 20,20 0,0,1 90,30 Q 90,60 50,90 Q 10,60 10,30 z" />
</g>
<use xlink:href="#heart" fill="none" stroke="red"/>
</svg>
推荐答案
使用 https://developer.mozilla.org/en-US/docs/Web/API/SVGGraphicsElement 又名由本地DOM元素实现的 SVGLocatable
和 SVGTransformable
接口/ API。
Use the https://developer.mozilla.org/en-US/docs/Web/API/SVGGraphicsElement aka. SVGLocatable
and SVGTransformable
interfaces/API that is implemented by the native DOM elements.
这些元素具有对应于transform属性的 .transform
属性。此属性的类型为 https://developer.mozilla.org/en -US / docs / Web / API / SVGAnimatedTransformList ,并且您要查看静态定义的baseVal。
These elements have a .transform
property that corresponds to the transform attribute. This property has the type https://developer.mozilla.org/en-US/docs/Web/API/SVGAnimatedTransformList and you want to look at the statically defined baseVal.
转换列表的属性为 numberOfItems
和 getItem
方法。它可能具有 .length
属性和 []
数组访问器,并且在浏览器中可能是可迭代的,但是不要
The transform list has an attribute numberOfItems
and a getItem
method. It may have a .length
property and []
array accessor and it may be iterable in your browser, but don't count on that.
每个项目的类型均为 https://developer.mozilla.org/zh-CN/docs/Web/API/SVGTransform
The .type
属性告诉您使用了哪一条指令。
The .type
property tells you which instruction was used.
因此,这里是您解析然后手动合成的方法再次变换属性:
Therefore, here is how you can parse and then manually synthesize the transform attribute again:
// javascript js equivalent declaration:
// function getAttributeTransform_js(nativeSVGElement) {
// typescript ts declaration
function getAttributeTransform_ts(nativeSVGElement: SVGGraphicsElement) {
// this definition works in ts and js
const tl = nativeSVGElement.transform.baseVal;
const st = [];
for (let i = 0; i < tl.numberOfItems; i++) {
const t/*: SVGTransform*/ = tl.getItem(i);
switch (t.type) {
case SVGTransform.SVG_TRANSFORM_UNKNOWN: break;
case SVGTransform.SVG_TRANSFORM_MATRIX: {
// A matrix(…) transformation
// Note: this is the most general transformation, capable of representing more transformations than the other combined.
// For SVG_TRANSFORM_MATRIX, the matrix contains the a, b, c, d, e, f values supplied by the user.
//
// Note: instead of comma (,), whitespace separation would also be allowed
st.push(`matrix(${t.matrix.a}, ${t.matrix.b}, ${t.matrix.c}, ${t.matrix.d}, ${t.matrix.e}, ${t.matrix.f})`);
break;
}
case SVGTransform.SVG_TRANSFORM_TRANSLATE: {
// A translate(…) transformation
// For SVG_TRANSFORM_TRANSLATE, e and f represent the translation amounts (a=1, b=0, c=0 and d=1).
st.push(`translate(${t.matrix.e}, ${t.matrix.f})`);
break;
}
case SVGTransform.SVG_TRANSFORM_SCALE: {
// A scale(…) transformation
// For SVG_TRANSFORM_SCALE, a and d represent the scale amounts (b=0, c=0, e=0 and f=0).
st.push(`scale(${t.matrix.a}, ${t.matrix.d})`);
break;
}
case SVGTransform.SVG_TRANSFORM_ROTATE: {
// A rotate(…) transformation
// For SVG_TRANSFORM_ROTATE, a, b, c, d, e and f together represent the matrix which will result in the given rotation.
// When the rotation is around the center point (0, 0), e and f will be zero.
/*
angle float A convenience attribute for SVG_TRANSFORM_ROTATE, SVG_TRANSFORM_SKEWX and SVG_TRANSFORM_SKEWY. It holds the angle that was specified.
For SVG_TRANSFORM_MATRIX, SVG_TRANSFORM_TRANSLATE and SVG_TRANSFORM_SCALE, angle will be zero.
*/
/*
This is the hardest case since the origin information is lost!
We need to recompute it from the matrix.
from https://math.stackexchange.com/questions/2093314/rotation-matrix-of-rotation-around-a-point-other-than-the-origin
matrix.a = cos_angle = c;
matrix.b = sin_angle = s;
Note that by the laws of geometry: c^2+s^2 = 1 (c and s are coordinates on the unit circle)
matrix.e = -x*c + y*s + x;
matrix.f = -x*s - y*c + y;
Using Mathematica/Wolfram Language:
"Assuming[c^2+s^2==1,Solve[e == -x*c + y*s + x&& f == -x*s - y*c + y,{x,y},Reals]//Simplify]//InputForm"
(you can use WL for free here: https://develop.wolframcloud.com/objects/c26e16f7-44e7-4bb6-81b3-bc07782f9cc5)
{{x -> (e + (f*s)/(-1 + c))/2, y -> (f - c*f + e*s)/(2 - 2*c)}}
*/
const e = t.matrix.e, f = t.matrix.f, c = t.matrix.a, s = t.matrix.b;
const originx = (e + (f*s)/(-1 + c))/2;
const originy = (f - c*f + e*s)/(2 - 2*c);
st.push(`rotate(${t.angle}, ${originx}, ${originy})`);
break;
}
case SVGTransform.SVG_TRANSFORM_SKEWX: {
// A skewx(…) transformation
// For SVG_TRANSFORM_SKEWX and SVG_TRANSFORM_SKEWY, a, b, c and d represent the matrix which will result in the given skew (e=0 and f=0).
/*
angle float A convenience attribute for SVG_TRANSFORM_ROTATE, SVG_TRANSFORM_SKEWX and SVG_TRANSFORM_SKEWY. It holds the angle that was specified.
For SVG_TRANSFORM_MATRIX, SVG_TRANSFORM_TRANSLATE and SVG_TRANSFORM_SCALE, angle will be zero.
*/
st.push(`skewx(${t.angle})`);
break;
}
case SVGTransform.SVG_TRANSFORM_SKEWY: {
// A skewy(…) transformation
// For SVG_TRANSFORM_SKEWX and SVG_TRANSFORM_SKEWY, a, b, c and d represent the matrix which will result in the given skew (e=0 and f=0).
/*
angle float A convenience attribute for SVG_TRANSFORM_ROTATE, SVG_TRANSFORM_SKEWX and SVG_TRANSFORM_SKEWY. It holds the angle that was specified.
For SVG_TRANSFORM_MATRIX, SVG_TRANSFORM_TRANSLATE and SVG_TRANSFORM_SCALE, angle will be zero.
*/
st.push(`skewy(${t.angle})`);
break;
}
}
}
return st.join(','); // instead of comma (,), whitespace separation is also allowed
}
// example
const r = <SVGRectElement>document.createElementNS("http://www.w3.org/2000/svg", "rect");
// the parseable syntax for the transform attribute is pretty relaxed
r.setAttribute("transform", "translate(1, 0),rotate(0.5), scale(1 2)");
// note that the browser may canonicalize your syntax
// EDGE canonicalizes the transform to read:
// 'translate(1) rotate(0.5) scale(1, 2)'
console.log(r.getAttribute("transform"));
// basically equivalent:
console.log(getAttributeTransform_ts(r));
您的示例:
function createElementFromHTML(htmlString) {
var div = document.createElement('div');
div.innerHTML = htmlString.trim();
// Change this to div.childNodes to support multiple top-level nodes
return div.firstChild;
}
getAttributeTransform_ts(createElementFromHTML(`
<g fill="grey"
transform="rotate(-10 50 100)
translate(-36 45.5)
skewX(40)
scale(1 0.5)">
<path id="heart" d="M 10,30 A 20,20 0,0,1 50,30 A 20,20 0,0,1 90,30 Q 90,60 50,90 Q 10,60 10,30 z" />
</g>
`))
// gives
// 'rotate(-10, 49.99999999999982, 99.99999999999972),translate(-36, 45.5),skewx(40),scale(1, 0.5)'
请注意,您应该使用 .getAttribute( transform)
,让浏览器为您合成SVGTransformList的字符串形式,而不是使用上面的脚本!
Note that you should use .getAttribute("transform")
to let the browser synthesize the string form of an SVGTransformList for you, instead of using my script above!
请注意,我们无法检索旋转,因为它没有API。
Note that we cannot retrieve the origin argument of "rotate" perfectly, because there is no API for it. It has to be computed from the 2d-homogeneous (rotation) matrix.
灵感来自于:
- Parse SVG transform attribute with javascript
- https://stackoverflow.com/a/41102221/524504
另请参见:
- https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/transform
这篇关于使用打字稿解析SVG变换属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!