سؤال

I have an interactive SVG diagram that is almost completely unlike this simple demo…

Demo: http://jsfiddle.net/CphxH/6/

…except insofar as it:

  • has a bunch of <text> elements,
  • dynamically adjusts the viewBox, and
  • I'd like the font size of the text seen on screen to be the same regardless of how big the viewBox is.

Ideally I'd like the equivalent of vector-effect:non-scaling-stroke (at least on Webkit), but for font-size. Does such a thing exist? If not, is my best bet to calculate the viewBox size, combine it with the aspectRatio to determine the larger axis, and then use JS to manipulate CSS rules for the font-size property?

هل كانت مفيدة؟

المحلول 2

Here is a JavaScript-based solution. You call recordSVGFontSizes() once at the beginning, passing it an SVG element and a StyleSheet. It calculates the viewport for the SVG, and runs through the stylesheet and finds all rules that mention font-size and record the original font size.

Then, when your viewBox changes, pass the recorded values to normalizeSVGFontSizes(). It will re-calculate the viewport and update all font sizes appropriately.

Note that since the viewport calculation does not currently work for Firefox, this script does not work there.

Demo: http://jsfiddle.net/CphxH/8/

// Do this once at startup
var recording = recordSVGFontSizes( mySVG, document.styleSheets[0] );

// Do this each time the viewport changes
normalizeSVGFontSizes(recording);

function recordSVGFontSizes(svgElement,styleSheet){
  var rules = [];
  var viewport = calculateViewport(svgElement);
  for (var i=styleSheet.cssRules.length;i--;){
    var rule = styleSheet.cssRules[i];
    if (rule.style.fontSize){
      var parts = rule.style.fontSize.split(/(\D+)/);
      rules.push({ rule:rule, units:parts[1], perpx:parts[0] / viewport.width });
    }
  }
  return {rules:rules, svg:svgElement};
}

function normalizeSVGFontSizes(fontSizeRecording){
  var viewport = calculateViewport(fontSizeRecording.svg);
  for (var i=fontSizeRecording.rules.length;i--;){
    var record = fontSizeRecording.rules[i];
    record.rule.style.fontSize = record.perpx*viewport.width + record.units;
  }
}

// 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 outer    = svg.getBoundingClientRect();
  var aspect   = svg.preserveAspectRatio.baseVal,
      viewBox  = svg.viewBox.baseVal,
      width    = viewBox && viewBox.width  || outer.width,
      height   = viewBox && viewBox.height || outer.height,
      x        = viewBox ? viewBox.x : 0,
      y        = viewBox ? viewBox.y : 0;
  if (!width || !height || !outer.width) 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 = outer.width / outer.height;
    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};
  }
}

نصائح أخرى

No, there is not any automatic way to keep the text size constant if you change the viewBox. You would have to adjust the text size with Javascript as you thought.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top