在嵌套的 SVG 中使用视图框实现缩放

问题描述

我的任务是在基于 SVG 的自定义图表中实现缩放。在我有一个 <svg> 元素之前。我研究了使用 transformviewBox 方法并决定使用 viewBox 方法。由于我必须支持仅在 x 轴上缩放,图表内容将根据 zoomFactor 和需要 preserveAspectRatio="none" 被压缩。这对于我使用 foreignObjects 的图表标签来说并不好看。它们也变得混乱,不再可读。我没有找到关于如何将 viewBox 仅应用于实际图表内容而不是比例/标签的任何解决方案。

我想出了将图表拆分为 3 个嵌套 svg 的解决方案。结构如下:

<svg> // Container SVG
   <svg>...</svg> // XAxis
   <svg>...</svg> // YAxis
   <svg>...</svg> // ChartContent
</svg>

viewBox 将仅应用于 ChartContent svg 并且具有实际比例的 svgs 保持不变,如果需要,只需在不同位置使用不同标签重新渲染。期望的结果类似于此示例:https://jsfiddle.net/19h83ker/1/

假设我有一个图表要显示,例如 4000 像素宽和 200 像素高,y 轴是 40 像素宽和 200 像素高,x 轴是 40 像素高和 4000 像素宽,我通常应该如何设置视口?如果我在 ContainerSVG 和 ChartContent SVG 上设置 width="100%"height="100%",我没有可用的滚动条。如果我在 ContainerSVG 上设置 width="4040" 并在 ChartContent SVG 上设置 width="4000"我有一个滚动条,但是在缩小 100% 时应用 viewBox 只会将我的 svg 的宽度和右侧减半50% 留空。我真的不明白我的结构中宽度/高度的组合是什么,我应该去。或者我在一般情况下犯了一个错误?有没有更好的方法来实现预期的结果?我已经在这上面花了 2 天的时间,除了这 3 个嵌套的 svgs 之外,真的没有看到任何其他选项。在一天结束时,应该可以在 4000 像素宽的示例 SVG 图表中进行平移/缩放。

解决方法

您提到的建议涉及设置宽度和高度,然后使用默认浏览器行为进行滚动和缩放。相反,您需要拦截适当的事件并修改 viewBox 属性。

下面的代码演示了鼠标滚轮事件的缩放。平移应该更简单(只有 viewBox 的第一和第二部分需要更改),但实现取决于您要使用的 UI 控件。

svg = document.getElementById("s")
var vb = [0,300,200]
svg.setAttribute("viewBox",vb) // vb gets converted to the string "0,40"

function zoom(wheelEvent){
  let k=1.005**wheelEvent.deltaY
  let ctm = svg.children[0].getScreenCTM()//If the svg is empty,this won't work
  // position of the mouse in svg coords
  let mx = (wheelEvent.clientX-ctm.e)/ctm.a
  let my = (wheelEvent.clientY-ctm.f)/ctm.d
  // To center the zoom on the mouse,we need:
  // (mx - initialX)/initialWidth == (mx - finalX)/finalWidth
  // finalX = mx - (mx - initalX)*(finalWidth/initialWidth)
  vb[0] = mx-(mx-vb[0])*k
  vb[1] = my-(my-vb[1])*k
  vb[2] *= k
  vb[3] *= k
  svg.setAttribute("viewBox",vb)
  wheelEvent.preventDefault() // prevent the page from scrolling
}

svg.addEventListener("wheel",zoom)
html,body,svg{
  width: 100%;
  height: 100%;
  margin: 0px;
  padding: 0px;
}
#s{
  width: 100%;
  height: 100%;
  background-color: red;
}
<svg id="outer" viewBox="0 0 200 100" preserveAspectRatio="xMinYMin">
<rect x="0" y="0" width="20" height="200" />
<rect x="0" y="0" width="100%" height="20" />
<svg id="s" x="20" y="20" width="180" height="180" viewBox="0,200">
<rect x="-1000" y="-1000" width="2000" height="2000" fill="lightgray"/>
<text font-size="60" x="10" y="60" fill="blue"> hello </text>
</svg>
</svg>

如果您只想缩放 x 轴,则需要为 'preserveAspectRatio' 属性设置不同的值(以及删除更改 vb[1] 和 vb[3] 的代码)。