Search code examples
svgintersection

Finding whether an SVG element is visible in the viewport


Say I have some SVG that looks like the following. #canvas will have some transforms applied:

<svg id="viewport" x="0" y="0" width="100%" height="100%">
    <g id="canvas" transform="scale(0.17)">
        <image class="imageTile" x="0" y="0" width="256" height="256"/>
        <image class="imageTile" x="256" y="0" width="256" height="256"/>
        <image class="imageTile" x="0" y="256" width="256" height="256"/>
        <image class="imageTile" x="256" y="256" width="256" height="256"/>
    </g>
</svg>

#canvas will be draggable, so when I drag a .imageTile into view, I will fire off a download.

var tiles = Y.all(".imageTile");
for (var i = 0; i < tiles.size(); i++) {
    if (the tile is visible - ??) {
        // set its xlink:href attribute
    }
}

I know that getScreenCTM() will get me an SVGMatrix object, but I don't know how to use that to calculate the intersection. I'm also aware of the SVGElement.getIntersectionList() method, but it seems to have some browser compatibility issues. Is there a better way?


Solution

  • With the help of Mr. B. Campin's SVG Open paper, I figured it out:

    var tile; // this is your SVG tile node
    
    var svgroot = tile.ownerSVGElement;
    var scale = svgroot.currentScale;
    var vbParts = svgroot.getAttribute("viewBox").split(" ");
    var vbx = parseInt(vbParts[0]);
    var vby = parseInt(vbParts[1]);
    var vbxu = parseInt(vbParts[2]);
    var vbyu = parseInt(vbParts[3]);
    var tx = svgroot.currentTranslate.x;
    var ty = svgroot.currentTranslate.y;
    var svgWidth = parseInt(svgroot.getAttribute("width"));
    var svgHeight = parseInt(svgroot.getAttribute("height"));
    var svgzoomfactor = vbxu / svgWidth;
    
    var vpRect = svgroot.createSVGRect();
    vpRect.x = parseFloat(vbx + (-tx) * (svgzoomfactor) / scale);
    vpRect.y = parseFloat(vby + (-ty) * (svgzoomfactor) / scale);
    vpRect.width = parseFloat(svgWidth * svgzoomfactor / scale);
    vpRect.height = parseFloat(svgHeight * svgzoomfactor / scale);
    
    if (svgroot.checkIntersection(tile, vpRect)) {
        // the tile intersects the viewport!
    }