动态添加到 svg-pan-zoom 元素的子项不会 pan/zoom,但之前添加的项目会

Dynamically added children to svg-pan-zoom element do not pan/zoom, but previously added items do

我正在使用 javascript 创建一个 svg 对象,绘制一个基本的 Sierpinski 三角形,然后将 svg 对象设置为 svg-pan-zoom。当缩放超过阈值时,我尝试重新绘制下一级三角形,我可以在屏幕上看到它们,但它们对缩放或平移没有反应。

我目前正在尝试在绘制下一层三角形后将 svg-pan-zoom 重新应用到对象,但这似乎不起作用。

HTML基本上是一个空体

有问题的JS代码:

var width = 300;
var height = 300;
var length;
var maxDepth = 5;
var depth = 0;
var svg;
var div;
//2D array of triangles, where first index is their recursive depth at an offset
var triangles;
var zoomCount = 0;

$(document).ready(function () {
    //init();



    var ns = 'http://www.w3.org/2000/svg'
    div = document.getElementById('drawing')
    svg = document.createElementNS(ns, 'svg')
    svg.setAttributeNS(null, 'id', 'svg-id')
    svg.setAttributeNS(null, 'width', '100%')
    svg.setAttributeNS(null, 'height', '100%')
    div.appendChild(svg)

    /*var rect = document.createElementNS(ns, 'rect')
    rect.setAttributeNS(null, 'width', 100)
    rect.setAttributeNS(null, 'height', 100)
    rect.setAttributeNS(null, 'fill', '#f06')
    svg.appendChild(rect)*/

    init();

    enableZoomPan();

});

function init() {

    triangles = create2DArray(5);

    //Calculate triangle line length
    length = height/Math.sin(60*Math.PI/180)    //Parameter conversion to radians

    t = new Triangle(new Point(0, height), new Point(width, height), new Point(width/2, 0));
    sketchTriangle(t);
    //Recursively draw children triangles
    drawSubTriangles(t, true);
    attachMouseWheelListener();

}

function enableZoomPan() {
    //Enable zoom/pan
    var test = svgPanZoom('#svg-id', {
        zoomEnabled: true,
        controlIconsEnabled: false,
        fit: true,
        center: true,
        minZoom: 0
    });
}

function attachMouseWheelListener() {
    if (div.addEventListener)
    {
        // IE9, Chrome, Safari, Opera
        div.addEventListener("mousewheel", MouseWheelHandler, false);
        // Firefox
        div.addEventListener("DOMMouseScroll", MouseWheelHandler, false);
    }
// IE 6/7/8
    else
    {
        div.attachEvent("onmousewheel", MouseWheelHandler);
    }

    function MouseWheelHandler(e)
    {
        // cross-browser wheel delta
        var e = window.event || e; // old IE support
        //Delta +1 -> scrolled up
        //Delta -1 -> scrolled down
        var delta = Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail)));
        console.log(delta);

        zoomCount = zoomCount + delta;

        if(zoomCount==15) {

            for(var i = 0; i < triangles[0].length; i++) {
                drawSubTriangles(triangles[0][i], false);
            }
            enableZoomPan();
        }

        return false;
    }
}


function drawLine(p1, p2) {

    var line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
    line.setAttribute('x1', p1.x);
    line.setAttribute('y1', p1.y);
    line.setAttribute('x2', p2.x);
    line.setAttribute('y2', p2.y);
    line.setAttribute('stroke', "black");
    line.setAttribute('stroke-width', 1);
    svg.appendChild(line);

}

//Recursive parameter if you want to draw recursively, or just stop after one level
function drawSubTriangles(t, recursive) {
    //End condition, bounded by maximum recursion depth
    if(depth == maxDepth && recursive==true) {
        //Push triangle to depth collection, to track in case zooming in and redrawing
        triangles[maxDepth-depth].push(t);
        return;
    }

    depth = depth + 1;

    //Sub triangle lengths are half of parent triangle
    subLength = length/depth;

    var midPoint1 = getCenter(t.p1, t.p2);
    var midPoint2 = getCenter(t.p2, t.p3);
    var midPoint3 = getCenter(t.p3, t.p1);

    midTriangle = new Triangle(midPoint1, midPoint2, midPoint3);

    sketchTriangle(midTriangle)

    //Recursive call to continue drawing children triangles until max depth
    if(recursive == true) {
        drawSubTriangles(new Triangle(t.p1, midPoint1, midPoint3), true);
        drawSubTriangles(new Triangle(midPoint3, midPoint2, t.p3), true);
        drawSubTriangles(new Triangle(midPoint1, t.p2, midPoint2), true);
    }
    depth = depth -1;
}



function sketchTriangle(t) {
    drawLine(t.p1, t.p2);
    drawLine(t.p2, t.p3);
    drawLine(t.p3, t.p1);
}

function create2DArray(rows) {
    var arr = [];

    for (var i=0;i<rows;i++) {
        arr[i] = [];
    }

    return arr;
}

function getCenter(p1, p2) {
    return new Point((p1.x + p2.x)/2, (p1.y + p2.y)/2);
}

function Point(x, y) {
    this.x = x;
    this.y = y;
}

function Triangle(p1, p2, p3) {
    this.p1 = p1;
    this.p2 = p2;
    this.p3 = p3;
}

出于某种原因,svg-pan-zoom 将所有 SVG 内容包装在 <g> 元素中。然后通过修改与此元素关联的 transform 属性来实现平移和缩放。所以实际上,当你第一次与 SVG 交互时,它的结构会发生变化:

<svg>
  <line ...>
  <line ...>
  ...
</svg>

对此:

<svg>
  <g transform="matrix(1 0 0 1 0 0)">
    <line ...>
    <line ...>
    ...
  </g>
</svg>

当您向绘图添加更多元素时,它们将附加到根 <svg> 元素。结果,它们根本没有被转换。

你可以很容易地解决这个问题。在调用 svg-pan-zoom 之前,只需将您的绘图包裹在 <g> 元素中。然后它将使用这个元素而不是添加自己的元素。添加到绘图时,将新对象附加到此元素。

这是您的代码,稍作修改:

var width = 300;
var height = 300;
var length;
var maxDepth = 5;
var depth = 0;
var svg;
var svgg;  /* Top <g> element inside svg */
var div;

//2D array of triangles, where first index is their recursive depth at an offset
var triangles;
var zoomCount = 0;

$(document).ready(function () {
    //init();

    var ns = 'http://www.w3.org/2000/svg'
    div = document.getElementById('drawing')
    svg = document.createElementNS(ns, 'svg')
    svg.setAttributeNS(null, 'id', 'svg-id')
    svg.setAttributeNS(null, 'width', '100%')
    svg.setAttributeNS(null, 'height', '100%')
    svgg = document.createElementNS(ns, 'g')
    svg.appendChild(svgg)
    div.appendChild(svg)

    init();
    enableZoomPan();
});

function init() {

    triangles = create2DArray(5);

    //Calculate triangle line length
    length = height/Math.sin(60*Math.PI/180)    //Parameter conversion to radians

    t = new Triangle(new Point(0, height), new Point(width, height), new Point(width/2, 0));
    sketchTriangle(t);
    //Recursively draw children triangles
    drawSubTriangles(t, true);
    attachMouseWheelListener();

}

function enableZoomPan() {
    //Enable zoom/pan
    var test = svgPanZoom('#svg-id', {
        zoomEnabled: true,
        controlIconsEnabled: false,
        fit: true,
        center: true,
        minZoom: 0
    });
}

function attachMouseWheelListener() {
    if (div.addEventListener)
    {
        // IE9, Chrome, Safari, Opera
        div.addEventListener("mousewheel", MouseWheelHandler, false);
        // Firefox
        div.addEventListener("DOMMouseScroll", MouseWheelHandler, false);
    }
// IE 6/7/8
    else
    {
        div.attachEvent("onmousewheel", MouseWheelHandler);
    }

    function MouseWheelHandler(e)
    {
        // cross-browser wheel delta
        var e = window.event || e; // old IE support
        //Delta +1 -> scrolled up
        //Delta -1 -> scrolled down
        var delta = Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail)));
        zoomCount = zoomCount + delta;
        if(zoomCount==15) {
            for(var i = 0; i < triangles[0].length; i++) {
                drawSubTriangles(triangles[0][i], false);
            }
            enableZoomPan();
        }
        return false;
    }
}


function drawLine(p1, p2) {

    var line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
    line.setAttribute('x1', p1.x);
    line.setAttribute('y1', p1.y);
    line.setAttribute('x2', p2.x);
    line.setAttribute('y2', p2.y);
    line.setAttribute('stroke', "black");
    line.setAttribute('stroke-width', 1);
    svgg.appendChild(line);

}

//Recursive parameter if you want to draw recursively, or just stop after one level
function drawSubTriangles(t, recursive) {
    //End condition, bounded by maximum recursion depth
    if(depth == maxDepth && recursive==true) {
        //Push triangle to depth collection, to track in case zooming in and redrawing
        triangles[maxDepth-depth].push(t);
        return;
    }

    depth = depth + 1;

    //Sub triangle lengths are half of parent triangle
    subLength = length/depth;

    var midPoint1 = getCenter(t.p1, t.p2);
    var midPoint2 = getCenter(t.p2, t.p3);
    var midPoint3 = getCenter(t.p3, t.p1);

    midTriangle = new Triangle(midPoint1, midPoint2, midPoint3);

    sketchTriangle(midTriangle)

    //Recursive call to continue drawing children triangles until max depth
    if(recursive == true) {
        drawSubTriangles(new Triangle(t.p1, midPoint1, midPoint3), true);
        drawSubTriangles(new Triangle(midPoint3, midPoint2, t.p3), true);
        drawSubTriangles(new Triangle(midPoint1, t.p2, midPoint2), true);
    }
    depth = depth -1;
}



function sketchTriangle(t) {
    drawLine(t.p1, t.p2);
    drawLine(t.p2, t.p3);
    drawLine(t.p3, t.p1);
}

function create2DArray(rows) {
    var arr = [];

    for (var i=0;i<rows;i++) {
        arr[i] = [];
    }

    return arr;
}

function getCenter(p1, p2) {
    return new Point((p1.x + p2.x)/2, (p1.y + p2.y)/2);
}

function Point(x, y) {
    this.x = x;
    this.y = y;
}

function Triangle(p1, p2, p3) {
    this.p1 = p1;
    this.p2 = p2;
    this.p3 = p3;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ariutta.github.io/svg-pan-zoom/dist/svg-pan-zoom.js"></script>
<div id="drawing"></div>


注意: 我不明白的一件事是为什么 svg-pan-zoom 不只是修改 SVG viewBox 属性。那么根本就不用修改文档结构了。