【问题标题】:Auto-size dynamic text to fill fixed size container自动调整大小的动态文本以填充固定大小的容器
【发布时间】:2010-10-15 20:06:38
【问题描述】:

我需要将用户输入的文本显示到固定大小的 div 中。我想要的是自动调整字体大小,以便文本尽可能地填充框。

所以 - 如果 div 是 400px x 300px。如果有人输入ABC,那么它的字体真的很大。如果他们输入一个段落,那么它将是一个小字体。

我可能希望从最大字体大小开始 - 可能是 32 像素,当文本太大而无法容纳容器时,缩小字体大小直到适合。

【问题讨论】:

  • 这可能是 HTML5/CSS3 中最令人惊奇的功能之一,无需 JS。
  • 我做了一些测量,改变了动态文本的长度和容器的大小,以确定哪种字体大小可以使文本完美契合。在做了一些回归分析之后,我想出了一个简单的数学函数,它会自动生成最佳字体大小。
  • 实际上,给出最佳字体大小的图形由 f(x) = g(letters) * (x / 1000)^n 给出,其中 g(x) 是简单的功能,n 根据您使用的字体而有所不同。 (尽管它可以对所有字体都有一个标准值,但如果您不想对其进行调整以使其绝对完美......)。 x 是容器的平方像素大小。
  • 如果你还有兴趣,我可以加一个答案。就我个人而言,我认为首先生成正确的字体大小是一种更好的方法,而不是在脚本“正确”之前尝试并失败。
  • 检查我的答案以获得更好的方法

标签: jquery html css


【解决方案1】:

尽管我很喜欢这个答案偶尔得到的支持(谢谢!),但这确实不是解决这个问题的最佳方法。请在此处查看其他一些精彩的答案,尤其是那些在没有循环的情况下找到解决方案的答案。


不过,为了参考,这是我的原始答案

<html>
<head>
<style type="text/css">
    #dynamicDiv
    {
    background: #CCCCCC;
    width: 300px;
    height: 100px;
    font-size: 64px;
    overflow: hidden;
    }
</style>

<script type="text/javascript">
    function shrink()
    {
        var textSpan = document.getElementById("dynamicSpan");
        var textDiv = document.getElementById("dynamicDiv");

        textSpan.style.fontSize = 64;

        while(textSpan.offsetHeight > textDiv.offsetHeight)
        {
            textSpan.style.fontSize = parseInt(textSpan.style.fontSize) - 1;
        }
    }
</script>

</head>
<body onload="shrink()">
    <div id="dynamicDiv"><span id="dynamicSpan">DYNAMIC FONT</span></div>
</body>
</html>

这是一个带有的版本:

<html>
<head>
<style type="text/css">
.dynamicDiv
{
    background: #CCCCCC;
    width: 300px;
    height: 100px;
    font-size: 64px;
    overflow: hidden;
}
</style>

<script type="text/javascript">
    function shrink()
    {
        var textDivs = document.getElementsByClassName("dynamicDiv");
        var textDivsLength = textDivs.length;

        // Loop through all of the dynamic divs on the page
        for(var i=0; i<textDivsLength; i++) {

            var textDiv = textDivs[i];

            // Loop through all of the dynamic spans within the div
            var textSpan = textDiv.getElementsByClassName("dynamicSpan")[0];

            // Use the same looping logic as before
            textSpan.style.fontSize = 64;

            while(textSpan.offsetHeight > textDiv.offsetHeight)
            {
                textSpan.style.fontSize = parseInt(textSpan.style.fontSize) - 1;
            }

        }

    }
</script>

</head>
<body onload="shrink()">
    <div class="dynamicDiv"><span class="dynamicSpan">DYNAMIC FONT</span></div>
    <div class="dynamicDiv"><span class="dynamicSpan">ANOTHER DYNAMIC FONT</span></div>
    <div class="dynamicDiv"><span class="dynamicSpan">AND YET ANOTHER DYNAMIC FONT</span></div>
</body>
</html>

【讨论】:

  • 我发现这对offsetWidth 效果更好,我还必须为大小创建一个变量,然后附加像素textSpan.style.fontSize = size+"px";
  • 确定 '+"px"' 是必要的。
  • 谢谢你,救命恩人!我不知道 jQuery,所以我坚持你的解决方案:)
【解决方案2】:

谢谢Attack。我想使用 jQuery。

你指出了我正确的方向,这就是我最终的结果:

这是插件的链接:https://plugins.jquery.com/textfill/
以及来源链接:http://jquery-textfill.github.io/

;(function($) {
    $.fn.textfill = function(options) {
        var fontSize = options.maxFontPixels;
        var ourText = $('span:visible:first', this);
        var maxHeight = $(this).height();
        var maxWidth = $(this).width();
        var textHeight;
        var textWidth;
        do {
            ourText.css('font-size', fontSize);
            textHeight = ourText.height();
            textWidth = ourText.width();
            fontSize = fontSize - 1;
        } while ((textHeight > maxHeight || textWidth > maxWidth) && fontSize > 3);
        return this;
    }
})(jQuery);

$(document).ready(function() {
    $('.jtextfill').textfill({ maxFontPixels: 36 });
});

而我的html是这样的

<div class='jtextfill' style='width:100px;height:50px;'>
    <span>My Text Here</span>
</div>

这是我的第一个 jquery 插件,所以它可能没有它应该的那么好。当然欢迎指点。

【讨论】:

  • 我实际上只是将它清理并打包为一个插件,可从 jquery.com 获取plugins.jquery.com/project/TextFill
  • @GeekyMonkey,你拉插件了吗?只是按照这个页面的欺骗链接,我想看看,但是指向你网站的 jQuery.com 链接返回404
  • 注意:我发现由于某种原因,这个插件只在 div (上例中的 $('.jtextfill') )是根文档的一部分时才有效。当 div 嵌入到其他 div 中时,看起来 .width() 返回零。
  • 那个循环上的“while”行在我看来是错误的——“||”周围应该有括号子表达式。现在的写法,只有在宽度过大时才检查最小字体大小,而不是高度。
  • 这种方法非常慢,每次字体改变大小时都需要重新渲染元素。检查我的答案以获得更好的方法。
【解决方案3】:

这是基于 GeekyMonkey 上面发布的内容,并进行了一些修改。

; (function($) {
/**
* Resize inner element to fit the outer element
* @author Some modifications by Sandstrom
* @author Code based on earlier works by Russ Painter (WebDesign@GeekyMonkey.com)
* @version 0.2
*/
$.fn.textfill = function(options) {

    options = jQuery.extend({
        maxFontSize: null,
        minFontSize: 8,
        step: 1
    }, options);

    return this.each(function() {

        var innerElements = $(this).children(':visible'),
            fontSize = options.maxFontSize || innerElements.css("font-size"), // use current font-size by default
            maxHeight = $(this).height(),
            maxWidth = $(this).width(),
            innerHeight,
            innerWidth;

        do {

            innerElements.css('font-size', fontSize);

            // use the combined height of all children, eg. multiple <p> elements.
            innerHeight = $.map(innerElements, function(e) {
                return $(e).outerHeight();
            }).reduce(function(p, c) {
                return p + c;
            }, 0);

            innerWidth = innerElements.outerWidth(); // assumes that all inner elements have the same width
            fontSize = fontSize - options.step;

        } while ((innerHeight > maxHeight || innerWidth > maxWidth) && fontSize > options.minFontSize);

    });

};

})(jQuery);

【讨论】:

  • 不同之处在于它可以采用多个子元素,并且它考虑了填充。使用 font-size 作为默认的最大尺寸,以避免混淆 javascript 和 css。
  • 这很好,但我该如何使用呢?我做 $('.outer').textfill();我没有得到任何改变。
  • 谢谢,这是一个非常好的实现。我遇到的一件事:如果您正在处理非常长的文本字符串和非常窄的容器,则文本字符串会伸出容器,但 outerWidth 仍然会被计算为好像没有。折腾“自动换行:断词;”进入该容器的 CSS,它将解决此问题。
【解决方案4】:

我的网站也有同样的问题。我有一个显示在投影仪、墙壁、大屏幕上的页面..

由于我不知道我的字体的最大尺寸,我重新使用了@GeekMonkey 上面的插件,但增加了字体大小:

$.fn.textfill = function(options) {
        var defaults = { innerTag: 'span', padding: '10' };
        var Opts = jQuery.extend(defaults, options);

        return this.each(function() {
            var ourText = $(Opts.innerTag + ':visible:first', this);
            var fontSize = parseFloat(ourText.css('font-size'),10);
            var doNotTrepass = $(this).height()-2*Opts.padding ;
            var textHeight;

            do {
                ourText.css('font-size', fontSize);
                textHeight = ourText.height();
                fontSize = fontSize + 2;
            } while (textHeight < doNotTrepass );
        });
    };

【讨论】:

  • +1 是这个页面上唯一对我有用的插件!
  • 这个插件让我崩溃了。
【解决方案5】:

由于性能不佳,我发现之前的任何解决方案都不够充分,所以我自己制作了一个使用简单数学而不是循环的解决方案。应该在所有浏览器中都能正常工作。

根据this performance test case,它比此处找到的其他解决方案要快得多。

(function($) {
    $.fn.textfill = function(maxFontSize) {
        maxFontSize = parseInt(maxFontSize, 10);
        return this.each(function(){
            var ourText = $("span", this),
                parent = ourText.parent(),
                maxHeight = parent.height(),
                maxWidth = parent.width(),
                fontSize = parseInt(ourText.css("fontSize"), 10),
                multiplier = maxWidth/ourText.width(),
                newSize = (fontSize*(multiplier-0.1));
            ourText.css(
                "fontSize", 
                (maxFontSize > 0 && newSize > maxFontSize) ? 
                    maxFontSize : 
                    newSize
            );
        });
    };
})(jQuery);

如果你想贡献我已经添加了this to Gist

【讨论】:

  • @Jon,谢谢!你是对的,我的脚本没有做多行,但是 OP 并没有特别要求这样做,所以你的假设可能是错误的。而且,这种行为在imo中没有多大意义。我想添加多行支持的最佳方法是根据字数拆分字符串,然后使用上述脚本计算每个部分,无论如何它很可能会更快。
  • @Jon,我玩了一点多行文本填充,最后得到了this solution。 sandstorm 的方法很可能更准确,但这个方法更快;)
  • 这是一个最小字体大小和最大字体大小的版本:gist.github.com/1714284
  • @Hoffmann 嗯。我的解决方案不会循环调用.css("font-size")。你从哪里得到那个的?我的解决方案可能更快,因为它没有您添加到插件中的任何花哨的东西。欢迎您将您的插件添加到 jsperf 中,我们将看看哪个是最快的 ;)
  • @MarcusEkwall 哦,对不起,出于某种原因,我虽然在那里看到了一个while循环。您的方法与我自己的方法相似,实际上我会慢一些,因为我的插件还做了一些其他的事情(比如调整以适应高度和宽度,集中文本和其他一些选项),没关系,真正慢的部分正在循环中调用 .css 函数。
【解决方案6】:

这是接受的答案的一个版本,它也可以采用 minFontSize 参数。

(function($) {
    /**
    * Resizes an inner element's font so that the inner element completely fills the outer element.
    * @author Russ Painter WebDesign@GeekyMonkey.com
    * @author Blake Robertson 
    * @version 0.2 -- Modified it so a min font parameter can be specified.
    *    
    * @param {Object} Options which are maxFontPixels (default=40), innerTag (default='span')
    * @return All outer elements processed
    * @example <div class='mybigdiv filltext'><span>My Text To Resize</span></div>
    */
    $.fn.textfill = function(options) {
        var defaults = {
            maxFontPixels: 40,
            minFontPixels: 10,
            innerTag: 'span'
        };
        var Opts = jQuery.extend(defaults, options);
        return this.each(function() {
            var fontSize = Opts.maxFontPixels;
            var ourText = $(Opts.innerTag + ':visible:first', this);
            var maxHeight = $(this).height();
            var maxWidth = $(this).width();
            var textHeight;
            var textWidth;
            do {
                ourText.css('font-size', fontSize);
                textHeight = ourText.height();
                textWidth = ourText.width();
                fontSize = fontSize - 1;
            } while ((textHeight > maxHeight || textWidth > maxWidth) && fontSize > Opts.minFontPixels);
        });
    };
})(jQuery);

【讨论】:

  • 谢谢,虽然我认为你的代码顶部有一个不应该出现的分号
【解决方案7】:

我从 Marcus Ekwall 复制了上面的脚本:https://gist.github.com/3945316 并根据我的喜好对其进行了调整,现在它会在调整窗口大小时触发,以便子组件始终适合其容器。我已经粘贴了下面的脚本以供参考。

(function($) {
    $.fn.textfill = function(maxFontSize) {
        maxFontSize = parseInt(maxFontSize, 10);
        return this.each(function(){
            var ourText = $("span", this);
            function resizefont(){
                var parent = ourText.parent(),
                maxHeight = parent.height(),
                maxWidth = parent.width(),
                fontSize = parseInt(ourText.css("fontSize"), 10),
                multiplier = maxWidth/ourText.width(),
                newSize = (fontSize*(multiplier));
                ourText.css("fontSize", maxFontSize > 0 && newSize > maxFontSize ? maxFontSize : newSize );
            }
            $(window).resize(function(){
                resizefont();
            });
            resizefont();
        });
    };
})(jQuery);

【讨论】:

  • 很高兴您能帮助提问者。但是,在某些情况下,只留下一个链接的答案可能是有害的。虽然你现在的答案很好,但如果链接死了,你的答案就会失去它的价值。因此,如果您在答案中总结文章中的内容,将会很有帮助。请参阅this 问题以获得澄清。
【解决方案8】:

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

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

只需调用

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

它会很适合您的容器。

在这里查看它的实际效果:

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

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

我将努力获得一个设置最大字体大小的选项。

编辑:我发现插件还有一些问题,除了标准的盒子模型之外,它不处理其他盒子模型,并且 div 不能有边距或边框。我会努力的。

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

Edit3:我已将插件更新到版本 1.2.0。对代码和新选项(verticalAlign、horizo​​ntalAlign、textAlign)的主要清理以及对 span 标签内的内部元素的支持(如换行符或字体真棒图标。)

【讨论】:

  • 我想知道为什么不支持将文本换行成多行?
  • @ManishSapariya 支持,但需要手动添加换行符(br 标签)。我不支持自动文本换行的原因是为了使其快速(仅更改字体大小两次而不是多次),我需要假设文本不会在单词之间换行。我的插件的工作方式是将字体大小设置为 1000px,然后查看文本与容器相比的大小因子,然后我将字体大小减小相同的因子。为了支持正常的文本换行,我需要使用非常慢的方法(多次减小字体大小)。
  • 嘿!由于此处没有私人消息,因此在 StackOverflow 上,我将不得不通过评论您的答案来询问您。我喜欢你的 jQuery 插件,但我无法让它为我工作。我已经包含了正确的 jQuery 库,下载了您的插件并将其包含在内。现在,当我尝试使用它时,控制台会显示“未捕获的 TypeError:未定义不是函数”。这是你熟悉的东西吗?你知道如何解决这个问题吗?谢谢
  • @GustvandeWal 包含jquery库后需要包含插件
  • 我做到了。我有 浏览器没有通知我它无法加载 jQuery 库或插件。
【解决方案9】:

建议的迭代解决方案可以在两个方面显着加快:

1) 将字体大小乘以某个常数,而不是加或减 1。

2) 首先,在使用课程常数时为零,例如,每个循环的大小加倍。然后,大致了解从哪里开始,通过更精细的调整来做同样的事情,比如乘以 1.1。虽然完美主义者可能想要理想字体的精确整数像素大小,但大多数观察者并没有注意到 100 和 110 像素之间的差异。如果你是一个完美主义者,那就重复第三次,进行更精细的调整。

我没有编写一个特定的例程或插件来回答确切的问题,我只是依靠基本思想并编写代码的变体来处理各种布局问题,而不仅仅是文本,包括拟合 div、span、图片,...按宽度,高度,面积,...在一个容器内,匹配另一个元素......

这是一个例子:

  var                           nWindowH_px             = jQuery(window).height();
  var                           nWas                    = 0;
  var                           nTry                    = 5;

  do{
   nWas = nTry;
   nTry *= 2;
   jQuery('#divTitle').css('font-size' ,nTry +'px');
  }while( jQuery('#divTitle').height() < nWindowH_px );

  nTry = nWas;

  do{
   nWas = nTry;
   nTry = Math.floor( nTry * 1.1 );
   jQuery('#divTitle').css('font-size' ,nTry +'px');
  }while( nWas != nTry   &&   jQuery('#divTitle').height() < nWindowH_px );

  jQuery('#divTitle').css('font-size' ,nWas +'px');

【讨论】:

    【解决方案10】:

    您可以使用FitText.js (github page) 来解决这个问题。与 TextFill 相比,它确实小而高效。 TextFill 使用昂贵的 while 循环,而 FitText 不使用。

    FitText 也更灵活(我在一个有非常特殊要求的项目中使用它,并且工作起来就像一个冠军!)。

    HTML:

    <div class="container">
      <h1 id="responsive_headline">Your fancy title</h1>
    </div>
    
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
    <script src="jquery.fittext.js"></script>
    <script>
      jQuery("#responsive_headline").fitText();
    </script>
    

    您也可以为其设置选项:

    <script>
      jQuery("#responsive_headline").fitText(1, { minFontSize: '30px', maxFontSize: '90px'});
    </script>
    

    CSS:

    #responsive_headline {
       width: 100%;
       display: block;
    }
    

    如果你需要的话,FitText 也有一个no-jQuery version

    【讨论】:

    • fittext 是否考虑高度?
    • @ManishSapariya 不,它没有。它只是将容器宽度除以 10 并将其用作字体大小。
    【解决方案11】:

    我为 AngularJS 创建了一个指令 - 深受 GeekyMonkey 的回答启发,但没有 jQuery 依赖项。

    演示: http://plnkr.co/edit/8tPCZIjvO3VSApSeTtYr?p=preview

    标记

    &lt;div class="fittext" max-font-size="50" text="Your text goes here..."&gt;&lt;/div&gt;

    指令

    app.directive('fittext', function() {
    
      return {
        scope: {
          minFontSize: '@',
          maxFontSize: '@',
          text: '='
        },
        restrict: 'C',
        transclude: true,
        template: '<div ng-transclude class="textContainer" ng-bind="text"></div>',
        controller: function($scope, $element, $attrs) {
          var fontSize = $scope.maxFontSize || 50;
          var minFontSize = $scope.minFontSize || 8;
    
          // text container
          var textContainer = $element[0].querySelector('.textContainer');
    
          angular.element(textContainer).css('word-wrap', 'break-word');
    
          // max dimensions for text container
          var maxHeight = $element[0].offsetHeight;
          var maxWidth = $element[0].offsetWidth;
    
          var textContainerHeight;
          var textContainerWidth;      
    
          var resizeText = function(){
            do {
              // set new font size and determine resulting dimensions
              textContainer.style.fontSize = fontSize + 'px';
              textContainerHeight = textContainer.offsetHeight;
              textContainerWidth = textContainer.offsetWidth;
    
              // shrink font size
              var ratioHeight = Math.floor(textContainerHeight / maxHeight);
              var ratioWidth = Math.floor(textContainerWidth / maxWidth);
              var shrinkFactor = ratioHeight > ratioWidth ? ratioHeight : ratioWidth;
              fontSize -= shrinkFactor;
    
            } while ((textContainerHeight > maxHeight || textContainerWidth > maxWidth) && fontSize > minFontSize);        
          };
    
          // watch for changes to text
          $scope.$watch('text', function(newText, oldText){
            if(newText === undefined) return;
    
            // text was deleted
            if(oldText !== undefined && newText.length < oldText.length){
              fontSize = $scope.maxFontSize;
            }
            resizeText();
          });
        }
      };
    });
    

    【讨论】:

    • 我遇到的一个问题是resizeText 似乎在ng-bind 实际将文本分配给元素之前被调用,导致它的大小基于先前的文本而不是当前文本。这在上面的演示中并不算太糟糕,它在用户键入时重复调用,但如果它被调用一次,从 null 变为实际值(如在单向绑定中),它会保持最大大小。跨度>
    【解决方案12】:

    编辑:此代码用于在 HTML5 视频顶部显示注释。它会在调整视频大小时即时更改字体大小(当调整浏览器窗口大小时)。注释已连接到视频(就像 YouTube 上的注释一样),这就是代码使用实例而不是 DOM 句柄的原因直接。

    根据要求,我将提供一些用于实现此目的的代码。 (HTML5 视频上的文本框。)代码是很久以前写的,坦率地说,我认为它相当混乱。由于问题已经得到解答,并且很久以前就已经接受了答案,所以我不费心重写它。但如果有人想稍微简化一下,欢迎您!

    // Figure out the text size:
    var text = val['text'];
    var letters = text.length;
    var findMultiplier = function(x) { // g(x)
        /* By analysing some functions with regression, the resulting function that
         gives the best font size with respect to the number of letters and the size
         of the note is:
         g(x) = 8.3 - 2.75x^0.15 [1 < x < 255]
         f(x) = g(letters) * (x / 1000)^0.5
         Font size = f(size)
         */
        return 8.3 - 2.75 * Math.pow(x, 0.15);
    };
    
    var findFontSize = function(x) { // f(x)
        return findMultiplier(letters) * Math.pow(x / 1000, 0.5);
    };
    
    val.setFontSizeListener = function() {
        p.style.fontSize = '1px'; // So the text should not overflow the box when measuring.
        var noteStyle = window.getComputedStyle(table);
        var width = noteStyle.getPropertyValue('width');
        var height = noteStyle.getPropertyValue('height');
        var size = width.substring(0, width.length - 2) * height.substring(0, height.length - 2);
        p.style.fontSize = findFontSize(size) + 'px';
    };
    window.addEventListener('resize', val.setFontSizeListener);
    

    您可能需要将这些数字从 font-family 调整为 font-family。一个很好的方法是下载一个名为 GeoGebra 的免费图形可视化工具。更改文本的长度和框的大小。然后你手动设置大小。将手动结果绘制到坐标系中。然后输入我在此处发布的两个等式并调整数字,直到“我的”图表适合您自己手动绘制的点。

    【讨论】:

      【解决方案13】:

      这是此解决方案的另一个版本:

      shrinkTextInElement : function(el, minFontSizePx) {
          if(!minFontSizePx) {
              minFontSizePx = 5;
          }
          while(el.offsetWidth > el.parentNode.offsetWidth || el.offsetHeight > el.parentNode.offsetHeight) {
      
              var newFontSize = (parseInt(el.style.fontSize, 10) - 3);
              if(newFontSize <= minFontSizePx) {
                  break;
              }
      
              el.style.fontSize = newFontSize + "px";
          }
      }
      

      【讨论】:

        【解决方案14】:

        这是一种改进的循环方法,它使用二进制搜索以尽可能少的步骤找到适合父级的最大可能大小(这比按固定字体大小步进更快、更准确)。该代码还针对性能进行了多种优化。

        默认情况下,将执行 10 次二进制搜索步骤,这将在最佳大小的 0.1% 范围内。您可以改为将 numIter 设置为某个值 N 以达到最佳大小的 1/2^N。

        使用 CSS 选择器调用它,例如:fitToParent('.title-span');

        /**
         * Fit all elements matching a given CSS selector to their parent elements'
         * width and height, by adjusting the font-size attribute to be as large as
         * possible. Uses binary search.
         */
        var fitToParent = function(selector) {
            var numIter = 10;  // Number of binary search iterations
            var regexp = /\d+(\.\d+)?/;
            var fontSize = function(elem) {
                var match = elem.css('font-size').match(regexp);
                var size = match == null ? 16 : parseFloat(match[0]);
                return isNaN(size) ? 16 : size;
            }
            $(selector).each(function() {
                var elem = $(this);
                var parentWidth = elem.parent().width();
                var parentHeight = elem.parent().height();
                if (elem.width() > parentWidth || elem.height() > parentHeight) {
                    var maxSize = fontSize(elem), minSize = 0.1;
                    for (var i = 0; i < numIter; i++) {
                        var currSize = (minSize + maxSize) / 2;
                        elem.css('font-size', currSize);
                        if (elem.width() > parentWidth || elem.height() > parentHeight) {
                            maxSize = currSize;
                        } else {
                            minSize = currSize;
                        }
                    }
                    elem.css('font-size', minSize);
                }
            });
        };
        

        【讨论】:

        • 喜欢这个选项。我对其进行了修改以添加vAlignpadding 的参数。 vAlign == true 将所选元素的行高设置为父元素的行高。填充将最终大小减小传递的值。它默认为 5。我认为它看起来非常不错。
        【解决方案15】:

        这是我对 OP 答案的修改。

        简而言之,许多尝试对此进行优化的人抱怨正在使用循环。是的,虽然循环可能很慢,但其他方法可能不准确。

        因此,我的方法使用 Binary Search 来找到最佳字体大小:

        $.fn.textfill = function()
        {
            var self = $(this);
            var parent = self.parent();
        
            var attr = self.attr('max-font-size');
            var maxFontSize = parseInt(attr, 10);
            var unit = attr.replace(maxFontSize, "");
        
            var minFontSize = parseInt(self.attr('min-font-size').replace(unit, ""));
            var fontSize = (maxFontSize + minFontSize) / 2;
        
            var maxHeight = parent.height();
            var maxWidth = parent.width();
        
            var textHeight;
            var textWidth;
        
            do
            {
                self.css('font-size', fontSize + unit);
        
                textHeight = self.height();
                textWidth = self.width();
        
                if(textHeight > maxHeight || textWidth > maxWidth)
                {
                    maxFontSize = fontSize;
                    fontSize = Math.floor((fontSize + minFontSize) / 2);
                }
                else if(textHeight < maxHeight || textWidth < maxWidth)
                {
                    minFontSize = fontSize;
                    fontSize = Math.floor((fontSize + maxFontSize) / 2);
                }
                else
                    break;
        
            }
            while(maxFontSize - minFontSize > 1 && maxFontSize > minFontSize);
        
            self.css('font-size', fontSize + unit);
        
            return this;
        }
        
        function resizeText()
        {
          $(".textfill").textfill();
        }
        
        $(document).ready(resizeText);
        $(window).resize(resizeText);
        

        这也允许元素指定最小和最大字体:

        <div class="container">
            <div class="textfill" min-font-size="10px" max-font-size="72px">
                Text that will fill the container, to the best of its abilities, and it will <i>never</i> have overflow.
            </div>
        </div>
        

        此外,该算法是无单位的。您可以指定emrem% 等,它会将其用作最终结果。

        这是小提琴:https://jsfiddle.net/fkhqhnqe/1/

        【讨论】:

          【解决方案16】:

          我选择了 geekMonkey 解决方案,但它太慢了。他所做的是将字体大小调整为最大(maxFontPixels),然后检查它是否适合容器。否则它将字体大小减小 1px 并再次检查。为什么不简单地检查前一个容器的高度并提交该值? (是的,我知道为什么,但我现在提出了一个解决方案,它只适用于高度,并且还有一个最小/最大选项)

          这是一个更快的解决方案:

          var index_letters_resize;
          (index_letters_resize = function() {
            $(".textfill").each(function() {
              var
                $this = $(this),
                height = Math.min( Math.max( parseInt( $this.height() ), 40 ), 150 );
              $this.find(".size-adjust").css({
                fontSize: height
              });
            });
          }).call();
          
          $(window).on('resize', function() {
            index_letters_resize();
          );
          

          这将是 HTML:

          <div class="textfill">
            <span class="size-adjust">adjusted element</span>
            other variable stuff that defines the container size
          </div>
          

          再次重申:此解决方案仅检查容器的高度。这就是为什么这个函数不需要检查元素是否适合里面。但我也实现了一个最小/最大值(40min,150max),所以对我来说这非常好(并且也适用于调整窗口大小)。

          【讨论】:

            【解决方案17】:

            我遇到了同样的问题,解决方案基本上是使用 javascript 来控制字体大小。 在 codepen 上查看此示例:

            https://codepen.io/ThePostModernPlatonic/pen/BZKzVR

            此示例仅用于高度,也许您需要添加一些关于宽度的 if。

            尝试调整它的大小

            <!doctype html>
            <html>
            <head>
            <meta charset="utf-8">
            <title>Documento sem título</title>
            <style>
            </style>
            </head>
            <body>
            <div style="height:100vh;background-color: tomato;" id="wrap">        
              <h1 class="quote" id="quotee" style="padding-top: 56px">Because too much "light" doesn't <em>illuminate</em> our paths and warm us, it only blinds and burns us.</h1>
            </div>
            </body>
            <script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
            <script>
              var multiplexador = 3;
              initial_div_height = document.getElementById ("wrap").scrollHeight;
              setInterval(function(){ 
                var div = document.getElementById ("wrap");
                var frase = document.getElementById ("quotee");
                var message = "WIDTH div " + div.scrollWidth + "px. "+ frase.scrollWidth+"px. frase \n";
                message += "HEIGHT div " + initial_div_height + "px. "+ frase.scrollHeight+"px. frase \n";           
                if (frase.scrollHeight < initial_div_height - 30){
                  multiplexador += 1;
                  $("#quotee").css("font-size", multiplexador); 
                }
                console.log(message);          
              }, 10);
            </script>
            </html>
            

            【讨论】:

              【解决方案18】:

              我喜欢

              let name = "Making statements based on opinion; back them up with references or personal experience."
              let originFontSize = 15;
              let maxDisplayCharInLine = 50; 
              let fontSize = Math.min(originFontSize, originFontSize / (name.length / maxDisplayCharInLine));
              

              【讨论】:

                【解决方案19】:

                只是想为 contenteditables 添加我的版本。

                $.fn.fitInText = function() {
                  this.each(function() {
                
                    let textbox = $(this);
                    let textboxNode = this;
                
                    let mutationCallback = function(mutationsList, observer) {
                      if (observer) {
                        observer.disconnect();
                      }
                      textbox.css('font-size', 0);
                      let desiredHeight = textbox.css('height');
                      for (i = 12; i < 50; i++) {
                        textbox.css('font-size', i);
                        if (textbox.css('height') > desiredHeight) {
                          textbox.css('font-size', i - 1);
                          break;
                        }
                      }
                
                      var config = {
                        attributes: true,
                        childList: true,
                        subtree: true,
                        characterData: true
                      };
                      let newobserver = new MutationObserver(mutationCallback);
                      newobserver.observe(textboxNode, config);
                
                    };
                
                    mutationCallback();
                
                  });
                }
                
                $('#inner').fitInText();
                #outer {
                  display: table;
                  width: 100%;
                }
                
                #inner {
                  border: 1px solid black;
                  height: 170px;
                  text-align: center;
                  display: table-cell;
                  vertical-align: middle;
                  word-break: break-all;
                }
                <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
                <div id="outer">
                  <div id="inner" contenteditable=true>
                    TEST
                  </div>
                </div>

                【讨论】:

                • 正是我所需要的。老实说,我从未使用过 jquery,是否可以使用纯 JavaScript 来做到这一点?我想用 Angular 在网页中实现它。
                【解决方案20】:

                我找到了一种方法来防止使用循环来缩小文本。它通过将字体大小乘以容器宽度和内容宽度之间的比率来调整字体大小。所以如果容器的宽度是内容的 1/3,那么 font-size 会减少 1/3 并且会是容器的宽度。为了扩大规模,我使用了一个 while 循环,直到内容大于容器。

                function fitText(outputSelector){
                    // max font size in pixels
                    const maxFontSize = 50;
                    // get the DOM output element by its selector
                    let outputDiv = document.getElementById(outputSelector);
                    // get element's width
                    let width = outputDiv.clientWidth;
                    // get content's width
                    let contentWidth = outputDiv.scrollWidth;
                    // get fontSize
                    let fontSize = parseInt(window.getComputedStyle(outputDiv, null).getPropertyValue('font-size'),10);
                    // if content's width is bigger than elements width - overflow
                    if (contentWidth > width){
                        fontSize = Math.ceil(fontSize * width/contentWidth,10);
                        fontSize =  fontSize > maxFontSize  ? fontSize = maxFontSize  : fontSize - 1;
                        outputDiv.style.fontSize = fontSize+'px';   
                    }else{
                        // content is smaller than width... let's resize in 1 px until it fits 
                        while (contentWidth === width && fontSize < maxFontSize){
                            fontSize = Math.ceil(fontSize) + 1;
                            fontSize = fontSize > maxFontSize  ? fontSize = maxFontSize  : fontSize;
                            outputDiv.style.fontSize = fontSize+'px';   
                            // update widths
                            width = outputDiv.clientWidth;
                            contentWidth = outputDiv.scrollWidth;
                            if (contentWidth > width){
                                outputDiv.style.fontSize = fontSize-1+'px'; 
                            }
                        }
                    }
                }
                

                这段代码是我上传到 Github https://github.com/ricardobrg/fitText/的测试的一部分

                【讨论】:

                  【解决方案21】:

                  这是我创建的最优雅的解决方案。它使用二进制搜索,进行 10 次迭代。天真的方法是做一个while循环并将字体大小增加1,直到元素开始溢出。您可以使用 element.offsetHeightelement.scrollHeight 确定元素何时开始溢出。如果 scrollHeight 比 offsetHeight 大,你的字体太大了。

                  二分搜索是一个更好的算法。它还受到您要执行的迭代次数的限制。只需调用 flexFont 并插入 div id,它就会在 8px96px 之间调整字体大小。

                  我花了一些时间研究这个主题并尝试不同的库,但最终我认为这是最简单、最直接的解决方案。

                  请注意,如果您愿意,可以更改为使用offsetWidthscrollWidth,或将两者都添加到此函数中。

                  // Set the font size using overflow property and div height
                  function flexFont(divId) {
                      var content = document.getElementById(divId);
                      content.style.fontSize = determineMaxFontSize(content, 8, 96, 10, 0) + "px";
                  };
                  
                  // Use binary search to determine font size
                  function determineMaxFontSize(content, min, max, iterations, lastSizeNotTooBig) {
                      if (iterations === 0) {
                          return lastSizeNotTooBig;
                      }
                      var obj = fontSizeTooBig(content, min, lastSizeNotTooBig);
                  
                      // if `min` too big {....min.....max.....}
                      // search between (avg(min, lastSizeTooSmall)), min)
                      // if `min` too small, search between (avg(min,max), max)
                      // keep track of iterations, and the last font size that was not too big
                      if (obj.tooBig) {
                          (lastSizeTooSmall === -1) ?
                              determineMaxFontSize(content, min / 2, min, iterations - 1, obj.lastSizeNotTooBig, lastSizeTooSmall) :
                                  determineMaxFontSize(content, (min + lastSizeTooSmall) / 2, min, iterations - 1, obj.lastSizeNotTooBig, lastSizeTooSmall);
                  
                      } else {
                          determineMaxFontSize(content, (min + max) / 2, max, iterations - 1, obj.lastSizeNotTooBig, min);
                      }
                  }
                  
                  // determine if fontSize is too big based on scrollHeight and offsetHeight, 
                  // keep track of last value that did not overflow
                  function fontSizeTooBig(content, fontSize, lastSizeNotTooBig) {
                      content.style.fontSize = fontSize + "px";
                      var tooBig = content.scrollHeight > content.offsetHeight;
                      return {
                          tooBig: tooBig,
                          lastSizeNotTooBig: tooBig ? lastSizeNotTooBig : fontSize
                      };
                  }
                  

                  【讨论】:

                  • 谢谢,这看起来很棒!我刚收到ReferenceError: lastSizeTooSmall is not defined。也许这需要在某个地方定义?
                  猜你喜欢
                  • 1970-01-01
                  • 2017-05-09
                  • 2011-05-21
                  • 2018-12-26
                  • 1970-01-01
                  • 2013-06-04
                  • 2010-10-19
                  • 1970-01-01
                  相关资源
                  最近更新 更多