如何放大复杂的svg结构

时间:2018-09-30 09:01:17

标签: reactjs svg

我在代码段中有一组复杂的形状。它们是用React渲染的,但我实际上只是在寻找一些有关如何放大和缩小这些形状的指针。

我的Google搜索失败了,我只能真正找到图表示例。

如何放大和缩小像这样的复杂结构?

    <svg height="767" width="903">
    <g class="vx-group vx-tree" transform="translate(20, 70)">
        <g class="vx-group" transform="translate(0, 70)">
            <g class="vx-group" transform="translate(0, 0)">
                <path class="vx-link-vertical" d="M451.5,0C451.5,233.5,451.5,233.5,451.5,467" percent="0.5"
                      stroke="#f7f7f3" stroke-width="1" stroke-opacity="0.2" fill="none"></path>
            </g>
            <g class="vx-group" transform="translate(0, 0)">
                <g class="vx-group" transform="translate(451.5, 0)" opacity="1">
                    <g class="vx-group node__container" transform="translate(0, 0)">
                        <svg class="" x="0" y="0" style="overflow: visible;">
                            <polygon
                                    points="25.98076211353316,-14.999999999999998 25.98076211353316,14.999999999999998 1.83697019872103e-15,30 -25.98076211353316,14.999999999999998 -25.980762113533157,-15.000000000000004 -5.510910596163089e-15,-30"
                                    class="node__hexagon"></polygon>
                        </svg>
                        <g class="vx-group node__business-unit" transform="translate(0, 0)">
                            <use xlink:href="#icon-BusinessUnit"></use>
                        </g>
                        <g class="hierarchy-label__container" transform="translate(0, -40)">
                            <path class="" d="
                                  M 0.0078125, 5.15625
                                  L 34.64882865137755,25.156249999999996 
                                  M -0.9921875, 5.15625 
                                  L -34.63320365137754,25.156249999999996
                                  H -65.8515625 
                                  a8,8 0 0 1 -8,-8  
                                  V -47.15625 
                                  a8,8 0 0 1 8,-8 H 65.8515625 a8,8 0 0 1 8,8 
                                  L 73.8515625, 17.156249999999996  
                                  a8,8 0 0 1 -8,8 
                                  L 34.64882865137755, 25.156249999999996 
                                  Z 
                                 ">
                            </path>
                            <svg x="0" y="0" style="overflow: visible;">
                                <text class="hierarchy-label__item__name" width="150" y="-25" x="0" text-anchor="middle"
                                      style="pointer-events: none;">
                                    <tspan x="0" dy="0em">Finance</tspan>
                                </text>
                            </svg>
                            <svg x="0" y="0" style="overflow: visible;">
                                <text class="hierarchy-label__item__type" width="150" y="-5" x="0" text-anchor="middle"
                                      style="pointer-events: none;">
                                    <tspan x="0" dy="0.71em">Business Unit</tspan>
                                </text>
                            </svg>
                        </g>
                    </g>
                </g>
            </g>
        </g>
    </g>
</svg>

4 个答案:

答案 0 :(得分:6)

svg中的缩放是通过viewBox完成的,它结合了缩放和偏移量。有一篇不错的文章How to Scale SVG。 来自以下article

  

如果您将文档视为画布,则视图框是其中的一部分   您希望观众看到的画布。

这就像相机应用程序中手机的屏幕一样,显示以指定比例和偏移量观察到的部分场景。

enter image description here

可以找到here。该示例很好地演示了viewBox。

进行一些数学运算,我使用鼠标滚轮实现了放大/缩小。此外,还可以通过鼠标移动进行平移并显示比例值。一个示例演示如何使用viewBox:

const svgImage = document.getElementById("svgImage");
const svgContainer = document.getElementById("svgContainer");

var viewBox = {x:0,y:0,w:svgImage.getAttribute("width"),h:svgImage.getAttribute("height")};
svgImage.setAttribute('viewBox', `${viewBox.x} ${viewBox.y} ${viewBox.w} ${viewBox.h}`);
const svgSize = {w:svgImage.getAttribute("width"),h:svgImage.getAttribute("height")};
var isPanning = false;
var startPoint = {x:0,y:0};
var endPoint = {x:0,y:0};;
var scale = 1;

svgContainer.onmousewheel = function(e) {
   e.preventDefault();
   var w = viewBox.w;
   var h = viewBox.h;
   var mx = e.x;//mouse x  
   var my = e.y;    
   var dw = w*Math.sign(e.deltaY)*0.05;
   var dh = h*Math.sign(e.deltaY)*0.05;
   var dx = dw*mx/svgSize.w;
   var dy = dh*my/svgSize.h;
   viewBox = {x:viewBox.x+dx,y:viewBox.y+dy,w:viewBox.w-dw,h:viewBox.h-dh};
   scale = svgSize.w/viewBox.w;
   zoomValue.innerText = `${Math.round(scale*100)/100}`;
   svgImage.setAttribute('viewBox', `${viewBox.x} ${viewBox.y} ${viewBox.w} ${viewBox.h}`);
}


svgContainer.onmousedown = function(e){
   isPanning = true;
   startPoint = {x:e.x,y:e.y};   
}

svgContainer.onmousemove = function(e){
   if (isPanning){
  endPoint = {x:e.x,y:e.y};
  var dx = (startPoint.x - endPoint.x)/scale;
  var dy = (startPoint.y - endPoint.y)/scale;
  var movedViewBox = {x:viewBox.x+dx,y:viewBox.y+dy,w:viewBox.w,h:viewBox.h};
  svgImage.setAttribute('viewBox', `${movedViewBox.x} ${movedViewBox.y} ${movedViewBox.w} ${movedViewBox.h}`);
   }
}

svgContainer.onmouseup = function(e){
   if (isPanning){ 
  endPoint = {x:e.x,y:e.y};
  var dx = (startPoint.x - endPoint.x)/scale;
  var dy = (startPoint.y - endPoint.y)/scale;
  viewBox = {x:viewBox.x+dx,y:viewBox.y+dy,w:viewBox.w,h:viewBox.h};
  svgImage.setAttribute('viewBox', `${viewBox.x} ${viewBox.y} ${viewBox.w} ${viewBox.h}`);
  isPanning = false;
   }
}

svgContainer.onmouseleave = function(e){
 isPanning = false;
}
<span id="zoomValue">1</span>
<div id="svgContainer">
<svg id="svgImage" height="964" width="767">
    <g  class="vx-group vx-tree" transform="translate(20, 70)">
        <g class="vx-group" transform="translate(0, 70)">
            <g class="vx-group" transform="translate(0, 0)">
                <path class="vx-link-vertical" d="M451.5,0C451.5,233.5,451.5,233.5,451.5,467" percent="0.5" stroke="#f7f7f3" stroke-width="1" stroke-opacity="0.2" fill="none"></path>
            </g>
            <g class="vx-group" transform="translate(0, 0)">
                <g class="vx-group" transform="translate(451.5, 0)" opacity="1">
                    <g class="vx-group node__container" transform="translate(0, 0)">
                        <svg class="" x="0" y="0" style="overflow: visible;">
                            <polygon points="25.98076211353316,-14.999999999999998 25.98076211353316,14.999999999999998 1.83697019872103e-15,30 -25.98076211353316,14.999999999999998 -25.980762113533157,-15.000000000000004 -5.510910596163089e-15,-30" class="node__hexagon"></polygon>
                        </svg>
                        <g class="vx-group node__business-unit" transform="translate(0, 0)">
                            <use xlink:href="#icon-BusinessUnit"></use>
                        </g>
                        <g class="hierarchy-label__container" transform="translate(0, -40)">
                           <path class="" d="
                              M 0.0078125, 5.15625
                              L 34.64882865137755,25.156249999999996 
                              M -0.9921875, 5.15625 
                              L -34.63320365137754,25.156249999999996
                              H -65.8515625 
                              a8,8 0 0 1 -8,-8  
                              V -47.15625 
                              a8,8 0 0 1 8,-8 H 65.8515625 a8,8 0 0 1 8,8 
                              L 73.8515625, 17.156249999999996  
                              a8,8 0 0 1 -8,8 
                              L 34.64882865137755, 25.156249999999996 
                              Z 
                             "></path>
     			          <svg x="0" y="0" style="overflow: visible;">
     			              <text class="hierarchy-label__item__name" width="150" y="-25" x="0" text-anchor="middle" style="pointer-events: none;">
     			                  <tspan x="0" dy="0em">Finance</tspan>
     			              </text>
     			          </svg>
             			  <svg x="0" y="0" style="overflow: visible;">
             			      <text class="hierarchy-label__item__type" width="150" y="-5" x="0" text-anchor="middle" style="pointer-events: none;">
             			          <tspan x="0" dy="0.71em">Business Unit</tspan>
             			      </text>
             			  </svg>
                       </g>
                   </g>
               </g>
           </g>
       </g>
   </g>
   </svg>
</div>

数学:

enter image description here

答案 1 :(得分:5)

您可以简单地使用 transform来缩放。在您要“固定”缩放原点的位置设置 transform-origin,然后像上面示例中使用最小值的范围输入元素一样,在scale(x)中使用transform1,最大到200,以便从1%缩放到200%

const slider = document.getElementById("zoomRange");
const zvgZoom = document.getElementById("svgZoom");
const zoomValue = document.getElementById("zoomValue");

slider.oninput = function() {
    //console.log('zoom', this.value / 100);
    zoomValue.innerText = `${this.value}%`;
    zvgZoom.style.transform = `scale(${this.value / 100})`;
}
#svgContainer {
    background-color: #dedede;
}

#svgZoom {
    transform-origin: 0% 0%;
}
<input type="range" min="1" max="200" value="100" class="slider" id="zoomRange">
<span id="zoomValue">100%</span>

<div id="svgContainer">
    <svg id="svgZoom" height="767" width="903">
        <g  class="vx-group vx-tree" transform="translate(20, 70)">
            <g class="vx-group" transform="translate(0, 70)">
                <g class="vx-group" transform="translate(0, 0)">
                    <path class="vx-link-vertical" d="M451.5,0C451.5,233.5,451.5,233.5,451.5,467" percent="0.5" stroke="#f7f7f3" stroke-width="1" stroke-opacity="0.2" fill="none"></path>
                </g>
                <g class="vx-group" transform="translate(0, 0)">
                    <g class="vx-group" transform="translate(451.5, 0)" opacity="1">
                        <g class="vx-group node__container" transform="translate(0, 0)">
                            <svg class="" x="0" y="0" style="overflow: visible;">
                                <polygon points="25.98076211353316,-14.999999999999998 25.98076211353316,14.999999999999998 1.83697019872103e-15,30 -25.98076211353316,14.999999999999998 -25.980762113533157,-15.000000000000004 -5.510910596163089e-15,-30" class="node__hexagon"></polygon>
                            </svg>
                            <g class="vx-group node__business-unit" transform="translate(0, 0)">
                                <use xlink:href="#icon-BusinessUnit"></use>
                            </g>
                            <g class="hierarchy-label__container" transform="translate(0, -40)">
                               <path class="" d="
                                  M 0.0078125, 5.15625
                                  L 34.64882865137755,25.156249999999996 
                                  M -0.9921875, 5.15625 
                                  L -34.63320365137754,25.156249999999996
                                  H -65.8515625 
                                  a8,8 0 0 1 -8,-8  
                                  V -47.15625 
                                  a8,8 0 0 1 8,-8 H 65.8515625 a8,8 0 0 1 8,8 
                                  L 73.8515625, 17.156249999999996  
                                  a8,8 0 0 1 -8,8 
                                  L 34.64882865137755, 25.156249999999996 
                                  Z 
                                 "></path>
         			          <svg x="0" y="0" style="overflow: visible;">
         			              <text class="hierarchy-label__item__name" width="150" y="-25" x="0" text-anchor="middle" style="pointer-events: none;">
         			                  <tspan x="0" dy="0em">Finance</tspan>
         			              </text>
         			          </svg>
                 			  <svg x="0" y="0" style="overflow: visible;">
                 			      <text class="hierarchy-label__item__type" width="150" y="-5" x="0" text-anchor="middle" style="pointer-events: none;">
                 			          <tspan x="0" dy="0.71em">Business Unit</tspan>
                 			      </text>
                 			  </svg>
                           </g>
                       </g>
                   </g>
               </g>
           </g>
       </g>
   </svg>
</div>

答案 2 :(得分:2)

当我找不到用于缩放的重量级脚本时(没有一个被测试者能够将鼠标光标保持在中心点),我进行了“一点点”研究,最终得到了自己的解决方案。我找到了使用viewbox的最简单方法。

外部svg必须声明了viewBox(或应在函数开头创建)。

<svg id="svgImage" width="900" height="500" viewBox="0 0 900 500">
<style>text { fill: white; }</style>
<g id="par" class="vx-group vx-tree" transform="translate(20, 70)">

    <g class="vx-group" transform="translate(0, 70)">
        <g class="vx-group" transform="translate(0, 0)">
            <path class="vx-link-vertical" d="M451.5,0C451.5,233.5,451.5,233.5,451.5,467" percent="0.5" stroke="#f7f7f3" stroke-width="1" stroke-opacity="0.2" fill="none"></path>
        </g>
        <g class="vx-group" transform="translate(0, 0)">
            <g class="vx-group" transform="translate(451.5, 0)" opacity="1">
                <g class="vx-group node__container" transform="translate(0, 0)">
                    <svg class="" x="0" y="0" style="overflow: visible;">
                        <polygon points="25.98076211353316,-14.999999999999998 25.98076211353316,14.999999999999998 1.83697019872103e-15,30 -25.98076211353316,14.999999999999998 -25.980762113533157,-15.000000000000004 -5.510910596163089e-15,-30" class="node__hexagon"></polygon>
                    </svg>
                    <g class="vx-group node__business-unit" transform="translate(0, 0)">
                        <use xlink:href="#icon-BusinessUnit"></use>
                    </g>
                    <g class="hierarchy-label__container" transform="translate(0, -40)">
                        <path class="" d="
                        M 0.0078125, 5.15625
                        L 34.64882865137755,25.156249999999996 
                        M -0.9921875, 5.15625 
                        L -34.63320365137754,25.156249999999996
                        H -65.8515625 
                        a8,8 0 0 1 -8,-8  
                        V -47.15625 
                        a8,8 0 0 1 8,-8 H 65.8515625 a8,8 0 0 1 8,8 
                        L 73.8515625, 17.156249999999996  
                        a8,8 0 0 1 -8,8 
                        L 34.64882865137755, 25.156249999999996 
                        Z 
                        "></path>
                        <svg x="0" y="0" style="overflow: visible;"><text class="hierarchy-label__item__name" width="150" y="-25" x="0" text-anchor="middle" style="pointer-events: none;"><tspan x="0" dy="0em">Finance</tspan></text></svg>
                        <svg x="0" y="0" style="overflow: visible;"><text class="hierarchy-label__item__type" width="150" y="-5" x="0" text-anchor="middle" style="pointer-events: none;"><tspan x="0" dy="0.71em">Business Unit</tspan></text></svg>
                    </g>
                </g>
            </g>
        </g>
    </g>
</g>
</svg>

<script>
const svgImage = document.getElementById("svgImage");
const svgSize = {w:svgImage.clientWidth ,h:svgImage.clientHeight};
var oldScale = 1;

svgImage.onmousewheel = function(e) {
    e.preventDefault();

    var svgW     = svgSize.w,
        svgH     = svgSize.h,
        mX       = e.offsetX,
        mY       = e.offsetY,
        delta    = (e.wheelDelta) ? -e.wheelDelta : e.detail,
        newScale = oldScale + (oldScale*delta/1200); //1200: intensity

    var vb      = svgImage.getAttribute('viewBox').split(" ");
    var newW    = svgW * newScale,
        newH    = svgH * newScale,
        newX    = vb[0]*1 + (vb[2]*1 - newW) * (mX/svgW),
        newY    = vb[1]*1 + (vb[3]*1 - newH) * (mY/svgH);

    viewBox = { x:Math.round(newX), y:Math.round(newY), w:newW, h:newH };
    svgImage.setAttribute('viewBox', `${viewBox.x} ${viewBox.y} ${viewBox.w} ${viewBox.h}`);
    oldScale = newScale;
</script>

PS。 Sara Soueidan撰写了很多有关SVG坐标系操纵的文章(不仅如此)。好博客可供挖掘。

答案 3 :(得分:1)

缩放和平移是数据可视化中常见且有用的技术,由于矢量图形不会像其位图副本那样受到像素化的影响,因此在基于SVG的可视化中效果特别好。

此答案探讨了尼克齐祝(Nick Qi Zhu)的书Data Visualization with D3.js Cookbook (2013).

中D3对缩放和平移的内置支持。

首先在网络浏览器中打开以下文件的本地副本:

https://github.com/NickQiZhu/d3-cookbook/blob/master/src/chapter10/zoom.html

在本食谱中,将使用D3缩放支持来实现几何缩放和平移。让我们看看如何在代码中完成此操作:

<script type="text/javascript">
var width = 960, height = 500, r = 50;

var data = [
    [width / 2 - r, height / 2 - r],
    [width / 2 - r, height / 2 + r],
    [width / 2 + r, height / 2 - r],
    [width / 2 + r, height / 2 + r]
];

var svg = d3.select("body").append("svg")
        .attr("width", width)
        .attr("height", height)
        .call(
            d3.behavior.zoom()
                .scaleExtent([1, 10])
                .on("zoom", zoom)
        )
        .append("g");

svg.selectAll("circle")
        .data(data)
        .enter().append("circle")
        .attr("r", r)
        .attr("transform", function (d) {
            return "translate(" + d + ")";
        });

function zoom() {
    svg.attr("transform", "translate(" 
        + d3.event.translate 
        + ")scale(" + d3.event.scale + ")");
}
</script>

此食谱可产生以下缩放和平移效果:

原文:

Original

缩放:

Zoom

Pan:

Pan

您会发现缩放和平移对鼠标滚轮和多点触摸手势的反应都很好(与其他答案相反)。大部分繁重的工作都由D3库完成,因此我们只需要很少的代码。