Авторазмер динамического текста для заполнения контейнера фиксированного размера - PullRequest
299 голосов
/ 27 марта 2009

Мне нужно отобразить введенный пользователем текст в div фиксированного размера. Я хочу, чтобы размер шрифта автоматически настраивался так, чтобы текст заполнял поле как можно больше.

Итак - если div составляет 400px x 300px. Если кто-то входит в ABC, то это действительно большой шрифт. Если они введут абзац, это будет крошечный шрифт.

Возможно, я бы хотел начать с максимального размера шрифта - возможно, 32 пикселя, и, хотя текст слишком велик, чтобы поместиться в контейнер, уменьшите размер шрифта до его соответствия.

Ответы [ 20 ]

162 голосов
/ 27 марта 2009

Спасибо Атака . Я хотел использовать 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, так что он, вероятно, не так хорош, как следовало бы. Указатели, безусловно, приветствуются.

52 голосов
/ 01 ноября 2011

Я не нашел ни одного из предыдущих решений достаточно адекватным из-за плохой производительности, поэтому я сделал свое собственное, которое использует простую математику вместо цикла. Должно работать нормально во всех браузерах.

Согласно этому тесту производительности он намного быстрее, чем другие решения, найденные здесь.

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

Если вы хотите внести свой вклад, я добавил это в Gist .

33 голосов
/ 27 марта 2009

Как бы мне не нравились случайные отзывы, которые я получаю за этот ответ (спасибо!), Это действительно не самый лучший подход к этой проблеме. Пожалуйста, ознакомьтесь с некоторыми другими замечательными ответами здесь, особенно те, которые нашли решения без зацикливания.


Тем не менее, для справки, вот мой оригинальный ответ :

<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>
32 голосов
/ 02 июля 2013

Большинство других ответов используют цикл для уменьшения размера шрифта до тех пор, пока он не уместится на элементе div, это ОЧЕНЬ медленно, поскольку страница должна повторно отображать элемент каждый раз, когда размер шрифта изменяется. В конце концов мне пришлось написать собственный алгоритм, чтобы он работал так, чтобы я мог периодически обновлять его содержимое, не останавливая работу браузера пользователя. Я добавил некоторые другие функции (вращение текста, добавление отступов) и упаковал его как плагин jQuery, вы можете получить его по адресу:

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

просто позвоните

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

и он прекрасно поместится на вашем контейнере.

Смотрите это в действии здесь:

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

Пока у него есть некоторые ограничения, div должен иметь фиксированную высоту и ширину, и он не поддерживает перенос текста в несколько строк.

Я буду работать над получением опции для установки максимального размера шрифта.

Edit: я обнаружил еще несколько проблем с плагином, он не обрабатывает другую блочную модель, кроме стандартной, и у div не может быть полей или границ. Я буду работать над этим.

Edit2: теперь я исправил эти проблемы и ограничения и добавил больше опций. Вы можете установить максимальный размер шрифта, а также можете ограничить размер шрифта, используя ширину, высоту или оба. Я постараюсь принять значения max-width и max-height в элементе оболочки.

Edit3: я обновил плагин до версии 1.2.0. Основная очистка кода и новые параметры (verticalAlign, horizontalAlign, textAlign) и поддержка внутренних элементов внутри тега span (например, разрывов строк или значков шрифта).

9 голосов
/ 08 ноября 2009

Это основано на том, что 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);
6 голосов
/ 12 ноября 2013

Я создал директиву для AngularJS - она ​​вдохновлена ​​ответом GeekyMonkey, но без зависимости jQuery.

Демо: http://plnkr.co/edit/8tPCZIjvO3VSApSeTtYr?p=preview

Разметка

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

Директива

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();
      });
    }
  };
});
6 голосов
/ 08 июля 2015

Вот улучшенный метод зацикливания, который использует бинарный поиск, чтобы найти максимально возможный размер, который вписывается в родительский элемент за наименьшее количество возможных шагов (это быстрее и точнее, чем переход на фиксированный размер шрифта). Код также оптимизирован для повышения производительности несколькими способами.

По умолчанию будет выполнено 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);
        }
    });
};
5 голосов
/ 24 октября 2012

Я добавил вышеприведенный скрипт от Маркуса Экволла: 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);
4 голосов
/ 16 февраля 2016

Вот моя модификация ответа ОП.

Короче говоря, многие люди, которые пытались оптимизировать это, жаловались на то, что используется цикл. Да, хотя петли могут быть медленными, другие подходы могут быть неточными.

Поэтому мой подход использует Бинарный поиск , чтобы найти лучший размер шрифта:

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

Кроме того, этот алгоритм не имеет единиц измерения. Вы можете указать em, rem, % и т. Д., И он будет использовать это для окончательного результата.

Вот Скрипка: https://jsfiddle.net/fkhqhnqe/1/

2 голосов
/ 21 марта 2011

У меня была точно такая же проблема с моим сайтом. У меня есть страница, которая отображается на проекторе, на стенах, на больших экранах ..

Поскольку я не знаю максимального размера шрифта, я повторно использовал плагин выше @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 );
        });
    };
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...