Dynamiczny tekst o automatycznym rozmiarze, aby wypełnić pojemnik o stałym rozmiarze

312

Muszę wyświetlić wpisany przez użytkownika tekst w div o stałym rozmiarze. Chcę, aby rozmiar czcionki był automatycznie dostosowywany, aby tekst wypełniał pole tak bardzo, jak to możliwe.

Więc - jeśli div wynosi 400px x 300px. Jeśli ktoś wpisze ABC, to jest to naprawdę duża czcionka. Jeśli wprowadzą akapit, będzie to mała czcionka.

Prawdopodobnie chciałbym zacząć od maksymalnego rozmiaru czcionki - może 32px, i chociaż tekst jest zbyt duży, aby zmieścił się w pojemniku, zmniejsz rozmiar czcionki, aż do dopasowania.

GeekyMonkey
źródło
119
Prawdopodobnie jedna z najbardziej niesamowitych funkcji, które powinny zostać dodane do HTML5 / CSS3 bez potrzeby JS.
John Magnolia,
Wykonałem kilka pomiarów, w których zmieniłem długość tekstu dynamicznego i rozmiar pojemnika, aby dowiedzieć się, który rozmiar czcionki idealnie dopasuje tekst. Po przeprowadzeniu analizy regresji opracowałem prostą funkcję matematyczną, która automatycznie wygeneruje najlepszy rozmiar czcionki.
Przyjaciel Kim
2
Okazuje się, że wykres, który daje najlepszy rozmiar czcionki, podaje f (x) = g (litery) * (x / 1000) ^ n, gdzie g (x) jest prostą funkcją, n zmienia się w zależności od używana czcionka. (Chociaż może mieć standardową wartość dla wszystkich czcionek, jeśli nie chcesz jej poprawiać, aby uzyskać absolutnie idealną ...). x jest rozmiarem kontenera w pikselach kwadratowych.
Przyjaciel Kim
1
Jeśli nadal jesteś zainteresowany, mogę dodać odpowiedź. Osobiście uważam, że jest to o wiele lepsza metoda, aby wygenerować właściwy rozmiar czcionki, zamiast próbować i nie udać się, dopóki skrypt nie „poprawi”.
Przyjaciel Kim
1
Sprawdź moją odpowiedź na lepszy sposób na zrobienie tego
Hoffmann,

Odpowiedzi:

167

Dzięki Attack . Chciałem użyć jQuery.

Wskazałeś mi właściwy kierunek, i tak oto skończyłem:

Oto link do wtyczki: https://plugins.jquery.com/textfill/
I link do źródła: 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 });
});

i mój HTML jest taki

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

To jest moja pierwsza wtyczka jquery, więc prawdopodobnie nie jest tak dobra, jak powinna. Wskaźniki są z pewnością mile widziane.

GeekyMonkey
źródło
8
Właśnie go wyczyściłem i spakowałem jako wtyczkę dostępną na stronie jquery.com pod adresem plugins.jquery.com/project/TextFill
GeekyMonkey
3
@GeekyMonkey, ściągnąłeś wtyczkę? Po prostu podążyłem za duplikatem do tej strony i pomyślałem, że rzuciłem okiem, ale jQuery.com linki do Twojej witryny wracają 404.
David mówi o przywróceniu Moniki
Uwaga: Odkryłem, że z jakiegoś powodu ta wtyczka działa tylko wtedy, gdy div ($ ('. Jtextfill') w powyższym przykładzie) jest częścią głównego dokumentu. Wygląda na to, że .width () zwraca zero, gdy div jest osadzony w innych divach.
Jayesh
1
Linia „while” w tej pętli wydaje mi się niewłaściwa - wokół „||” powinny znajdować się nawiasy podwyrażenie. Sposób pisania teraz minimalny rozmiar czcionki jest sprawdzany tylko wtedy, gdy szerokość jest zbyt duża, a nie wysokość.
Pointy,
4
To podejście jest BARDZO powolne, za każdym razem, gdy czcionka zmienia rozmiar, element wymaga ponownego renderowania. Sprawdź moją odpowiedź, aby uzyskać lepszy sposób na zrobienie tego.
Hoffmann
52

Nie uważałem żadnego z poprzednich rozwiązań za wystarczające ze względu na słabą wydajność, więc stworzyłem własne, które używa prostej matematyki zamiast zapętlania. Powinien również działać dobrze we wszystkich przeglądarkach.

Zgodnie z tym przypadkiem testu wydajności jest on znacznie szybszy niż inne znalezione tutaj rozwiązania.

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

Jeśli chcesz przyczynić się, dodałem to do Gist .

mekwall
źródło
1
@ Jon, dzięki! Masz rację, że mój skrypt nie robi wielu wierszy, ale z drugiej strony OP nie poprosił o to specjalnie, więc twoje przypuszczenie może być błędne. Również tego rodzaju zachowanie nie ma większego sensu. Wydaje mi się, że najlepszym sposobem na dodanie obsługi wielu wierszy byłoby podzielenie ciągu znaków na podstawie ilości słów, a następnie obliczenie każdej części powyższym skryptem i najprawdopodobniej i tak byłoby szybsze.
mekwall
4
@Jon, bawiłem się trochę przy wypełnianiu tekstu wielowierszowego i skończyłem z tym rozwiązaniem . Metoda burzy piaskowej jest najprawdopodobniej bardziej dokładna, ale ta jest szybsza;)
mekwall 14.11.11
2
Oto wersja z minimalnym i maksymalnym rozmiarem czcionki: gist.github.com/1714284
Jess Telford
1
@Hoffmann Hmm. Moje rozwiązanie nie wywołuje .css("font-size")pętli. Skąd to masz? Moje rozwiązanie jest prawdopodobnie szybsze, ponieważ nie zawiera żadnych fantazyjnych elementów dodanych do wtyczki. Jesteś mile widziany, aby dodać wtyczkę do jsperf i zobaczymy, który z nich jest najszybszy;)
mekwall
1
@MarcusEkwall Och przepraszam, z jakiegoś powodu myślałem, że widziałem tam pętlę chwilę. Twoje podejście jest podobne do mojego i rzeczywiście byłoby trochę wolniejsze, ponieważ moja wtyczka robi również inne rzeczy (takie jak dopasowanie do wysokości i szerokości, centralizuje tekst i kilka innych opcji), to nie ma znaczenia, naprawdę powolna część wywołuje funkcję .css w pętli.
Hoffmann
35

Chociaż bardzo podoba mi się okazjonalne poparcie dla tej odpowiedzi (dzięki!), To naprawdę nie jest najlepsze podejście do tego problemu. Sprawdź niektóre inne wspaniałe odpowiedzi tutaj, zwłaszcza te, które znalazły rozwiązania bez zapętlania.


Jednak dla odniesienia, oto moja pierwotna odpowiedź :

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

A oto wersja z klasami :

<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>
atak
źródło
3
Przekonałem się, że to działa lepiej offsetWidth, musiałem również utworzyć zmienną dla rozmiaru, a następnie dołączyć pxtextSpan.style.fontSize = size+"px";
Wez
2
koniecznie „+” px ”.
sandun dhammika
@IdanShechter Przepraszam za długie, długie oczekiwanie! Dodano przykład!
atak
Dziękuję, ratuj życie! Nie wiem o jQuery, więc trzymam się twojego rozwiązania :)
vintproykt
32

Większość innych odpowiedzi używa pętli, aby zmniejszyć rozmiar czcionki, dopóki nie zmieści się na div, jest to BARDZO powolne, ponieważ strona musi ponownie renderować element za każdym razem, gdy czcionka zmienia rozmiar. W końcu musiałem napisać własny algorytm, aby działał w sposób, który pozwalał mi okresowo aktualizować jego zawartość bez zamrażania przeglądarki użytkownika. Dodałem kilka innych funkcji (obracanie tekstu, dodawanie dopełnienia) i spakowałem jako wtyczkę jQuery, którą możesz pobrać pod adresem:

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

po prostu zadzwoń

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

i będzie dobrze pasować do twojego pojemnika.

Zobacz to w akcji tutaj:

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

Na razie ma pewne ograniczenia, div musi mieć stałą wysokość i szerokość i nie obsługuje zawijania tekstu do wielu linii.

Będę pracował nad uzyskaniem opcji ustawienia maksymalnego rozmiaru czcionki.

Edycja: Znalazłem więcej problemów z wtyczką, nie obsługuje ona innych modeli pudełek poza standardowym i div nie może mieć marginesów ani ramek. Popracuję nad tym.

Edycja2: Naprawiłem te problemy i ograniczenia i dodałem więcej opcji. Możesz ustawić maksymalny rozmiar czcionki, a także możesz ograniczyć rozmiar czcionki, używając szerokości lub wysokości. Popracuję nad przyjęciem wartości maksymalnej szerokości i maksymalnej wysokości w elemencie zawijającym.

Edycja3: Zaktualizowałem wtyczkę do wersji 1.2.0. Główne czyszczenie kodu i nowe opcje (verticalAlign, horizontalAlign, textAlign) oraz obsługa wewnętrznych elementów wewnątrz znacznika rozpiętości (takich jak podział linii lub niesamowite ikony czcionek).

Hoffmann
źródło
1
Zastanawiam się, dlaczego zawijanie tekstu do wielu linii nie jest obsługiwane?
Manish Sapariya
1
@ManishSapariya Jest obsługiwany, ale musisz ręcznie dodać podział wiersza (tagi br). Powodem, dla którego nie obsługuję automatycznego zawijania tekstu jest to, że aby było szybkie (zmieniaj rozmiar czcionki tylko dwa razy zamiast wiele razy), muszę założyć, że tekst nie będzie zawijał się między słowami. Mój sposób działania wtyczki polega na ustawieniu rozmiaru czcionki na 1000px, a następnie zobaczeniu współczynnika rozmiaru tekstu w porównaniu do kontenera, a następnie zmniejszam rozmiar czcionki o ten sam współczynnik. Aby wesprzeć normalne zawijanie tekstu, musiałbym zastosować powolne podejście (kilkakrotnie zmniejszyć rozmiar czcionki), które jest bardzo wolne.
Hoffmann
Hej! Ponieważ nie ma tutaj prywatnych wiadomości, na StackOverflow będę musiał cię zapytać, komentując twoją odpowiedź. Uwielbiam twoją wtyczkę jQuery, ale nie mogę zmusić jej do działania. Dołączyłem odpowiednią bibliotekę jQuery, pobrałem wtyczkę i włączyłem ją. Teraz, gdy próbuję go użyć, konsola mówi „Niepoprawny typ błędu: niezdefiniowany nie jest funkcją”. Czy to coś, co znasz? Czy wiesz jak to naprawić? Dzięki
Gust van de Wal
@GustvandeWal Musisz dołączyć wtyczkę po dołączeniu biblioteki jquery
Hoffmann
Zrobiłem. Mam <script type = "text / javascript" src = " code.jquery.com/jquery-2.1.1.min.js"></… src" js / jquery-bigtext.js "> </ script > Przeglądarka nie informuje mnie, że nie może załadować biblioteki jQuery ani wtyczki.
Gust van de Wal
9

Jest to oparte na tym, co opublikował GeekyMonkey, z pewnymi modyfikacjami.

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

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

    return this.each(function() {

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

        do {

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

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

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

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

    });

};

})(jQuery);
sandstrom
źródło
różnica polega na tym, że może uwzględniać wiele elementów potomnych i że uwzględnia dopełnienie. Używa rozmiaru czcionki jako domyślnego maksymalnego rozmiaru, aby uniknąć mieszania javascript i css.
sandstrom
5
To wspaniale, ale jak go używać? Robię $ ('. Outer'). Textfill (); i nie dostaję żadnej zmiany.
Drew Baker
3
Dzięki, to bardzo miłe wdrożenie. Jedna rzecz, na którą natknąłem się: jeśli masz do czynienia z bardzo długimi łańcuchami tekstowymi i bardzo wąskimi kontenerami, łańcuch tekstowy wystaje z kontenera, ale zewnętrzna szerokość będzie nadal obliczana tak, jakby nie. Rzut „zawijanie wyrazów: słowo-łamanie;” do twojego CSS dla tego kontenera, to naprawi ten problem.
Jon
8

Oto ulepszona metoda zapętlania, która wykorzystuje wyszukiwanie binarne, aby znaleźć największy możliwy rozmiar, który pasuje do elementu nadrzędnego w jak najmniejszej liczbie kroków (jest to szybsze i dokładniejsze niż przeskakiwanie o ustalony rozmiar czcionki). Kod jest również zoptymalizowany na kilka sposobów pod kątem wydajności.

Domyślnie zostanie wykonanych 10 kroków wyszukiwania binarnego, które osiągną 0,1% optymalnego rozmiaru. Zamiast tego możesz ustawić numIter na pewną wartość N, aby uzyskać wartość w zakresie 1/2 ^ N optymalnego rozmiaru.

Zadzwoń za pomocą selektora CSS, np .: 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);
        }
    });
};
Luke Hutchison
źródło
Uwielbiam tę opcję. Zmodyfikowałem go, aby dodać parametry dla vAligni padding. vAlign == trueustawia wysokość linii wybranego elementu na wysokość rodziców. Wypełnienie zmniejsza końcowy rozmiar o przekazaną wartość. Domyślnie jest to 5. Myślę, że wygląda naprawdę ładnie.
Adwokat diabła
6

Stworzyłem dyrektywę dla AngularJS - mocno zainspirowaną odpowiedzią GeekyMonkey, ale bez zależności jQuery.

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

Narzut

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

Dyrektywa

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();
      });
    }
  };
});
sqren
źródło
Jednym z problemów, z którymi miałem do czynienia, jest to, że resizeTextwydaje się , że jest wywoływana, zanim ng-bindfaktycznie przypisze tekst do elementu, co powoduje zmianę rozmiaru na podstawie poprzedniego tekstu, a nie bieżącego. Nie jest to takie złe w powyższej wersji demonstracyjnej, w której jest wywoływana wielokrotnie w miarę pisania przez użytkownika, ale jeśli zostanie wywołana raz, przechodząc od wartości null do wartości rzeczywistej (jak w powiązaniu jednokierunkowym), pozostaje na maksymalnym rozmiarze.
Miral
5

Rozwinąłem powyższy skrypt od Marcusa Ekwalla: https://gist.github.com/3945316 i poprawiłem go zgodnie z moimi preferencjami, teraz uruchamia się, gdy okno jest zmieniane, aby dziecko zawsze mieściło się w pojemniku. Wkleiłem poniższy skrypt w celach informacyjnych.

(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);
nimrod
źródło
2
Wspaniale, że próbujesz pomóc pytającemu wyjść. Jednak pozostawienie odpowiedzi zawierającej tylko link może w niektórych przypadkach być szkodliwe. Chociaż twoja odpowiedź jest teraz dobra, gdyby link umarł, twoja odpowiedź straci na wartości. Przyda się więc podsumowanie treści artykułu w odpowiedzi. Zobacz to pytanie w celu wyjaśnienia.
Cody Guldner
5

Oto moja modyfikacja odpowiedzi PO.

Krótko mówiąc, wiele osób, które próbowały zoptymalizować tę sytuację, narzekało, że używana jest pętla. Tak, podczas gdy pętle mogą być wolne, inne podejścia mogą być niedokładne.

Dlatego moje podejście wykorzystuje wyszukiwanie binarne, aby znaleźć najlepszy rozmiar czcionki:

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

Pozwala to również elementowi określić minimalną i maksymalną czcionkę:

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

Co więcej, ten algorytm jest bezjednostkowy. Można określić em, rem,% , itd. I będzie wykorzystywać że jego wynik końcowy.

Oto skrzypce: https://jsfiddle.net/fkhqhnqe/1/

Bum
źródło
2

Miałem dokładnie ten sam problem z moją witryną. Mam stronę wyświetlaną na projektorze, na ścianach, na dużych ekranach ..

Ponieważ nie znam maksymalnego rozmiaru mojej czcionki, ponownie użyłem wtyczki powyżej @GeekMonkey, ale zwiększałem rozmiar czcionki:

$.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 );
        });
    };
guillaumepotier
źródło
+1 za bycie jedyną wtyczką na tej stronie, która faktycznie działała dla mnie!
skybondsor
2
Ta wtyczka powoduje awarię strony.
Jezen Thomas
1

Oto wersja zaakceptowanej odpowiedzi, która może również przyjmować parametr minFontSize.

(function($) {
    /**
    * Resizes an inner element's font so that the inner element completely fills the outer element.
    * @author Russ Painter [email protected]
    * @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);
blak3r
źródło
dzięki, chociaż myślę, że masz średnik na górze kodu, którego nie powinno być
Patrick Moore
1

Możesz użyć FitText.js ( strona github ), aby rozwiązać ten problem. Jest naprawdę mały i wydajny w porównaniu do TextFill. TextFill używa drogiej pętli while i FitText nie.

Również FitText jest bardziej elastyczny (używam go w proyect ze specjalnymi wymaganiami i działa jak mistrz!).

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>

Możesz także ustawić opcje:

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

CSS:

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

A jeśli potrzebujesz, FitText ma również wersję bez jQuery .

eduludi
źródło
Czy tekst tekstowy uwzględnia wysokość?
Manish Sapariya
1
@ManishSapariya nie, nie ma. Po prostu dzieli szerokość pojemnika przez 10 i używa go jako rozmiaru czcionki.
Dan H
1

EDYCJA: Ten kod był używany do wyświetlania notatek na górze filmu HTML5. Zmienia rozmiar czcionki w locie, gdy rozmiar wideo jest zmieniany (podczas zmiany rozmiaru okna przeglądarki). Notatki zostały połączone z filmem (podobnie jak notatki na YouTube), dlatego kod używa instancji zamiast uchwytu DOM bezpośrednio.

Zgodnie z życzeniem wrzucę kod, którego użyłem, aby to osiągnąć. (Ramki tekstowe nad filmem HTML5). Kod został napisany dawno temu i szczerze mówiąc, myślę, że jest dość niechlujny. Ponieważ na pytanie już udzielono odpowiedzi, a odpowiedź już dawno została zaakceptowana, nie zawracam sobie głowy przepisywaniem tego. Ale jeśli ktoś chce to nieco uprościć, nie ma za co!

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

Prawdopodobnie będziesz musiał dostosować te liczby z rodziny czcionek do rodziny czcionek. Dobrym sposobem na to jest pobranie bezpłatnego wizualizatora grafów o nazwie GeoGebra. Zmień długość tekstu i rozmiar pudełka. Następnie ręcznie ustaw rozmiar. Wykreślić ręczne wyniki w układzie współrzędnych. Następnie wpisujesz dwa równania, które tu opublikowałem, i poprawiasz liczby, aż „mój” wykres pasuje do twoich ręcznie wykreślonych punktów.

Przyjaciel Kim
źródło
1

Proponowane rozwiązania iteracyjne można znacznie przyspieszyć na dwóch frontach:

1) Pomnóż rozmiar czcionki przez pewną stałą, zamiast dodawać lub odejmować 1.

2) Po pierwsze, zero przy użyciu stałej kursu, powiedzmy, podwójnej wielkości każdej pętli. Następnie, mając przybliżony pomysł, od czego zacząć, zrób to samo, dokładniej dostosowując, powiedzmy, pomnóż przez 1,1. Podczas gdy perfekcjonista może chcieć dokładnego rozmiaru w pikselach idealnej czcionki, większość obserwatorów nie zauważa różnicy między 100 a 110 pikseli. Jeśli jesteś perfekcjonistą, powtórz po raz trzeci z jeszcze lepszą regulacją.

Zamiast pisać konkretną procedurę lub wtyczkę, która odpowiada na dokładne pytanie, polegam tylko na podstawowych pomysłach i piszę różne wersje kodu, aby poradzić sobie z wszelkimi problemami związanymi z układem, a nie tylko z tekstem, włączając dopasowanie div, zakresów, obrazów. .. według szerokości, wysokości, powierzchni, ... w kontenerze, pasując do innego elementu ....

Oto przykład:

  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');
DaveWalley
źródło
1

To najbardziej eleganckie rozwiązanie, jakie stworzyłem. Korzysta z wyszukiwania binarnego, wykonując 10 iteracji. Naiwnym sposobem było wykonanie pętli while i zwiększenie rozmiaru czcionki o 1, aż element zacznie się przepełniać. Za pomocą można określić, kiedy element zaczyna się przepełniać element.offsetHeight i element.scrollHeight . Jeśli scrollHeight jest większy niż offsetHeight, masz za duży rozmiar czcionki.

Wyszukiwanie binarne jest do tego znacznie lepszym algorytmem. Jest również ograniczony liczbą iteracji, które chcesz wykonać. Wystarczy wywołać flexFont i wstawić identyfikator div, a dopasuje on rozmiar czcionki między 8px oraz x 96 pikseli .

Spędziłem trochę czasu badając ten temat i wypróbowując różne biblioteki, ale ostatecznie uważam, że jest to najłatwiejsze i najprostsze rozwiązanie, które faktycznie zadziała.

Uwaga: jeśli chcesz, możesz zmienić użycie offsetWidthi scrollWidth/ lub dodać oba do tej funkcji.

// 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
    };
}
Nick Gallimore
źródło
Dzięki, wygląda świetnie! Właśnie dostaję ReferenceError: lastSizeTooSmall is not defined. Może to gdzieś trzeba zdefiniować?
ndbroadbent
0

Mam ten sam problem, a rozwiązaniem jest użycie javascript do kontrolowania rozmiaru czcionki. Sprawdź ten przykład na codepen:

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

Ten przykład dotyczy tylko wysokości, być może trzeba wstawić trochę, jeśli chodzi o szerokość.

spróbuj zmienić jego rozmiar

<!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>
Heitor Giacomini
źródło
0

Podobało mi się

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));
Tuan Nguyen
źródło
0

Chciałem tylko dodać moją wersję dla 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>

wutzebaer
źródło
0

Znalazłem sposób, aby zapobiec użyciu pętli do zmniejszania tekstu. Dostosowuje rozmiar czcionki, mnożąc ją dla szybkości między szerokością pojemnika a szerokością zawartości. Jeśli więc szerokość kontenera wynosi 1/3 zawartości, rozmiar czcionki zostanie zmniejszony o 1/3 i będzie zawierał szerokość kontenera. Aby zwiększyć skalę, użyłem pętli while, dopóki zawartość nie będzie większa niż kontener.

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

Ten kod jest częścią testu, który przesłałem do Github https://github.com/ricardobrg/fitText/

Ricardo BRGWeb
źródło
0

Wybrałem rozwiązanie geekMonkey, ale jest zbyt wolne. Robi to, że dostosowuje rozmiar czcionki do maksimum (maxFontPixels), a następnie sprawdza, czy mieści się w pojemniku. w przeciwnym razie zmniejsza rozmiar czcionki o 1px i sprawdza ponownie. Może po prostu sprawdzisz poprzedni pojemnik pod kątem wysokości i podasz tę wartość? (tak, wiem dlaczego, ale teraz stworzyłem rozwiązanie, które działa tylko na wysokości i ma również opcję min / max)

Oto o wiele szybsze rozwiązanie:

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

i to byłby HTML:

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

Znowu: to rozwiązanie WYŁĄCZNIE sprawdza wysokość pojemnika. Dlatego ta funkcja nie musi sprawdzać, czy element pasuje do środka. Ale zaimplementowałem również wartość min / max (40min, 150max), więc dla mnie działa to doskonale dobrze (a także działa na zmianę rozmiaru okna).

honk31
źródło
-1

Oto kolejna wersja tego rozwiązania:

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";
    }
}
PaulG
źródło