Авторазмер динамического текста для заполнения контейнера фиксированного размера


312

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

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

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


119
Вероятно, одна из самых удивительных функций, которые должны были быть добавлены в HTML5 / CSS3 без необходимости JS.
Джон Магнолия

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

2
Фактически получается, что график, который дает вам лучший размер шрифта, определяется как f (x) = g (буквы) * (x / 1000) ^ n, где g (x) - простая функция, n изменяется в зависимости от шрифт, который вы используете. (Хотя он может иметь стандартное значение для всех шрифтов, если вы не хотите настраивать его, чтобы получить его абсолютно идеально ...). x - размер в квадратных пикселях контейнера.
Друг Ким

1
Если вы все еще заинтересованы, я могу добавить ответ. Лично я думаю, что это гораздо лучший способ для генерации правильного размера шрифта, вместо того, чтобы пытаться и терпеть неудачу, пока скрипт не "сделает это правильно".
Друг Ким

1
Проверьте мой ответ для лучшего способа сделать это
Хоффманн

Ответы:


167

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


8
Я на самом деле просто очистил его и упаковал как плагин, доступный на jquery.com по адресу plugins.jquery.com/project/TextFill
GeekyMonkey,

3
@ GeekyMonkey, ты вытащил плагин? Просто перешел на двойную ссылку на эту страницу и подумал, что посмотрю, но ссылки на jQuery.com возвращаются на ваш сайт 404.
Дэвид говорит восстановить Монику

Примечание: я обнаружил, что по какой-то причине этот плагин работает только тогда, когда div ($ ('. Jtextfill') в приведенном выше примере) является частью корневого документа. Похоже, что .width () возвращает ноль, когда div встроен в другие div.
Джаеш

1
Строка «while» в этом цикле мне кажется неправильной - вокруг «||» должны быть круглые скобки подвыражения. Как написано сейчас, минимальный размер шрифта проверяется только тогда, когда ширина слишком велика, а не высота.
Заостренный

4
Этот подход ОЧЕНЬ медленный, каждый раз, когда шрифт меняет размеры, требуется перерисовка элемента. Проверьте мой ответ для лучшего способа сделать это.
Хоффманн

52

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

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

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


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

4
@Jon, я немного поиграл с многострочным текстовым заполнением и в итоге нашел это решение . Метод песчаной бури, скорее всего, более точный, но этот быстрее;)
mekwall,

2
Вот версия с минимальным размером шрифта и максимальным: gist.github.com/1714284
Джесс Телфорд

1
@ Хоффманн Хмм. Мое решение не вызывает .css("font-size")в цикле. С чего взял? Мое решение, вероятно, быстрее, поскольку в нем нет ничего необычного, что вы добавили в свой плагин. Вы можете добавить свой плагин в jsperf, и мы увидим, какой из них самый быстрый;)
mekwall

1
@MarcusEkwall Ой, извините, по какой-то причине я думал, что видел цикл там. Ваш подход похож на мой, и на самом деле мой будет немного медленнее, так как мой плагин также делает некоторые другие вещи (например, подгонку по высоте и ширине, централизует текст и некоторые другие параметры), не так уж важно, очень медленная часть вызывает функцию .css внутри цикла.
Хоффманн

35

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


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

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

3
Я обнаружил, что это работает лучше offsetWidth, мне также пришлось создать переменную для размера, а затем добавить pxtextSpan.style.fontSize = size+"px";
Wez

2
обязательно '+ "px"' необходимо.
сандун дхаммика

@IdanShechter Извините за долгое ожидание! Добавил пример!
атака

Спасибо, спасатель! Я не осведомлен о jQuery, поэтому я придерживаюсь вашего решения :)
vintproykt

32

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

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

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

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

и это будет хорошо вписываться в ваш контейнер.

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

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

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

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

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

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

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


1
Мне интересно, почему не поддерживается перенос текста в несколько строк?
Маниш Сапария

1
@ManishSapariya Поддерживается, но вам нужно добавить разрывы строк (теги br) вручную. Причина, по которой я не поддерживаю автоматическое перенос текста, заключается в том, что для его быстрого выполнения (меняйте размер шрифта только дважды, а не несколько раз), мне нужно сделать предположение, что текст не будет переноситься между словами. Мой плагин работает так, чтобы установить размер шрифта равным 1000 пикселей, а затем посмотреть, какой размер текста сравнивается с контейнером, а затем уменьшить размер шрифта на тот же коэффициент. Для поддержки обычного переноса текста мне нужно будет использовать медленный подход (уменьшить размер шрифта в несколько раз), который очень медленный.
Гофман

Привет! Поскольку здесь нет личных сообщений, в StackOverflow мне придется спросить вас, комментируя ваш ответ. Мне нравится твой плагин jQuery, но я не могу заставить его работать на меня. Я включил правильную библиотеку jQuery, скачал ваш плагин и включил его. Теперь, когда я пытаюсь его использовать, консоль говорит «Uncaught TypeError: undefined не является функцией». Вы знакомы с этим? Вы знаете, как это исправить? Спасибо
Густ ван де Валь

@GustvandeWal Вы должны включить плагин после включения библиотеки jquery
Хоффманн

Я сделал. У меня есть <script type = "text / javascript" src = " code.jquery.com/jquery-2.1.1.min.js"></… src" js / jquery-bigtext.js "> </ script > Браузер не сообщает мне, что не может загрузить библиотеку jQuery или плагин.
Гаст ван де Валь

9

Это основано на том, что 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);

Разница в том, что он может принимать несколько дочерних элементов и учитывать отступы. Использует размер шрифта в качестве максимального размера по умолчанию, чтобы избежать смешивания JavaScript и CSS.
Sandstrom

5
Это здорово, но как мне это использовать? Я делаю $ ('. External'). Textfill (); и я не получаю изменений.
Дрю Бейкер

3
Спасибо, это очень хорошая реализация. Одна вещь, с которой я столкнулся: если вы имеете дело с очень длинными текстовыми строками и очень узкими контейнерами, текстовая строка будет торчать из контейнера, но externalWidth все равно будет вычисляться так, как если бы она этого не делала. Бросок "перенос слов: разрыв слова"; в ваш CSS для этого контейнера, это решит эту проблему.
Джон

8

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

По умолчанию будет выполнено 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);
        }
    });
};

Люблю этот вариант. Я изменил его, чтобы добавить параметры для vAlignи padding. vAlign == trueустанавливает высоту строки выбранного элемента равной высоте родительского элемента. Отступ уменьшает окончательный размер на переданное значение. По умолчанию это 5. Я думаю, что это выглядит очень хорошо.
Адвокат дьявола

6

Я создал директиву для 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();
      });
    }
  };
});

Одна проблема, с которой я столкнулся, заключается в том, что resizeTextкажется, что она вызывается до того, как ng-bindфактически назначить текст элементу, что приводит к его размеру на основе предыдущего текста, а не текущего текста. Это не так уж плохо в приведенной выше демонстрации, где он вызывается несколько раз при вводе пользователем, но если он вызывается один раз, переходя от нуля к реальному значению (как в односторонней привязке), он остается в максимальном размере.
Мирал

5

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

2
Здорово, что ты пытаешься помочь аскеру. Однако в некоторых случаях оставлять ответ только с ссылкой. Хотя ваш ответ хорош сейчас, если ссылка когда-либо умрет, ваш ответ потеряет свою ценность. Так что будет полезно, если вы обобщите содержание статьи в своем ответе. Смотрите этот вопрос для уточнения.
Коди Гулднер

5

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

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

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

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

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

Поскольку я не знаю максимальный размер моего шрифта, я повторно использовал плагин выше @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 за то, что был единственным плагином на этой странице, который действительно работал для меня!
Skybondsor

2
Этот плагин сбивает страницу для меня.
Джезен Томас

1

Вот версия принятого ответа, которая также может принимать параметр 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);

спасибо, хотя я думаю, что у вас есть точка с запятой в верхней части кода, которого не должно быть
Патрик Мур

1

Вы можете использовать FitText.js ( страница Github ) для решения этой проблемы. Действительно маленький и эффективный по сравнению с 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 .


Fittext учитывает высоту?
Маниш Сапария

1
@ManishSapariya нет, это не так. Он просто делит ширину контейнера на 10 и использует его как размер шрифта.
Дан Х

1

РЕДАКТИРОВАТЬ: Этот код был использован для отображения заметок в верхней части видео 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. Измените длину текста и размер поля. Затем вы вручную устанавливаете размер. Представьте результаты ручного анализа в систему координат. Затем вы вводите два уравнения, которые я выложил здесь, и изменяете числа, пока «мой» график не будет соответствовать вашим собственным точкам, построенным вручную.


1

Предлагаемые итеративные решения могут быть значительно ускорены по двум направлениям:

1) Умножьте размер шрифта на некоторую константу, а не добавляя или вычитая 1.

2) Во-первых, ноль при использовании константы курса, скажем, удвоить размер каждой петли. Затем, имея приблизительное представление о том, с чего начать, проделайте то же самое с более точной настройкой, скажем, умножьте на 1,1. Хотя перфекционисту может потребоваться точный целочисленный размер пикселя для идеального шрифта, большинство наблюдателей не замечают разницы между 100 и 110 пикселями. Если вы перфекционист, повторите третий раз с еще более точной настройкой.

Вместо того, чтобы писать конкретную подпрограмму или плагин, который отвечает на точный вопрос, я просто полагаюсь на основные идеи и пишу варианты кода для решения всех видов проблем компоновки, а не только текста, включая подгонку элементов div, spans, images. ... по ширине, высоте, площади, ... в контейнере, соответствующем другому элементу ....

Вот пример:

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

1

Это самое элегантное решение, которое я создал. Он использует бинарный поиск, делая 10 итераций. Наивным способом было сделать цикл while и увеличить размер шрифта на 1, пока элемент не начал переполняться. Вы можете определить, когда элемент начинает переполняться, используя element.offsetHeight и element.scrollHeight . Если scrollHeight больше, чем offsetHeight, у вас слишком большой размер шрифта.

Бинарный поиск - гораздо лучший алгоритм для этого. Он также ограничен количеством итераций, которые вы хотите выполнить. Просто вызовите flexFont и вставьте идентификатор div, и он будет регулировать размер шрифта от 8 до 96 пикселей .

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

Обратите внимание, если вы хотите, вы можете изменить, чтобы использовать offsetWidthи scrollWidth, или добавить оба к этой функции.

// 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. Может быть, это нужно где-то определить?
ndbroadbent

0

Я получил ту же проблему, и решение в основном заключается в использовании JavaScript для контроля размера шрифта. Проверьте этот пример на codepen:

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

Это пример только для высоты, может быть, вам нужно поставить некоторые, если о ширине.

попытаться изменить его размер

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

0

Мне понравилось

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

0

Просто хотел добавить мою версию для 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>


0

Я нашел способ предотвратить использование циклов для сжатия текста. Он корректирует размер шрифта, умножая его на соотношение между шириной контейнера и шириной содержимого. Таким образом, если ширина контейнера составляет 1/3 содержимого, размер шрифта будет уменьшен на 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/


0

Я пошел с решением GeekMonkey, но это слишком медленно. Что он делает, он устанавливает максимальный размер шрифта (maxFontPixels), а затем проверяет, подходит ли он внутри контейнера. иначе это уменьшает размер шрифта на 1 пиксель и проверяет снова. Почему бы просто не проверить предыдущий контейнер на высоту и передать это значение? (да, я знаю почему, но теперь я принял решение, которое работает только на высоте, а также имеет параметр min / max)

Вот гораздо более быстрое решение:

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>

Опять же: это решение ТОЛЬКО проверяет высоту контейнера. Вот почему эта функция не должна проверять, подходит ли элемент внутрь. Но я также реализовал минимальное / максимальное значение (40 мин, 150 макс), поэтому для меня это прекрасно работает (а также работает с изменением размера окна).


-1

Вот еще одна версия этого решения:

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";
    }
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.