无论 viewBox 缩放如何,都保持相同的字体大小文本

Phr*_*ogz 6 javascript css svg

我有一个交互式 SVG 图表,它几乎完全不同于这个简单的 demo\xe2\x80\xa6

\n\n

演示: http: //jsfiddle.net/CphxH/6/

\n\n

\xe2\x80\xa6除了:

\n\n
    \n
  • 有一堆<text>元素,
  • \n
  • 动态调整viewBox, 和
  • \n
  • 我希望无论 viewBox 有多大,屏幕上看到的文本的字体大小都是相同的。
  • \n
\n\n

理想情况下,我想要相当于vector-effect:non-scaling-stroke(至少在 Webkit 上),但对于font-size. 这样的事情存在吗?如果不是,我最好的选择是计算 viewBox 大小,将其与 结合起来aspectRatio以确定较大的轴,然后使用 JS 操作font-size属性的 CSS 规则?

\n

Phr*_*ogz 1

这是一个基于 JavaScript 的解决方案。您recordSVGFontSizes()在开始时调用一次,向其传递一个 SVG 元素和一个样式表。它计算 SVG 的视口,并运行样式表并查找提及font-size和记录原始字体大小的所有规则。

然后,当您的 viewBox 发生变化时,将记录的值传递给normalizeSVGFontSizes(). 它将重新计算视口并适当更新所有字体大小。

请注意,由于视口计算当前不适用于 Firefox,因此该脚本不适用于 Firefox。

演示: 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};
  }
}
Run Code Online (Sandbox Code Playgroud)