jQuery 自动调整动态文本大小以填充固定大小的容器

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/687998/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-26 09:35:06  来源:igfitidea点击:

Auto-size dynamic text to fill fixed size container

jqueryhtmlcss

提问by GeekyMonkey

I need to display user entered text into a fixed size div. What i want is for the font size to be automatically adjusted so that the text fills the box as much as possible.

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

So - If the div is 400px x 300px. If someone enters ABC then it's really big font. If they enter a paragraph, then it would be a tiny font.

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

I'd probably want to start with a maximum font size - maybe 32px, and while the text is too big to fit the container, shrink the font size until it fits.

我可能想从最大字体大小开始 - 可能是 32px,虽然文本太大而无法容纳容器,但缩小字体大小直到适合。

采纳答案by GeekyMonkey

Thanks Attack. I wanted to use jQuery.

感谢攻击。我想使用 jQuery。

You pointed me in the right direction, and this is what I ended up with:

你给我指明了正确的方向,这就是我的结果:

Here is a link to the plugin: https://plugins.jquery.com/textfill/
And a link to the source: http://jquery-textfill.github.io/

这是插件的链接: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 });
});

and my html is like this

我的 html 是这样的

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

This is my first jquery plugin, so it's probably not as good as it should be. Pointers are certainly welcome.

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

回答by mekwall

I didn't find any of the previous solutions to be adequate enough due to bad performance, so I made my own that uses simple math instead of looping. Should work fine in all browsers as well.

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

According to this performance test caseit is much faster then the other solutions found here.

根据此性能测试用例,它比此处找到的其他解决方案要快得多。

(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);

If you want to contribute I've added this to Gist.

如果您想做出贡献,我已将其添加到 Gist

回答by attack

As much as I love the occasional upvotes I get for this answer (thanks!), this is really not the greatest approach to this problem. Please check out some of the other wonderful answers here, especially the ones that have found solutions without looping.

尽管我喜欢偶尔为这个答案点赞(谢谢!),但这真的不是解决这个问题的最好方法。请在此处查看其他一些精彩的答案,尤其是那些无需循环即可找到解决方案的答案。



Still, for the sake of reference, here's my original answer:

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

<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>


And here's a version with classes:

这是一个带有的版本:

<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>

回答by Hoffmann

Most of the other answers use a loop to reduce the font-size until it fits on the div, this is VERY slow since the page needs to re-render the element each time the font changes size. I eventually had to write my own algorithm to make it perform in a way that allowed me to update its contents periodically without freezing the user browser. I added some other functionality (rotating text, adding padding) and packaged it as a jQuery plugin, you can get it at:

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

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

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

simply call

简单地打电话

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

and it will fit nicely on your container.

它非常适合您的容器。

See it in action here:

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

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

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

For now it has some limitations, the div must have a fixed height and width and it does not support wrapping text into multiple lines.

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

I will work on getting an option to set the maximum font-size.

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

Edit: I have found some more problems with the plugin, it does not handle other box-model besides the standard one and the div can't have margins or borders. I will work on it.

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

Edit2: I have now fixed those problems and limitations and added more options. You can set maximum font-size and you can also choose to limit the font-size using either width, height or both. I will work into accepting a max-width and max-height values in the wrapper element.

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

Edit3: I have updated the plugin to version 1.2.0. Major cleanup on the code and new options (verticalAlign, horizontalAlign, textAlign) and support for inner elements inside the span tag (like line-breaks or font-awesome icons.)

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

回答by sandstrom

This is based on what GeekyMonkey posted above, with some modifications.

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

; (function($) {
/**
* Resize inner element to fit the outer element
* @author Some modifications by Sandstrom
* @author Code based on earlier works by Russ Painter ([email protected])
* @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);

回答by Luke Hutchison

Here's an improved looping method that uses binary search to find the largest possible size that fits into the parent in the fewest steps possible (this is faster and more accurate than stepping by a fixed font size). The code is also optimized in several ways for performance.

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

By default, 10 binary search steps will be performed, which will get within 0.1% of the optimal size. You could instead set numIter to some value N to get within 1/2^N of the optimal size.

默认情况下,将执行 10 个二分搜索步骤,这将在最佳大小的 0.1% 以内。您可以将 numIter 设置为某个值 N 以获得最佳大小的 1/2^N 以内。

Call it with a CSS selector, e.g.: fitToParent('.title-span');

使用 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);
        }
    });
};

回答by sqren

I've created a directive for AngularJS - heavely inspired by GeekyMonkey's answer but without the jQuery dependency.

我为 AngularJS 创建了一个指令 - 受到 GeekyMonkey 答案的极大启发,但没有 jQuery 依赖。

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

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

Markup

标记

<div class="fittext" max-font-size="50" text="Your text goes here..."></div>

<div class="fittext" max-font-size="50" text="Your text goes here..."></div>

Directive

指示

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();
      });
    }
  };
});

回答by nimrod

I forked the script above from Marcus Ekwall: https://gist.github.com/3945316and tweaked it to my preferences, it now fires when the window is resized, so that the child always fits its container. I've pasted the script below for reference.

我从 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);

回答by Boom

Here's my modification of the OP's answer.

这是我对 OP 答案的修改。

In short, many people who tried to optimize this complained that a loop was being used. Yes, while loops can be slow, other approaches can be inaccurate.

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

Therefore, my approach uses Binary Searchto find the best Font Size:

因此,我的方法使用二进制搜索来找到最佳字体大小:

$.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);

This also allows the element to specify the minimum and maximum font:

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

<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>

Furthermore, this algorithm is unitless. You may specify em, rem, %, etc. and it will use that for its final result.

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

Here's the Fiddle: https://jsfiddle.net/fkhqhnqe/1/

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

回答by guillaumepotier

I had exactly the same problem with my website. I have a page that is displayed on a projector, on walls, big screens..

我的网站遇到了完全相同的问题。我有一个页面显示在投影仪、墙壁、大屏幕上。

As I don't know the max size of my font, I re-used the plugin above of @GeekMonkey but incrementing the fontsize :

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

$.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 );
        });
    };