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


312

Мені потрібно відобразити введений користувачем текст у діві фіксованого розміру. Мені потрібно, щоб розмір шрифту був автоматично відрегульований, щоб текст максимально заповнив поле.

Отже - якщо діва 400px x 300px. Якщо хтось входить в ABC, це справді великий шрифт. Якщо вони вводять абзац, то це був би крихітний шрифт.

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


119
Напевно, одна з найдивовижніших функцій, яку слід було б додати до HTML5 / CSS3 без необхідності JS.
Джон Магнолія

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

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

1
Якщо вас все ще цікавить, я можу додати відповідь. Особисто я вважаю, що це набагато кращий спосіб генерувати правильний розмір шрифту, в першу чергу, замість того, щоб намагатися і не вдаватись до тих пір, поки сценарій «не встигає».
Друг Кіма

1
Перевірте мою відповідь на кращий спосіб зробити це
Hoffmann

Відповіді:


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

1
Рядок "while" в цьому циклі для мене виглядає неправильним - навколо "||" мають бути дужки. піддекспресія. Як написано зараз, мінімальний розмір шрифту перевіряється лише тоді, коли ширина занадто велика, а не висота.
Pointy

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
@Jon, дякую! Ви маєте рацію, що мій сценарій не робить декілька рядків, але знову ж таки ОП спеціально не просив цього, щоб ваше припущення могло бути неправильним. Крім того, така поведінка не має особливого сенсу. Я думаю, що найкращим способом додати багаторядкову підтримку було б розділити рядок на основі кількості слів, а потім обчислити кожну частину вищевказаним сценарієм, і це, швидше за все, буде швидше.
mekwall

4
@Jon, я трохи пограв з багаторядковим заповненням тексту і закінчив це рішення . метод піщаної бурі, швидше за все, більш точний, але цей швидший;)
mekwall

2
Ось версія з мінімальним розміром шрифту, а також максимальним: gist.github.com/1714284
Jess Telford,

1
@Hoffmann Hmm. Моє рішення не викликає .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"' є необхідним.
Sandun dhammika

@IdanShechter Вибачте за довго, довго чекайте! Додано приклад!
напад

Дякую, рятувальник життя! Я не знаю jQuery, тому я дотримуюся вашого рішення :)
vintproykt

32

Більшість інших відповідей використовують цикл для зменшення розміру шрифту до тих пір, поки він не впишеться у діву, це ДУЖЕ повільно, оскільки сторінка потребує повторного відображення елемента кожного разу, коли шрифт змінює розмір. Зрештою, мені довелося написати власний алгоритм, щоб змусити його виконувати таким чином, що дозволило мені періодично оновлювати його вміст, не заморожуючи браузер користувача. Я додав ще якусь функціональність (обертовий текст, додавання прокладок) і упакував його як плагін jQuery, ви можете отримати його за адресою:

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

просто зателефонуйте

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

і він добре поміститься на вашому контейнері.

Дивіться це в дії тут:

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

Наразі він має деякі обмеження, div повинен мати фіксовану висоту та ширину, і він не підтримує загортання тексту у кілька рядків.

Я працюю над тим, щоб отримати можливість встановити максимальний розмір шрифту.

Редагувати: Я знайшов ще деякі проблеми з плагіном, він не обробляє інші бокс-моделі, крім стандартної, і div не може мати поля або межі. Я над цим працюю.

Edit2: я тепер виправив ці проблеми та обмеження та додав ще варіанти. Ви можете встановити максимальний розмір шрифту, а також можете обмежити розмір шрифту за допомогою ширини, висоти або обох. Я буду працювати над прийняттям значень максимальної ширини та висоти в елементі обгортки.

Edit3: я оновив плагін до версії 1.2.0. Основне очищення коду та нові параметри (вертикальне вирівнювання, горизонтальне вирівнювання, текстове вирівнювання) та підтримка внутрішніх елементів всередині тегу прольоту (наприклад, перерив рядків або приголомшливі шрифти).


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

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

Гей! Оскільки приватних повідомлень тут немає, в StackOverflow мені доведеться запитати вас, коментуючи вашу відповідь. Я люблю ваш плагін jQuery, але я не можу змусити його працювати на мене. Я включив правильну бібліотеку jQuery, завантажив ваш плагін і включив його. Тепер, коли я намагаюся використовувати його, на консолі написано "Uncaught TypeError: undefined is not a function". Це щось вам знайоме? Ви знаєте, як це виправити? Спасибі
Густ ван де Валь

@GustvandeWal Вам потрібно включити плагін після включення бібліотеки jquery
Hoffmann

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

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.
пісок

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

3
Дякую, це дуже приємна реалізація. Одне, на що я зіткнувся: якщо ви маєте справу з дуже довгими текстовими рядками та дуже вузькими контейнерами, текстовий рядок буде стирчати з контейнера, але зовнішня ширина все одно буде обчислюватися так, ніби її немає. Кидок "слово-обгортка: перерва-слово;" у ваш 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 використовує дорогий цикл, а цикл 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);

Можливо, вам доведеться змінити ці числа від сімейства шрифтів до сімейства шрифтів. Хороший спосіб зробити це - завантажити безкоштовний візуалізатор графіків під назвою GeoGebra. Змініть довжину тексту та розмір поля. Потім ви вручну встановлюєте розмір. Встановіть вручну результати в систему координат. Потім ви вводите два рівняння, які я тут розмістив, і ви налаштовуєте числа, поки "мій" графік не відповідає вашим власним точкам вручну.


1

Запропоновані ітеративні рішення можна різко просунути на два фронти:

1) Помножте розмір шрифту на деяку постійну, а не додаючи або віднімаючи 1.

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

Замість того, щоб писати певний розпорядок або плагін, який відповідає на точне запитання, я просто покладаюся на основні ідеї і пишу варіанти коду, щоб обробляти всілякі проблеми з компонуванням, а не лише текст, включаючи розміщення знаків, прольотів, зображень. .. по ширині, висоті, площі, ... всередині контейнера, що відповідає іншому елементу ....

Ось приклад:

  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 ітерацій. Наївним способом було зробити цикл часу і збільшити розмір шрифту на 1, поки елемент не почав переповнюватися. Ви можете визначити, коли елемент починає переповнюватися, використовуючи element.offsetHeight та element.scrollHeight . Якщо scrollHeight більший, ніж offsetHeight, у вас занадто великий розмір шрифту.

Бінарний пошук є набагато кращим алгоритмом цього. Він також обмежений кількістю ітерацій, які ви хочете виконати. Просто зателефонуйте на flexFont та вставте ідентифікатор div, і він відрегулює розмір шрифту між 8px та 96px .

Я витратив деякий час на дослідження цієї теми та пробуючи різні бібліотеки, але в кінцевому підсумку вважаю, що це найпростіше і найпростіше рішення, яке насправді спрацює.

Зверніть увагу, якщо ви хочете, ви можете змінити використання 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

Просто хотів додати мою версію для вмісту матеріалів.

$.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 і буде шириною контейнера. Для масштабування я використовував цикл час, поки вміст не перевищує контейнер.

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>

Знову ж таки: це рішення ТІЛЬКИ перевіряє висоту контейнера. Ось чому цю функцію не потрібно перевіряти, чи вписується елемент всередину. Але я також реалізував значення min / max (40min, 150max), тому для мене це працює чудово (а також працює на розмірі вікон).


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