How can I scale arbitrary text to always fit the v

2019-09-19 11:17发布

A site I'm busy working on has a section with some very large headings. There's something I'm not sure how to handle:

The heading may be one two short or long words, e.g: "Cyprus" to "Nouvelle Zelande", and it must scale to be roughly the width of the viewport. That means "Cyprus", being shorter, will have larger individual characters than longer text than "Nouvelle Zelande".

This would be relatively easy to do with JavaScript, I think, but I'd like to go for a pure HTML/CSS solution. So: how can I scale text to fit the width of the viewport? So far, I'm stumped and not sure how to do it, myself.

Some details:

  • You only need to target the most recent version of each browser, which includes IE11.
  • You may use any and all HTML5 and CSS3 that works within those browsers.
  • It's okay if you make the text "Nouvelle Zelande" word-wrap, as long as the longer of the two words still roughly fits to the width available.
  • You may add extra elements inside/around the headings.

Note that viewport units are not a solution. Previous questions asking about this (Pure CSS to make font-size responsive based on dynamic amount of characters, Font scaling based on width of container) have answers of "use viewport units, like vw!", but that doesn't handle this scenario at all, and astute readers even pointed this out. I've even used vw in the code sample below to demonstrate its non-solution-ness. It'll size based on the viewport just fine, but won't do any sizing based on the amount of text.

Code sample

h2 {
  font-family: sans-serif;
  text-transform: uppercase;
  font-size: 14vw;
  white-space: nowrap;
  overflow-x: hidden;
  margin: 0;
}
<h2>Nouvelle Zelande</h2>
<h2>Australia</h2>
<h2>Cyprus</h2>

2条回答
时光不老,我们不散
2楼-- · 2019-09-19 11:40

The only unit, if being used to set font size, that is relative to the size of its container, is viewport units vw/vh, which will not solve your case alone, even if the container is the same width as the viewport, since it does not calc the letter size to fit into the container.

The closest non-script solution I can come up with is to use the CSS element counter trick, and wrap each letter in a span

The 130vw I set here, worked best for the given font, though this might need to be adjusted based on which font family is being used.

h2 {
  display: inline-block;
  font-family: sans-serif;
  text-transform: uppercase;
  white-space: nowrap;
  overflow-x: hidden;
  margin: 0;
}

/* 1 letter */
h2 span:first-child:nth-last-child(1) {
    font-size: 130vw;
}
/* skipped 2-5 in this demo */

/* 6 letters */
h2 span:first-child:nth-last-child(6),
h2 span:first-child:nth-last-child(6) ~ span {
    font-size: calc(130vw / 6);
}

/* skipped 7-14 in this demo */

/* 15 letters */
h2 span:first-child:nth-last-child(15),
h2 span:first-child:nth-last-child(15) ~ span {
    font-size: calc(130vw / 15);
}
<h2><span>N</span><span>o</span><span>u</span><span>v</span><span>e</span><span>l</span><span>l</span><span>e</span> &nbsp;<span>Z</span><span>e</span><span>l</span><span>a</span><span>n</span><span>d</span><span>e</span></h2><br>
<h2><span>C</span><span>y</span><span>p</span><span>r</span><span>u</span><span>s</span></h2>


Here is the same concept using a script, and without the span's

(function (d,t) {
  window.addEventListener("resize", throttler, false);
  window.addEventListener("load", throttler(), false);  /*  run once on load to init  */
  
  function throttler() {
    if ( !t ) {
      t = setTimeout(function() {
        t = null;
        keepTextFit(d.querySelectorAll('h2'));
       }, 66);
    }
  }
  function keepTextFit(el) {
    var f = el[0].getAttribute("data-font");
    for (var i = 0; i < el.length; i++) {
      var c = el[i].textContent.split('').length;      
      el[i].style.cssText = 
        'font-size: calc(' + f + ' / ' + c + ')';
    }
  }
})(document,null);
h2 {
  display: inline-block;
  font-family: sans-serif;
  text-transform: uppercase;
  white-space: nowrap;
  overflow-x: hidden;
  margin: 0;
}
<h2 data-font="130vw">Nouvelle Zelande</h2>
<h2>Australia</h2>
<h2>Cyprus</h2>

Note, since resize events can fire at a high rate, the throttler is used to reduced the rate so the handler doesn't execute expensive operations such as DOM modifications too often.


If you want to make a perfect fit, check this post: fit-text-perfectly-inside-a-div

查看更多
欢心
3楼-- · 2019-09-19 11:48

If you are looking to use a plugin there's

http://fittextjs.com/

wich can do that for you

查看更多
登录 后发表回答