D3 SVG 将缩放移动到具有不同角度的 <g> 内的矩形



<svg>了背景图像和该 svg 上处于正确位置的不同矩形.
我的矩形按平移值按<g>分组,每个矩形都有特定的旋转值。现在我希望我的 SVG 聚焦(显示在页面中心)特定的矩形上,而不会干扰背景图像并在那里进行 svg 翻译。主要问题是这些矩形也有不同的角度。 所以翻译无法正常工作。

SVG 如下所示.
任何人都可以为我提供指南吗?

<svg width="4500" height="3610" style="background: url(&quot;/images/001.jpg&quot;) no-repeat;">   
<g id="228" transform="translate(2001,620)rotate(216) translate(0 -216)">
<g>
<rect x="0" y="0" height="72" width="36" stroke="black" style="fill: transparent;">
</rect><text x="0" y="36" text-anchor="start" style="fill: black;">228</text>
</g>
<g>
<rect x="36" y="0" height="72" width="36" stroke="black" style="fill: transparent;">
</rect><text x="36" y="36" text-anchor="start" style="fill: black;">229</text>
</g>
</g>
</svg>

这在 SVG 中应该更容易,而且可能更容易,但这是我的解决方案:

首先,我们需要获取要缩放到的矩形的边界框,包括它的坐标空间:

// Bounds in local coordinates space:
var bounds = rectElement.getBBox();

其次,我们从这个边界框里得到四个角:

// Corners of rect in local coordinate space:
var corners = [
{x: bounds.x,y: bounds.y},
{x: bounds.x + bounds.width, y: bounds.y},
{x: bounds.x + bounds.width, y: bounds.y + bounds.height },
{x: bounds.x, y: bounds.y + bounds.height }
];

第三,我们得到矩形和 SVG 之间的转换矩阵:

// relevant transform:
var t = rectElement.getCTM();

现在,我们可以将我们拥有的角转换为全局 SVG 单位:

// Convert the points to global SVG coordainte space:
for(var i = 0; i < corners.length; i++) {
var point = svg.node().createSVGPoint();
point.x = corners[i].x;
point.y = corners[i].y;
corners[i] = point.matrixTransform(t);
}

好的,此时我们将旋转矩形的角放在可用坐标中。我们想找到这些坐标的宽度和高度:

// get extents for x,y in global SVG space:
var x = d3.extent(corners, function(d) { return d.x; });
var y = d3.extent(corners, function(d) { return d.y; });
var w = x[1] - x[0];
var h = y[1] - y[0];    

这为我们提供了旋转的宽度和高度,我们现在可以用来缩放 SVG:。我在这里的缩放函数中使用了很小的边距(每边 10% 的边距):

// For getting scale factor:
var scale = function(elementWidth,elementHeight,SVGWidth,SVGHeight) {
return Math.min(SVGWidth/elementWidth*0.8,SVGHeight/elementHeight*0.8);
}

我这样使用:

var k = scale(w,h,SVGwidth,SVGheight);

此时,我可以缩放到平移为-x[0]*k-y[0]*k和比例为k的矩形。但是,这将导致 SVG 左上角的形状,我想考虑边距,矩形宽度/高度的任何内容相对于 SVG 宽度/高度都较小。为此,我计算 x, y 中每个偏移量:

// Offset to center feature:
var ox = (SVGwidth - w*k)/2;
var oy = (SVGheight- h*k)/2;

现在我可以创建我的变换以在 SVG 中将给定矩形居中:

var newTransform = "translate("+[-x[0]*k+ox,-y[0]*k+oy]+")scale("+k+")";

这些代码片段使用父g来保存 SVG 中的所有内容,这样可以更轻松地管理转换。此外,此g在缩放到矩形时擦除了它的变换,因为我们不希望从当前缩放状态/变换进行相对变换,而是从原始缩放状态/变换进行相对变换,否则我们会看到奇怪的行为。

下面是一个具有缩放行为的示例,允许您手动缩放/平移,但可以选择单击以缩放到矩形:

var svg = d3.select("svg");
var contents = svg.html();
svg.selectAll("*").remove();
var g = svg.append("g")
.attr("transform", "translate(0,0)scale(1)")
.html(contents);
var SVGwidth = 400;
var SVGheight = 180;
// For getting scale factor:
var scale = function(elementWidth,elementHeight,SVGWidth,SVGHeight) {
return Math.min(SVGWidth/elementWidth*0.8,SVGHeight/elementHeight*0.8);
}
var zoom = d3.zoom()
.on("zoom", function(d) {
g.attr("transform",d3.event.transform);
})
svg.call(zoom);
function zoomToRect(rect) {
// Bounds in local coordinates space:
var bounds = rect.getBBox();

// Corners of rect in local coordinate space:
var corners = [
{x: bounds.x,y: bounds.y},
{x: bounds.x + bounds.width, y: bounds.y},
{x: bounds.x + bounds.width, y: bounds.y + bounds.height },
{x: bounds.x, y: bounds.y + bounds.height }
];
// Reset transform:
g.attr("transform","");

// relevant transform:
var t = rect.getCTM();

// Convert the points to global SVG coordainte space:
for(var i = 0; i < corners.length; i++) {
var point = svg.node().createSVGPoint();
point.x = corners[i].x;
point.y = corners[i].y;
corners[i] = point.matrixTransform(t);
}

// get extents for x,y in global SVG space:
var x = d3.extent(corners, function(d) { return d.x; });
var y = d3.extent(corners, function(d) { return d.y; });
var w = x[1] - x[0];
var h = y[1] - y[0]; 
var k = scale(w,h,SVGwidth,SVGheight);
// Offset to center feature:
var ox = (SVGwidth - w*k)/2;
var oy = (SVGheight- h*k)/2;

var newTransform = d3.zoomIdentity
.translate(-x[0]*k+ox,-y[0]*k+oy)
.scale(k);

svg.call(zoom.transform,newTransform);              

}


d3.select("button").on("click", function() { zoomToRect(d3.select("rect").node()) });
svg {
border: 1px dotted black;
}
div {
position: absolute;
top: 0;
left: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg width="400" height="180" style="background: url(&quot;/images/001.jpg&quot;) no-repeat;">   
<g id="228" transform="translate(2001,620)rotate(216) translate(0 -216)">
<g>
<rect x="0" y="0" height="72" width="36" stroke="black" style="fill: transparent;"></rect>
<text x="0" y="36" text-anchor="start" style="fill: black;">228</text>
</g>
<g>
<rect x="36" y="0" height="72" width="36" stroke="black" style="fill: transparent;">
</rect><text x="36" y="36" text-anchor="start" style="fill: black;">229</text>
</g>
</g>
<g id="228" transform="translate(1801,550)rotate(65) translate(0 0)">
<g>
<rect x="0" y="0" height="100" width="20" stroke="black" style="fill: transparent;"></rect>
<text x="0" y="36" text-anchor="start" style="fill: black;">454</text>
</g>
<g>
<rect x="36" y="0" height="20" width="100" stroke="black" style="fill: transparent;">
</rect><text x="36" y="36" text-anchor="start" style="fill: black;">455</text>
</g> 
</g>
</svg>
<div><button>Zoom to 228</button></div>

下面是一个片段,它只是在没有缩放行为的情况下缩放,使用 SVG 和两个奖励矩形在四个矩形之间循环:

var svg = d3.select("svg");
var contents = svg.html();
svg.selectAll("*").remove();
var g = svg.append("g")
.attr("transform", "translate(0,0)scale(1)")
.html(contents);
var SVGwidth = 400;
var SVGheight = 180;
// For getting scale factor:
var scale = function(elementWidth,elementHeight,SVGWidth,SVGHeight) {
return Math.min(SVGWidth/elementWidth*0.8,SVGHeight/elementHeight*0.8);
}
var current = 0;
function transition() {
g.selectAll("rect").each(function(_,i,n) {
if(i != current) return;


// Bounds in local coordinates space:
var bounds = n[i].getBBox();

// Corners of rect in local coordinate space:
var corners = [
{x: bounds.x,y: bounds.y},
{x: bounds.x + bounds.width, y: bounds.y},
{x: bounds.x + bounds.width, y: bounds.y + bounds.height },
{x: bounds.x, y: bounds.y + bounds.height }
];
// Hold original transform:
var oldTransform = g.attr("transform");
// Reset transform:
g.attr("transform","");

// relevant transform:
var t = n[i].getCTM();

// Convert the points to global SVG coordainte space:
for(var i = 0; i < corners.length; i++) {
var point = svg.node().createSVGPoint();
point.x = corners[i].x;
point.y = corners[i].y;
corners[i] = point.matrixTransform(t);
}

// get extents for x,y in global SVG space:
var x = d3.extent(corners, function(d) { return d.x; });
var y = d3.extent(corners, function(d) { return d.y; });
var w = x[1] - x[0];
var h = y[1] - y[0]; 
var k = scale(w,h,SVGwidth,SVGheight);
// Offset to center feature:
var ox = (SVGwidth - w*k)/2;
var oy = (SVGheight- h*k)/2;

var newTransform = "translate("+[-x[0]*k+ox,-y[0]*k+oy]+")scale("+k+")"

g.attr("transform",oldTransform)
.transition()
.on("start", function() {
d3.select(n[current]).style("fill","orange");
})
.duration(2000)
.attr("transform",newTransform)
.on("end", function() {
d3.select(n[current]).style("fill","none");
current++;
current = current%g.selectAll("rect").size();
transition();         
})
})
}
transition();
svg {
border: 1px dotted black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg width="400" height="180" style="background: url(&quot;/images/001.jpg&quot;) no-repeat;">   
<g id="228" transform="translate(2001,620)rotate(216) translate(0 -216)">
<g>
<rect x="0" y="0" height="72" width="36" stroke="black" style="fill: transparent;"></rect>
<text x="0" y="36" text-anchor="start" style="fill: black;">228</text>
</g>
<g>
<rect x="36" y="0" height="72" width="36" stroke="black" style="fill: transparent;">
</rect><text x="36" y="36" text-anchor="start" style="fill: black;">229</text>
</g>
</g>
<g id="228" transform="translate(1801,550)rotate(65) translate(0 0)">
<g>
<rect x="0" y="0" height="100" width="20" stroke="black" style="fill: transparent;"></rect>
<text x="0" y="36" text-anchor="start" style="fill: black;">454</text>
</g>
<g>
<rect x="36" y="0" height="20" width="100" stroke="black" style="fill: transparent;">
</rect><text x="36" y="36" text-anchor="start" style="fill: black;">455</text>
</g> 
</g>
</svg>

最新更新