关于 svg 中矩阵值 e 和 f 的误解
Misconception regarding the matrix values e and f in svg
关于 svg 元素的 matrix
,特别是 e 和 f 部分,我似乎有一个逻辑上的误解,希望有人能帮我弄清楚。
基于this:
Translation is equivalent to the matrix:
or [1 0 0 1 tx ty], where tx and ty are the distances to translate
coordinates in x and y, respectively.
例子
下面提供两个小例子svg
。每个包含一个 path
,它们都被旋转 15°
。我试图用石灰 circle
.
标记点 p(x1,y1)
;window.onload = () => {
//REM: Query the first element in svg => [path, path]
document.querySelectorAll('svg > *[transform]').forEach((element) => {
let tSVG = element.ownerSVGElement;
let tBBox = element.getBBox();
let tMatrix = element.transform.baseVal.getItem(0).matrix;
//REM: Draw a circle at p(x1,y1) - before rotation
let tOldX = tBBox.x * tMatrix.a + tMatrix.e;
let tOldY = tBBox.y * tMatrix.d + tMatrix.f
let tCircle1 = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
tCircle1.setAttributeNS(null, 'r', '5');
tCircle1.setAttributeNS(null, 'fill', 'orange');
tCircle1.setAttributeNS(null, 'fill-opacity', '0.5');
tCircle1.setAttributeNS(null, 'cx', tOldX);
tCircle1.setAttributeNS(null, 'cy', tOldY);
tSVG.appendChild(tCircle1);
//REM: Rotate by 15° around center of itself
let tTransform = tSVG.createSVGTransform();
tTransform.setRotate(15, tBBox.x + tBBox.width / 2, tBBox.y + tBBox.height / 2);
element.transform.baseVal.appendItem(tTransform);
element.transform.baseVal.initialize(element.transform.baseVal.consolidate());
//REM: Fetch new matrix
tMatrix = element.transform.baseVal.getItem(0).matrix;
//REM: Pure scale
let tScaleX = Math.sqrt(tMatrix.a * tMatrix.a + tMatrix.b * tMatrix.b);
let tScaleY = Math.sqrt(tMatrix.c * tMatrix.c + tMatrix.d * tMatrix.d);
//REM: Draw a circle at p(x1,y1) - after rotation
let tNewX = tBBox.x * tScaleX + tMatrix.e;
let tNewY = tBBox.y * tScaleY + tMatrix.f;
let tCircle2 = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
tCircle2.setAttributeNS(null, 'r', '5');
tCircle2.setAttributeNS(null, 'fill', 'lime');
tCircle2.setAttributeNS(null, 'fill-opacity', '0.9');
tCircle2.setAttributeNS(null, 'cx', tNewX);
tCircle2.setAttributeNS(null, 'cy', tNewY);
tSVG.appendChild(tCircle2);
//REM: Connection
let tLine = document.createElementNS('http://www.w3.org/2000/svg', 'line');
tLine.setAttributeNS(null, 'stroke', 'green');
tLine.setAttributeNS(null, 'x1', tOldX);
tLine.setAttributeNS(null, 'y1', tOldY);
tLine.setAttributeNS(null, 'x2', tNewX);
tLine.setAttributeNS(null, 'y2', tNewY);
tSVG.appendChild(tLine);
console.log(
'e: ' + tMatrix.e,
'f: ' + tMatrix.f
)
})
}
<svg xmlns = 'http://www.w3.org/2000/svg' viewBox = '950,-250,100,400' height='800' width='300'>
<path d = 'm1029.31 112.6h97.40v57.01h-97.40z' fill = 'red' transform = 'matrix(0.9, 0, 0, 0.9, 15, -15)' />
</svg>
<svg xmlns = 'http://www.w3.org/2000/svg' viewBox = '-40,-40,150,100' height='200' width='300'>
<path d = 'M0 0L100 0L100 60L0 60z' fill = 'blue' transform = 'matrix(0.9, 0, 0, 0.9, 15, -15)'></path>
</svg>
问题
根据我的理解,我应该能够通过使用以下计算:
- x1: (bbox.x * scale) + matrix.e //原始位置x缩放+物体移动x
- y1: (bbox.y * scale) + matrix.f //原始位置y缩放+物体移动y
然而,它只适用于 second/blue svg 而不是 first/red svg。 first/red svg 的 f 对我来说似乎有点离谱:
- e: 80.92766714917656 f: -261.78134648854723 //first/red svg
- e: 23.52145237337207 f: -25.726854765665173 //second/blue svg
我哪里出错了?
备注
虽然我为语法添加了 Javascript 标记,但重点不在 Javascript 上。 Javascript 提供了正确放置石灰 circle
的其他可能性。
我没有研究它失败的确切原因。我有几个想法。但是,如果您应用正确的矩阵乘法公式,它就可以正常工作。
let tNewX = tBBox.x * tMatrix.a + tBBox.y * tMatrix.c + tMatrix.e;
let tNewY = tBBox.x * tMatrix.b + tBBox.y * tMatrix.d + tMatrix.f;
;window.onload = () => {
//REM: Query the first element in svg => [path, path]
document.querySelectorAll('svg > *[transform]').forEach((element) => {
let tSVG = element.ownerSVGElement;
let tBBox = element.getBBox();
let tMatrix = element.transform.baseVal.getItem(0).matrix;
//REM: Draw a circle at p(x1,y1) - before rotation
let tOldX = tBBox.x * tMatrix.a + tMatrix.e;
let tOldY = tBBox.y * tMatrix.d + tMatrix.f
let tCircle1 = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
tCircle1.setAttributeNS(null, 'r', '5');
tCircle1.setAttributeNS(null, 'fill', 'orange');
tCircle1.setAttributeNS(null, 'fill-opacity', '0.5');
tCircle1.setAttributeNS(null, 'cx', tOldX);
tCircle1.setAttributeNS(null, 'cy', tOldY);
tSVG.appendChild(tCircle1);
//REM: Rotate by 15° around center of itself
let tTransform = tSVG.createSVGTransform();
tTransform.setRotate(15, tBBox.x + tBBox.width / 2, tBBox.y + tBBox.height / 2);
element.transform.baseVal.appendItem(tTransform);
element.transform.baseVal.initialize(element.transform.baseVal.consolidate());
//REM: Fetch new matrix
tMatrix = element.transform.baseVal.getItem(0).matrix;
//REM: Draw a circle at p(x1,y1) - after rotation
let tNewX = tBBox.x * tMatrix.a + tBBox.y * tMatrix.c + tMatrix.e;
let tNewY = tBBox.x * tMatrix.b + tBBox.y * tMatrix.d + tMatrix.f;
let tCircle2 = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
tCircle2.setAttributeNS(null, 'r', '5');
tCircle2.setAttributeNS(null, 'fill', 'lime');
tCircle2.setAttributeNS(null, 'fill-opacity', '0.9');
tCircle2.setAttributeNS(null, 'cx', tNewX);
tCircle2.setAttributeNS(null, 'cy', tNewY);
tSVG.appendChild(tCircle2);
//REM: Connection
let tLine = document.createElementNS('http://www.w3.org/2000/svg', 'line');
tLine.setAttributeNS(null, 'stroke', 'green');
tLine.setAttributeNS(null, 'x1', tOldX);
tLine.setAttributeNS(null, 'y1', tOldY);
tLine.setAttributeNS(null, 'x2', tNewX);
tLine.setAttributeNS(null, 'y2', tNewY);
tSVG.appendChild(tLine);
console.log(
'e: ' + tMatrix.e,
'f: ' + tMatrix.f
)
})
}
<svg xmlns = 'http://www.w3.org/2000/svg' viewBox = '950,-250,100,400' height='800' width='300'>
<path d = 'm1029.31 112.6h97.40v57.01h-97.40z' fill = 'red' transform = 'matrix(0.9, 0, 0, 0.9, 15, -15)' />
</svg>
<svg xmlns = 'http://www.w3.org/2000/svg' viewBox = '-40,-40,150,100' height='200' width='300'>
<path d = 'M0 0L100 0L100 60L0 60z' fill = 'blue' transform = 'matrix(0.9, 0, 0, 0.9, 15, -15)'></path>
</svg>
关于 svg 元素的 matrix
,特别是 e 和 f 部分,我似乎有一个逻辑上的误解,希望有人能帮我弄清楚。
基于this:
Translation is equivalent to the matrix:
or [1 0 0 1 tx ty], where tx and ty are the distances to translate coordinates in x and y, respectively.
例子
下面提供两个小例子svg
。每个包含一个 path
,它们都被旋转 15°
。我试图用石灰 circle
.
;window.onload = () => {
//REM: Query the first element in svg => [path, path]
document.querySelectorAll('svg > *[transform]').forEach((element) => {
let tSVG = element.ownerSVGElement;
let tBBox = element.getBBox();
let tMatrix = element.transform.baseVal.getItem(0).matrix;
//REM: Draw a circle at p(x1,y1) - before rotation
let tOldX = tBBox.x * tMatrix.a + tMatrix.e;
let tOldY = tBBox.y * tMatrix.d + tMatrix.f
let tCircle1 = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
tCircle1.setAttributeNS(null, 'r', '5');
tCircle1.setAttributeNS(null, 'fill', 'orange');
tCircle1.setAttributeNS(null, 'fill-opacity', '0.5');
tCircle1.setAttributeNS(null, 'cx', tOldX);
tCircle1.setAttributeNS(null, 'cy', tOldY);
tSVG.appendChild(tCircle1);
//REM: Rotate by 15° around center of itself
let tTransform = tSVG.createSVGTransform();
tTransform.setRotate(15, tBBox.x + tBBox.width / 2, tBBox.y + tBBox.height / 2);
element.transform.baseVal.appendItem(tTransform);
element.transform.baseVal.initialize(element.transform.baseVal.consolidate());
//REM: Fetch new matrix
tMatrix = element.transform.baseVal.getItem(0).matrix;
//REM: Pure scale
let tScaleX = Math.sqrt(tMatrix.a * tMatrix.a + tMatrix.b * tMatrix.b);
let tScaleY = Math.sqrt(tMatrix.c * tMatrix.c + tMatrix.d * tMatrix.d);
//REM: Draw a circle at p(x1,y1) - after rotation
let tNewX = tBBox.x * tScaleX + tMatrix.e;
let tNewY = tBBox.y * tScaleY + tMatrix.f;
let tCircle2 = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
tCircle2.setAttributeNS(null, 'r', '5');
tCircle2.setAttributeNS(null, 'fill', 'lime');
tCircle2.setAttributeNS(null, 'fill-opacity', '0.9');
tCircle2.setAttributeNS(null, 'cx', tNewX);
tCircle2.setAttributeNS(null, 'cy', tNewY);
tSVG.appendChild(tCircle2);
//REM: Connection
let tLine = document.createElementNS('http://www.w3.org/2000/svg', 'line');
tLine.setAttributeNS(null, 'stroke', 'green');
tLine.setAttributeNS(null, 'x1', tOldX);
tLine.setAttributeNS(null, 'y1', tOldY);
tLine.setAttributeNS(null, 'x2', tNewX);
tLine.setAttributeNS(null, 'y2', tNewY);
tSVG.appendChild(tLine);
console.log(
'e: ' + tMatrix.e,
'f: ' + tMatrix.f
)
})
}
<svg xmlns = 'http://www.w3.org/2000/svg' viewBox = '950,-250,100,400' height='800' width='300'>
<path d = 'm1029.31 112.6h97.40v57.01h-97.40z' fill = 'red' transform = 'matrix(0.9, 0, 0, 0.9, 15, -15)' />
</svg>
<svg xmlns = 'http://www.w3.org/2000/svg' viewBox = '-40,-40,150,100' height='200' width='300'>
<path d = 'M0 0L100 0L100 60L0 60z' fill = 'blue' transform = 'matrix(0.9, 0, 0, 0.9, 15, -15)'></path>
</svg>
问题
根据我的理解,我应该能够通过使用以下计算:
- x1: (bbox.x * scale) + matrix.e //原始位置x缩放+物体移动x
- y1: (bbox.y * scale) + matrix.f //原始位置y缩放+物体移动y
然而,它只适用于 second/blue svg 而不是 first/red svg。 first/red svg 的 f 对我来说似乎有点离谱:
- e: 80.92766714917656 f: -261.78134648854723 //first/red svg
- e: 23.52145237337207 f: -25.726854765665173 //second/blue svg
我哪里出错了?
备注
虽然我为语法添加了 Javascript 标记,但重点不在 Javascript 上。 Javascript 提供了正确放置石灰 circle
的其他可能性。
我没有研究它失败的确切原因。我有几个想法。但是,如果您应用正确的矩阵乘法公式,它就可以正常工作。
let tNewX = tBBox.x * tMatrix.a + tBBox.y * tMatrix.c + tMatrix.e;
let tNewY = tBBox.x * tMatrix.b + tBBox.y * tMatrix.d + tMatrix.f;
;window.onload = () => {
//REM: Query the first element in svg => [path, path]
document.querySelectorAll('svg > *[transform]').forEach((element) => {
let tSVG = element.ownerSVGElement;
let tBBox = element.getBBox();
let tMatrix = element.transform.baseVal.getItem(0).matrix;
//REM: Draw a circle at p(x1,y1) - before rotation
let tOldX = tBBox.x * tMatrix.a + tMatrix.e;
let tOldY = tBBox.y * tMatrix.d + tMatrix.f
let tCircle1 = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
tCircle1.setAttributeNS(null, 'r', '5');
tCircle1.setAttributeNS(null, 'fill', 'orange');
tCircle1.setAttributeNS(null, 'fill-opacity', '0.5');
tCircle1.setAttributeNS(null, 'cx', tOldX);
tCircle1.setAttributeNS(null, 'cy', tOldY);
tSVG.appendChild(tCircle1);
//REM: Rotate by 15° around center of itself
let tTransform = tSVG.createSVGTransform();
tTransform.setRotate(15, tBBox.x + tBBox.width / 2, tBBox.y + tBBox.height / 2);
element.transform.baseVal.appendItem(tTransform);
element.transform.baseVal.initialize(element.transform.baseVal.consolidate());
//REM: Fetch new matrix
tMatrix = element.transform.baseVal.getItem(0).matrix;
//REM: Draw a circle at p(x1,y1) - after rotation
let tNewX = tBBox.x * tMatrix.a + tBBox.y * tMatrix.c + tMatrix.e;
let tNewY = tBBox.x * tMatrix.b + tBBox.y * tMatrix.d + tMatrix.f;
let tCircle2 = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
tCircle2.setAttributeNS(null, 'r', '5');
tCircle2.setAttributeNS(null, 'fill', 'lime');
tCircle2.setAttributeNS(null, 'fill-opacity', '0.9');
tCircle2.setAttributeNS(null, 'cx', tNewX);
tCircle2.setAttributeNS(null, 'cy', tNewY);
tSVG.appendChild(tCircle2);
//REM: Connection
let tLine = document.createElementNS('http://www.w3.org/2000/svg', 'line');
tLine.setAttributeNS(null, 'stroke', 'green');
tLine.setAttributeNS(null, 'x1', tOldX);
tLine.setAttributeNS(null, 'y1', tOldY);
tLine.setAttributeNS(null, 'x2', tNewX);
tLine.setAttributeNS(null, 'y2', tNewY);
tSVG.appendChild(tLine);
console.log(
'e: ' + tMatrix.e,
'f: ' + tMatrix.f
)
})
}
<svg xmlns = 'http://www.w3.org/2000/svg' viewBox = '950,-250,100,400' height='800' width='300'>
<path d = 'm1029.31 112.6h97.40v57.01h-97.40z' fill = 'red' transform = 'matrix(0.9, 0, 0, 0.9, 15, -15)' />
</svg>
<svg xmlns = 'http://www.w3.org/2000/svg' viewBox = '-40,-40,150,100' height='200' width='300'>
<path d = 'M0 0L100 0L100 60L0 60z' fill = 'blue' transform = 'matrix(0.9, 0, 0, 0.9, 15, -15)'></path>
</svg>