简体   繁体   English

缩放固定宽度字体以适合一行上的常量字符数

[英]Scale fixed-width font to fit constant number of characters on a line

I have a websocket based terminal session. 我有一个基于websocket的终端会话。 I want the font size to increase to fill the div such that it is always 80 characters wide. 我希望增加字体大小以填充div,使其总是80个字符宽。 What's the best way of doing this? 这样做的最佳方法是什么? I'd love it if there was a CSS-only way of doing this, but I'm already using jQuery, so a vanilla javascript or jQuery solution would be good too. 我喜欢它,如果只有CSS这样做的方式,但我已经在使用jQuery,所以一个vanilla javascript或jQuery解决方案也会很好。

For browsers that support fractional font sizes or CSS text-rendering: geometricPrecision , you can get a fixed-width font to perfectly fit within any bounds. 对于支持小数字体大小或CSS text-rendering: geometricPrecision浏览器text-rendering: geometricPrecision ,您可以获得固定宽度的字体,以完全适合任何边界。

In this screenshot, the fourth line is exactly 80 characters: 在此屏幕截图中,第四行正好是80个字符:

在此输入图像描述

For browsers that don't support fractional font sizes , it is impossible to always make x characters fit within a given width. 对于不支持小数字体大小的浏览器,不可能始终使x字符适合给定的宽度。

Here's the same text with a 12px font: 这是12px字体的相同文本:

在此输入图像描述

… and with a 13px font: ...并使用13px字体:

在此输入图像描述

Using the 12px font, the longest line is 83 characters, so it's too small. 使用12px字体,最长的行是83个字符,所以它太小了。 Using the 13px font, the longest line is 77 characters, so it's too large. 使用13px字体,最长的行是77个字符,所以它太大了。

In this situation, you must shrink or widen the container to match the font: 在这种情况下,您必须缩小或加宽容器以匹配字体:

在此输入图像描述

The code below places an 80-character wide span in divs of varying widths, using a style of word-wrap: break-word; 下面的代码使用一种word-wrap: break-word;的方式在宽度不同的div中放置一个80个字符的宽跨度word-wrap: break-word; . It does a binary search for the best font size, using getClientRects() to determine upper and lower bounds for the binary search. 它使用getClientRects()来确定二进制搜索的上限和下限,从而对二进制搜索最佳字体大小。

In case the browser doesn't support fractional font sizes, it adjusts the container's width to match the font size. 如果浏览器不支持小数字体大小,它会调整容器的宽度以匹配字体大小。

The span is then removed. 然后移除跨度。

$('div').each(function() {
  var x= $('<span>'+Array(80).join('x')+'</span>').appendTo(this),
      lo= 0.1,
      hi= 50,
      mid;
  do {
    mid= (lo+hi)/2;
    $(this).css({
      fontSize: mid
    });
    if(x[0].getClientRects().length > 1) {
      hi= mid;
    }
    else {
      lo= mid;
    }
  } while(Math.abs(hi-lo)>0.001);
  while(x[0].getClientRects().length === 1) {
    $(this).css({
      width: '-=1'
    });
  }
  while(x[0].getClientRects().length === 2) {
    $(this).css({
      width: '+=1'
    });
  }
  x.remove();
});

Tested in Chrome, IE, Firefox, Safari, and Opera. 在Chrome,IE,Firefox,Safari和Opera中测试过。

Fiddle 小提琴

Here's a CSS-only example based on the8472 's answer: 这是一个基于CSS的例子,基于8472的答案:

div,textarea{
    width:100%;
    max-width: 100%;
    box-sizing: border-box;
    -moz-box-sizing: border-box;
    font-family:  "Lucida Console", Monaco, monospace;
    font-size: 1.99vw; /* The text overflows in safari using (int)2 */
    white-space: nowrap;
    overflow-x:hidden;

    /* styling */ 
    background:#09f;
    min-height: 100px;
    margin-bottom: 30px;
}

How does it work? 它是如何工作的?

To get the right font-size, divide 160 by the number of characters you want to fit per line, minus 0.01. 要获得正确的字体大小,请将160除以每行所需的字符数减去0.01。

http://jsfiddle.net/mb2L4mee/1/ http://jsfiddle.net/mb2L4mee/1/

Tested in Chrome, FF and safari. 在Chrome,FF和Safari中测试过。

One approach would be to calculate the size of a 80 char line for a fixed font-size and register a function on resize event to adjust the font-size based on the terminal size. 一种方法是计算固定font-size大小的80字符行font-size并在resize事件上注册函数以根据终端大小调整font-size大小。

<div id="terminal">
    <div class="text">
        Some text
    </div>
</div>

function scale(lineWith) { /* lineWidth based on 80char 10px font-size */
    var ratio = $('#terminal').width() / lineWith;
    $('.text').css('font-size', (10 * ratio) + 'px');
}
$(window).resize(function(){
    scale(lineWith)
});

Demo: http://jsfiddle.net/44x56zsu/ 演示: http//jsfiddle.net/44x56zsu/

This seems to adjust pretty good in Chrome (Versión 39.0.2171.99 m) 这似乎在Chrome中调整得非常好(Versión39.0.2171.99m)

But I don't think it's possible to adjust the font-size to exactly fit the container size for all browsers because decimal values are rounded. 但我不认为可以调整font-size以适合所有浏览器的容器大小,因为十进制值是四舍五入的。 Font size test: http://jsfiddle.net/ahaq49t1/ 字体大小测试: http//jsfiddle.net/ahaq49t1/

字体大小测试结果

This rounding issue in some browsers prevents to do an exact adjustment using font properties. 某些浏览器中的此舍入问题会阻止使用字体属性进行精确调整。

Within limits it might be possible in pure CSS. 在限制范围内,它可能在纯CSS中。 If you know the fraction of the viewport that the div will occupy then you can scale the font based on the view port width. 如果您知道div将占据视口的分数,则可以根据视图端口宽度缩放字体。

.terminal {font-size: 1vw;} would result in a font size (vertical) equivalent to 1% of the view port. .terminal {font-size: 1vw;}会导致字体大小(垂直)相当于视图端口的1%。 Since a monospace font also has a fixed aspect ratio that results in a width depending on the aspect ratio of the font and the view port size. 由于等宽字体也具有固定的宽高比,因此宽度取决于字体的宽高比和视口大小。

Not sure if this is the most elegant solution, and it needs a bit more polishing too. 不确定这是否是最优雅的解决方案,它还需要更多的抛光。 But you can measure the font. 但你可以测量字体。 Create a hidden measure bar to see how wide the font is in pixels. 创建一个隐藏的度量栏,以查看字体的宽度(以像素为单位)。 Fill it with the desired amount of characters (using 20 here for demonstration purposes): 填充所需的字符数量(此处使用20作为演示用途):

<span id="measureBar">12345678901234567890</span>

Keep increasing the font size of the measure bar and find the largest width that's still not wider than the div: 继续增加度量栏的字体大小,找到仍然不比div宽的最大宽度:

 $( function() { var $measureBar = $('#measureBar'); var $console = $('#console'); $measureBar.css('font-size', '1px'); for (i = 1; $measureBar.width() <= $console.width(); i++) { $measureBar.css('font-size', i + 'px'); } $console.css('font-size', (i - 1) + 'px'); } ) 
 #console { background-color: black; color: white; } #measureBar { display: none; } #measureBar, #console { font-family: courier; } 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <span id="measureBar">12345678901234567890</span> <div id="console">12345678901234567890 Text that should be twenty characters wide.</div> 

I was facing a very similar requirement and came up with a fairly straight forward solution. 我面临着非常相似的要求,并提出了一个相当直接的解决方案。 As soon as I get some down time, I'll be writing a jQuery plugin around this example code. 一旦我得到一些停机时间,我将围绕这个示例代码编写一个jQuery插件。

As was mentioned in some of the other answers here, the key is using em as your font-size unit. 正如在这里的一些其他答案中提到的,关键是使用em作为字体大小的单位。 Additionally, to achieve a similar layout in all (modern) browsers, for a solution that must cope with various fixed-width fonts, the ruler method is the most reliable approach that I have found. 此外,为了在所有(现代)浏览器中实现类似的布局,对于必须处理各种固定宽度字体的解决方案,标尺方法是我发现的最可靠的方法。

 function fixieRemeasure() { var offset = document.getElementById("fixie").offsetWidth, ruler = (document.getElementById("fixieRuler").offsetWidth * 1.01), fontSize = (offset / ruler); document.getElementById("fixie").style.fontSize = fontSize + "em"; } function fixieInit(columns) { document.getElementById("fixieRuler").innerText = new Array(columns + 1).join("X").toString(); fixieRemeasure(); } (function () { fixieInit(80); window.onresize = function () { fixieRemeasure(); }; } ()); 
 #fixie { padding: 0 2px 0 6px; font-size: 1em; background-color: #000; color: #0F0; } #fixieRuler { font-size: 1em; visibility: hidden; } 
 <code id="fixieRuler"></code> <div id="fixie"> <pre>+------------------------------------------------------------------------------+ | | | ,%%%, | | ,%%%` %==-- | | ,%%`( '| | | ,%%@ /\\_/ | | ,%.-"""--%%% "@@__ | | %%/ |__`\\ | | .%'\\ | \\ / // | | ,%' > .'----\\ | [/ | | < <<` || | | `\\\\\\ || | | )\\\\ )\\ | | ^^^jgs^^"""^^^^^^""^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | ASCII Art: http://www.chris.com/ascii/index.php?art=animals/horses | +------------------------------------------------------------------------------+</pre> </div> 

This is only a proof of concept and there are a few fairly obvious issues baked in. Mainly, the resize method is called continuously and that should be replaced with something like onResizeEnd and properly attached to the DOM prior to production use. 这只是一个概念证明,并且有一些相当明显的问题。主要是,resize方法被连续调用,应该替换为onResizeEnd之类的东西,并在生产使用之前正确附加到DOM。 To componentize, I'd use a class selector instead of an id, and would also dynamically insert/remove the ruler element on-the-fly. 为了组件化,我使用类选择器而不是id,并且还可以动态地插入/移除标尺元素。

Hope it helps. 希望能帮助到你。

UPDATE UPDATE

I created a new project, fixie.js on github , based on this example code. 我基于这个示例代码在github上创建了一个新项目fixie.js Still has a bit of work to be done in terms of performance and modularization, but it is works well in all browsers and I believe that it provides the simplest means to solve the issue with a minimal amount of boilerplate code. 在性能和模块化方面仍有一些工作要做,但它在所有浏览器中都运行良好,我相信它提供了最简单的方法,用最少量的样板代码来解决问题。

The only requirements are that you set a font-size to your pre elements in em, and provide them a class name with the format of 'fixie_ COLS ': 唯一的要求是为em中的pre元素设置font-size,并为它们提供格式为'fixie_ COLS '的类名:

<pre class="fixie_80">I'll be 80 columns</pre>

jQuery & webcomponent implementations to follow. 要遵循的jQuery和webcomponent实现。 It's MIT licensed and any contributions are welcome: https://github.com/dperish/fixie.js 这是麻省理工学院的许可,欢迎任何贡献: https//github.com/dperish/fixie.js

This can be done using pure css using the vw (viewport width), vh (viewport height), vmin (relative to width or height, whichever is smaller), vmax (relative to width or height, whichever is larger) length Units. 这可以使用纯css使用vw (视口宽度), vh (视口高度), vmin (相对于宽度或高度,取较小者), vmax (相对于宽度或高度,以较大者为准)长度单位来完成。

For browser compatibility you can check this out. 对于浏览器兼容性,您可以查看

For a simple tutorial check this out. 有关简单的教程,请查看此内容

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM