JavaScript缩放文本以适应固定的Div

58
在JavaScript/jQuery中,我该如何缩放可变长度的文本行以适应固定宽度的Div,以便一行始终适合Div(作为一行)?

是的,我希望它能够根据文本中的字母数量自动调整字体大小,以便始终适合Div中的一行。 - Stephen Watkins
可能与https://dev59.com/JFHTa4cB1Zd3GeqPUL1h相同吗? - Mic
@StephenWatkins 我知道这是一个旧问题,但我建议您查看我的答案。 - Hoffmann
https://github.com/rikschennink/fitty - Lance
20个回答

72

这有点像是一种hack,但可以实现你想要的功能。

<div id="hidden-resizer" style="visibility: hidden"></div>

将此代码放置在页面底部,以确保它不会影响页面上的其他元素。

然后执行以下操作:

var size;
var desired_width = 50;
var resizer = $("#hidden-resizer");

resizer.html("This is the text I want to resize.");

while(resizer.width() > desired_width) {
  size = parseInt(resizer.css("font-size"), 10);
  resizer.css("font-size", size - 1);
}

$("#target-location").css("font-size", size).html(resizer.html());

16
很好的回答,+1。一个改进是使用jQuery添加#hidden-resizer div,以避免弄乱HTML的语义。$('<div />', {id: 'hidden-resizer'}).hide().appendTo(document.body); - lonesomeday
5
这太棒了。我也喜欢人们证明事情并非不可能 ;-) - Stephen Watkins
8
这种方法非常慢,每次元素的字体大小改变时都会重新绘制该元素,如果您经常使用它,这可能会导致用户浏览器冻结。我建议查看我的答案和我的jQuery插件,以更好地解决这个问题: https://github.com/DanielHoffmann/jquery-bigtext - Hoffmann
2
-1 表示错误的 HTML 元素。你应该使用 <span> 而不是 <div>。 <span> 的宽度将始终等于其内部单词的实际宽度。如果未定义,<div> 的宽度将始终等于 <body> 的宽度,而不考虑 <div> 内部单词的实际宽度。请使用 <span id="hidden-resizer" style="display: none;"></span>。 - Kawd
不错的回答!但是如果我想调整某些东西的大小,有时它比所需宽度小(有时更大),该怎么办? - frankelot
显示剩余3条评论

32

HTML:

<div class="box" style="width:700px">This is a sentence</div>
<div class="box" style="width:600px">This is a sentence</div>
<div class="box" style="width:500px">This is a sentence</div>
<div class="box" style="width:400px">This is a sentence</div>

JavaScript:

$( '.box' ).each(function ( i, box ) {

    var width = $( box ).width(),
        html = '<span style="white-space:nowrap"></span>',
        line = $( box ).wrapInner( html ).children()[ 0 ],
        n = 100;

    $( box ).css( 'font-size', n );

    while ( $( line ).width() > width ) {
        $( box ).css( 'font-size', --n );
    }

    $( box ).text( $( line ).text() );

});

演示: http://jsfiddle.net/e8B9j/2/show/

从URL中删除“/show/”即可查看代码。


1
如果您的代码出现问题,$(line).width() 对我总是返回0。 - Frederick Behrends
3
不使用while循环,直接计算比例:$(box).css('font-size', width/$(line).width()*100-1); http://jsfiddle.net/e8B9j/411/ - teambob
一直在为fittextjs、bigtext.js等插件苦恼,但这个简直太好用了! - Cheong D Mun Pong
最后一行代码是做什么的?看起来它在追加文本,但显然已经将其附加到元素中,那么它为什么还在这里?编辑:好的,我明白了,它在这里是为了去掉 span 包装元素。 - Max Yari
非常好。我将n的起始值更改为$(box).height(),因为我的盒子也有限制高度。否则,短字符串会变得太大而无法适应盒子。 - Christian
显示剩余10条评论

14

我编写了一个jQuery插件来完成这个任务:

http://michikono.github.com/boxfit

该插件会将文本在水平和垂直方向上缩放到盒子内的最大可用大小,然后居中显示。

你需要做的唯一一件事就是定义一个带有文本内容的div:

<div id="scale">some text</div>

然后调用:

$('#scale').boxfit()

该方法将接受一些参数来禁用/启用文本换行和居中对齐。


9

对于新的浏览器,您可以使用Inline-SVG。这可以像通过CSS缩放图像一样缩放。

.smallerVersion{
max-width: 50%
}
<!DOCTYPE html>
<html>
<head>
<title>Scale SVG example</title>
</head>

<body>





<h2>Default version:</h2>
<svg viewBox="0 0 240 80" xmlns="http://www.w3.org/2000/svg">
  <style>
    .small { font: italic 13px sans-serif; }
    .heavy { font: bold 30px sans-serif; }

    /* Note that the color of the text is set with the    *
     * fill property, the color property is for HTML only */
    .Rrrrr { font: italic 40px serif; fill: red; }
  </style>

  <text x="20" y="35" class="small">My</text>
  <text x="40" y="35" class="heavy">cat</text>
  <text x="55" y="55" class="small">is</text>
  <text x="65" y="55" class="Rrrrr">Grumpy!</text>
</svg>

<h2>Scaled version:</h2>
<svg class="smallerVersion" viewBox="0 0 240 80" xmlns="http://www.w3.org/2000/svg">
  <style>
    .small { font: italic 13px sans-serif; }
    .heavy { font: bold 30px sans-serif; }

    /* Note that the color of the text is set with the    *
     * fill property, the color property is for HTML only */
    .Rrrrr { font: italic 40px serif; fill: red; }
  </style>

  <text x="20" y="35" class="small">My</text>
  <text x="40" y="35" class="heavy">cat</text>
  <text x="55" y="55" class="small">is</text>
  <text x="65" y="55" class="Rrrrr">Grumpy!</text>
</svg>




</body>

</html>

(width: 100%;)


7
大多数其他答案使用循环来减小字体大小,直到它适合div,这非常慢,因为页面需要每次改变字体大小重新渲染元素。我最终不得不编写自己的算法,以使其以一种允许我定期更新其内容而不冻结用户浏览器的方式执行。我添加了一些其他功能(旋转文本,添加填充)并将其打包为jQuery插件,您可以在以下位置获取它:

https://github.com/DanielHoffmann/jquery-bigtext

简单地调用

$("#text").bigText();

它将完美地适配您的容器。

在此处查看其实际效果:

http://danielhoffmann.github.io/jquery-bigtext/

目前它有一些限制,div必须具有固定的高度和宽度,并且不支持将文本换行为多行。

编辑2:我现在已经解决了这些问题和限制,并添加了更多选项。您可以设置最大字体大小,还可以选择使用宽度、高度或两者(默认为两者)来限制字体大小。我将努力接受包装元素中的最大宽度和最大高度值。


5
我不知道确切的方法,但是这里有一个近似值:
var factor = 1/3;  // approximate width-to-height ratio
var div = $('#mydiv');
div.css('font-size', div.width() / (div.text().length * factor) + 'px');

根据您所使用的字体,您需要调整factor。对于 Times New Roman 字体,1/3 似乎可以正常工作。


3

支持目标类的修改版@sworoc

function resizeText(text, size, target) {

    var fontSize = 0;
    var resizer = $('<span>');

    resizer.html(text).hide();
    resizer.attr('class', target.attr('class'));
    $('body').append(resizer);

    while(resizer.width() < size) {
        fontSize++
        resizer.css("font-size", fontSize);
    }

    target.css('font-size', fontSize).html(text);
    resizer.remove();
}

使用方法

resizeText("@arunoda", 350, $('#username'));

2

在您的 div 中,将文本放在一个没有填充的 span 中。然后,span 的宽度将是文本的长度。
用于查找正确字体大小的未经测试的代码:

var objSpan = $('.spanThatHoldsText');  
var intDivWidth = $('.divThatHasAFixedWidth').width();  
var intResultSize;  

for (var intFontSize = 1; intFontSize < 100; intFontSize++)  

  objSpan.css('font-size', intFontSize);  

  if (objSpan.width() > intDivWidth) {  
    intResultSize = intFontSize - 1;  
    break;  
  }  

}

objSpan.css('font-size', intResultSize);

有时候伟大的思想会相互吻合,你可以通过使用数字来设置字体大小,而我的方法是解析字体大小的值。不确定哪种方法更可靠。 - sworoc
如果它也检查高度就更好了。http://jsfiddle.net/ad5pf/1/ 如果 (objSpan.width() > intDivWidth || objSpan.height() > intDivHeight) {
intResultSize = intFontSize - 1;
break;
}
- user566245

2
这里是我想出的一个coffeescript解决方案。我克隆需要调整其字体大小的元素,然后在计算结束时删除该元素。为了提高性能,我编写了一些代码,只有在200ms停止调整大小后才执行计算。
将class属性标记为autofont的元素可以不影响其他操作地完成这个过程。
# 
# Automagically resize fonts to fit the width of the 
# container they are in as much as possible. 
#
fixfonts = ->

  scaler = 1.2

  for element in $('.autofont')

    size =  1
    element = $(element)
    desired_width = $(element).width()

    resizer = $(element.clone())
    resizer.css
      'font-size': "#{size}em"
      'max-width': desired_width
      'display': 'inline'
      'width': 'auto'
    resizer.insertAfter(element)

    while resizer.width() < desired_width
      size = size * scaler
      resizer.css
        'font-size':  "#{size}em"

    $(element).css
        'font-size': "#{size / scaler }em"

    resizer.remove()

$(document).ready =>
  fixfonts()

  timeout = 0
  doresize = ->
    timeout--
    if timeout == 0
      fixfonts()

  $(window).resize ->
    timeout++
    window.setTimeout doresize, 200

超时代码是个好主意,但有更好的方法。timeoutId = null; $(window).resize -> { window.clearTimeout(timeoutId); timeoutId = window.setTimeout fixfonts, 200; } - Peter Taylor

1

我不知道如何将这个添加到sworoc的帖子中,但我还是想分享一下: 如果您正在使用任何形式的AJAX导航,则Lonesomeday的解决方案会出现问题。 我稍微修改了一下:

if ($('#hidden-resizer').length == 0){ 
    $('<div />', {id: 'hidden-resizer'}).hide().appendTo(document.body); 
}

1
你应该将它作为答案的另一个评论发布。 - Nightfirecat
@Nightfirecat:deweydb的声望不足以执行该操作。 - Robert Harvey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接