Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Determining the SVG viewport in global/root coordinates

Tags:

javascript

svg

The content actually visible inside an SVG element depends on its aspect ratio, its viewBox value, and its preserveAspectRatio value.

For example, the same SVG element size with the same viewBox value (dotted red below) can have different viewports (shown in blue below) depending on the preserveAspectRatio:

            pictoral examples of varying preserveAspectRatio settings and the resulting viewboxes

  • Chrome has a viewport property, but in my uses it's always an empty (all 0-valued) SVGRect. Firefox does not implement this property.

  • The svg.getBBox() method returns the value of the viewBox, not the actual visible viewport content.

Given a reference to a SVG element, how can I most easily determine the visible content (the viewport), expressed in the user coordinates at the root of the SVG (the same coordinates as the viewBox)?

like image 570
Phrogz Avatar asked May 14 '14 20:05

Phrogz


1 Answers

Here's a function that works for Chrome, Safari, and Firefox. See the test page here:

Test Page: http://phrogz.net/SVG/CalculateSVGViewport.html

// Given an <svg> element, returns an object with the visible bounds
// expressed in local viewBox units, e.g.
// { x:-50, y:-50, width:100, height:100 }
function calculateViewport(svg){ // http://phrogz.net/JS/_ReuseLicense.txt
  var style    = getComputedStyle(svg),
      owidth   = parseInt(style.width,10),
      oheight  = parseInt(style.height,10),
      aspect   = svg.preserveAspectRatio.baseVal,
      viewBox  = svg.viewBox.baseVal,
      width    = viewBox && viewBox.width  || owidth,
      height   = viewBox && viewBox.height || oheight,
      x        = viewBox ? viewBox.x : 0,
      y        = viewBox ? viewBox.y : 0;
  if (!width || !height || !owidth || !oheight) return;
  if (aspect.align==aspect.SVG_PRESERVEASPECTRATIO_NONE || !viewBox || !viewBox.height){
    return {x:x,y:y,width:width,height:height};
  }else{
    var inRatio  = viewBox.width / viewBox.height,
        outRatio = owidth / oheight;
    var meetFlag = aspect.meetOrSlice != aspect.SVG_MEETORSLICE_SLICE;
    var fillAxis = outRatio>inRatio ? (meetFlag?'y':'x') : (meetFlag?'x':'y');
    if (fillAxis=='x'){
      height = width/outRatio;
      var diff = viewBox.height - height;
      switch (aspect.align){
        case aspect.SVG_PRESERVEASPECTRATIO_UNKNOWN: 
        case aspect.SVG_PRESERVEASPECTRATIO_XMINYMID:
        case aspect.SVG_PRESERVEASPECTRATIO_XMIDYMID:
        case aspect.SVG_PRESERVEASPECTRATIO_XMAXYMID:
          y += diff/2;
        break;
        case aspect.SVG_PRESERVEASPECTRATIO_XMINYMAX:
        case aspect.SVG_PRESERVEASPECTRATIO_XMIDYMAX:
        case aspect.SVG_PRESERVEASPECTRATIO_XMAXYMAX:
          y += diff;
        break;
      }
    }
    else{
      width = height*outRatio;
      var diff = viewBox.width - width;
      switch (aspect.align){
        case aspect.SVG_PRESERVEASPECTRATIO_UNKNOWN: 
        case aspect.SVG_PRESERVEASPECTRATIO_XMIDYMIN:
        case aspect.SVG_PRESERVEASPECTRATIO_XMIDYMID:
        case aspect.SVG_PRESERVEASPECTRATIO_XMIDYMAX:
          x += diff/2;
        break;
        case aspect.SVG_PRESERVEASPECTRATIO_XMAXYMID:
        case aspect.SVG_PRESERVEASPECTRATIO_XMAXYMIN:
        case aspect.SVG_PRESERVEASPECTRATIO_XMAXYMAX:
          x += diff;
        break;
      }
    }
    return {x:x,y:y,width:width,height:height};
  }
}
like image 163
Phrogz Avatar answered Sep 26 '22 02:09

Phrogz