我有这个SVG:
<svg id="root" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="347.27mm" viewBox="0 0 409.32783 1230.3321" width="115.48mm" version="1.1">
<circle id="b1" cx="0" cy="0" r="15" class="bottom" />
<circle id="b2" cx="120" cy="100" r="15" class="bottom" />
<g transform="translate(17,43)">
<circle id="b3" cx="240" cy="200" r="15" class="bottom" />
<circle id="b4" cx="360" cy="300" r="15" class="bottom" />
</g>
<!-- <g transform="translate(10,20)"> -->
<circle id="t1" cx="80" cy="30" r="15" class="top" />
<circle id="t2" cx="130" cy="30" r="15" class="top" />
<!-- <g transform="translate(10,20)"> -->
<circle id="t3" cx="180" cy="30" r="15" class="top" />
<circle id="t4" cx="230" cy="30" r="15" class="top" />
<!-- </g> -->
<!-- </g> -->
</svg>
红球坐标被后面的代码更改后。它呈现为:
我想把红球放在绿色的上面。我有一个解决方案,如果您删除SVG标记中的viewBox属性。但不幸的是我无法做到这一点。但是,如果viewBox不存在,则此方法有效:
var svg = document.getElementById("root");
for (var index = 1; index <= 4; index++) {
var bottom = svg.getElementById("b" + index);
var top = svg.getElementById("t" + index);
var targetX = parseFloat(top.getAttribute("cx"));
var targetY = parseFloat(top.getAttribute("cy"));
var center = getElementCenter(svg, bottom);
top.setAttribute("cx", parseFloat(center.x));
top.setAttribute("cy", parseFloat(center.y));
}
function getTransformedCoords(x, y, matrix) {
var transformedX = x * matrix.a + y * matrix.c + matrix.e;
var transformedY = x * matrix.b + y * matrix.d + matrix.f;
return {
x: transformedX,
y: transformedY
};
}
function getElementCenter(svg, element) {
var x, y, width = 0, height = 0;
var boundingBox = element.getBBox();
var centerX = boundingBox.x + (boundingBox.width / 2);
var centerY = boundingBox.y + (boundingBox.height / 2);
var out = getTransformedCoords(centerX, centerY, element.getCTM()); // getCTM affected by the viewport
centerX = out.x;
centerY = out.y;
return {
x: centerX,
y: centerY
};
}
我的工作理论是,这是因为viewBox与视口的比例不同。但我不知道如何解决这个问题。
我想使用普通JavaScript 来执行此操作。即使使用合适的库也不错。
问题:
JSFiddle如果你想尝试代码:https://jsfiddle.net/kentl/gpqcdecx/
PS。如果要设置样式,这是CSS:
.top {fill:red;animation:fall 4s ease-in-out infinite;}
.bottom{fill:green;}
@keyframes fall {0%{opacity:0.0;}50%{opacity:1.0;}100%{opacity:0.0;}}
答案 0 :(得分:0)
只需使用getTransformToElement。对于Chrome,您需要一个polyfill,因为它不再在那里实施。
// The goal is to place the red circles on top of the green ones
// Working theory:
// The viewBox hasn't got the same ratio as the viewport, which affects the returned transformation, returning
// a matrix with slightly larger numbers than 1 for scaling X and Y. As that matrix is used to convert from the
// local coordinate space in the bottom element, into the global one used by the top element, the coordinates are
// scaled up slightly.. But when those coordinates are set in the top element, they'll be affected AGAIN by the
// same up-scaling. Making them become more and more off the further from 0,0 we come.
// Question:
// 1. How can that be fixed?
// 2. Only if it's not too hard: Uncomment the <g>'s in the SVG. How do we compensate for the affected top CTM as well?
console.clear();
var svg = document.getElementById("root");
for (var index = 1; index <= 4; index++) {
var bottom = document.getElementById("b" + index);
var top1 = document.getElementById("t" + index);
var targetX = parseFloat(top1.getAttribute("cx"));
var targetY = parseFloat(top1.getAttribute("cy"));
var center = getElementCenter(svg, bottom);
t = bottom.getTransformToElement(top1);
top1.setAttribute("cx", parseFloat(center.x) + t.e);
top1.setAttribute("cy", parseFloat(center.y) + t.f);
}
function getElementCenter(svg, element) {
var x, y, width = 0, height = 0;
var boundingBox = element.getBBox();
var centerX = boundingBox.x + (boundingBox.width / 2);
var centerY = boundingBox.y + (boundingBox.height / 2);
return {
x: centerX,
y: centerY
};
}
&#13;
.top {
fill: red;
animation: fall 4s ease-in-out infinite;
}
.bottom {
fill: green;
}
@keyframes fall {
0% {
opacity: 0.0;
}
50% {
opacity: 1.0;
}
100% {
opacity: 0.0;
}
}
&#13;
<svg id="root" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="347.27mm" viewBox="0 0 409.32783 1230.3321" width="115.48mm" version="1.1">
<circle id="b1" cx="0" cy="0" r="15" class="bottom" />
<circle id="b2" cx="120" cy="100" r="15" class="bottom" />
<g transform="translate(17,43)">
<circle id="b3" cx="240" cy="200" r="15" class="bottom" />
<circle id="b4" cx="360" cy="300" r="15" class="bottom" />
</g>
<!-- <g transform="translate(10,20)"> -->
<circle id="t1" cx="80" cy="30" r="15" class="top" />
<circle id="t2" cx="130" cy="30" r="15" class="top" />
<!-- <g transform="translate(10,20)"> -->
<circle id="t3" cx="180" cy="30" r="15" class="top" />
<circle id="t4" cx="230" cy="30" r="15" class="top" />
<!-- </g> -->
<!-- </g> -->
</svg>
&#13;