Dynamischer Text in automatischer Größe, um Container mit fester Größe zu füllen

312

Ich muss vom Benutzer eingegebenen Text in einem Div mit fester Größe anzeigen. Ich möchte, dass die Schriftgröße automatisch angepasst wird, damit der Text das Feld so weit wie möglich ausfüllt.

Also - Wenn das div 400px x 300px ist. Wenn jemand ABC betritt, ist es eine wirklich große Schrift. Wenn sie einen Absatz eingeben, ist dies eine winzige Schriftart.

Ich möchte wahrscheinlich mit einer maximalen Schriftgröße beginnen - vielleicht 32 Pixel - und während der Text zu groß ist, um in den Container zu passen, verkleinern Sie die Schriftgröße, bis sie passt.

GeekyMonkey
quelle
119
Wahrscheinlich die erstaunlichste Funktion, die HTML5 / CSS3 ohne JS hinzugefügt werden sollte.
John Magnolia
Ich habe einige Messungen durchgeführt, bei denen ich die Länge eines dynamischen Textes und die Größe des Containers geändert habe, um herauszufinden, durch welche Schriftgröße der Text perfekt passt. Und nach einigen Regressionsanalysen habe ich eine einfache mathematische Funktion entwickelt, die automatisch die beste Schriftgröße generiert.
Freund von Kim
2
Es stellt sich tatsächlich heraus, dass das Diagramm mit der besten Schriftgröße durch f (x) = g (Buchstaben) * (x / 1000) ^ n gegeben ist, wobei g (x) eine einfache Funktion ist, von der n abhängig ist die Schriftart, die Sie verwenden. (Obwohl es einen Standardwert für alle Schriftarten haben kann, wenn Sie es nicht optimieren möchten, um es absolut perfekt zu machen ...). x ist die Größe des Containers in quadratischen Pixeln.
Freund von Kim
1
Wenn Sie noch interessiert sind, kann ich eine Antwort hinzufügen. Persönlich denke ich, dass es eine viel bessere Methode ist, die richtige Schriftgröße zu generieren, anstatt zu versuchen und zu scheitern, bis das Skript "es richtig macht".
Freund von Kim
1
Überprüfen Sie meine Antwort für einen besseren Weg, um dies zu tun
Hoffmann

Antworten:

167

Danke Attack . Ich wollte jQuery verwenden.

Sie haben mich in die richtige Richtung gelenkt, und am Ende habe ich Folgendes erreicht:

Hier ist ein Link zum Plugin: https://plugins.jquery.com/textfill/
Und ein Link zur Quelle: 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 });
});

und mein HTML ist so

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

Dies ist mein erstes jquery-Plugin, daher ist es wahrscheinlich nicht so gut, wie es sein sollte. Zeiger sind auf jeden Fall willkommen.

GeekyMonkey
quelle
8
Ich habe es gerade aufgeräumt und als Plugin verpackt, das von jquery.com unter plugins.jquery.com/project/TextFill
GeekyMonkey am
3
@GeekyMonkey, hast du das Plugin gezogen? Ich bin gerade einem betrogenen Link zu dieser Seite gefolgt und dachte, ich würde einen Blick darauf werfen, aber die jQuery.com-Links zu Ihrer Website kehren zurück 404.
David sagt, Monica
Hinweis: Ich habe festgestellt, dass dieses Plugin aus irgendeinem Grund nur funktioniert, wenn das div ($ ('. Jtextfill') im obigen Beispiel) Teil des Stammdokuments ist. Es sieht so aus, als ob .width () Null zurückgibt, wenn das Div in andere Divs eingebettet ist.
Jayesh
1
Die "while" -Zeile in dieser Schleife sieht für mich falsch aus - es sollte Klammern um das "||" geben. Unterausdruck. So wie es jetzt geschrieben ist, wird das Minimum der Schriftgröße nur überprüft, wenn die Breite zu groß ist, nicht die Höhe.
Pointy
4
Dieser Ansatz ist SEHR langsam. Jedes Mal, wenn die Schriftgröße geändert wird, muss das Element neu gerendert werden. Überprüfen Sie meine Antwort für einen besseren Weg, dies zu tun.
Hoffmann
52

Ich fand keine der vorherigen Lösungen aufgrund der schlechten Leistung ausreichend, also habe ich meine eigene erstellt, die einfache Mathematik anstelle von Schleifen verwendet. Sollte auch in allen Browsern funktionieren.

Nach diesem Leistungstestfall ist es viel schneller als die anderen hier gefundenen Lösungen.

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

Wenn Sie dazu beitragen möchten, habe ich dies zu Gist hinzugefügt .

Mekwall
quelle
1
@ Jon, danke! Sie haben Recht, dass mein Skript nicht mehrere Zeilen enthält, aber das OP hat nicht ausdrücklich danach gefragt, sodass Ihre Annahme möglicherweise falsch ist. Auch diese Art von Verhalten macht imo wenig Sinn. Ich denke, der beste Weg, um mehrzeilige Unterstützung hinzuzufügen, wäre, die Zeichenfolge basierend auf der Anzahl der Wörter zu teilen und dann jeden Teil mit dem obigen Skript zu berechnen, und es wäre höchstwahrscheinlich sowieso schneller.
Mekwall
4
@ Jon, ich habe ein bisschen mit mehrzeiliger Textfüllung herumgespielt und bin zu dieser Lösung gekommen . Sandsturm Methode ist höchstwahrscheinlich genauer, aber diese ist schneller;)
Mekwall
2
Hier ist eine Version mit minimaler und maximaler Schriftgröße
Jess Telford
1
@Hoffmann Hmm. Meine Lösung ruft keine .css("font-size")Schleife auf. Woher hast du das? Meine Lösung ist wahrscheinlich schneller, da sie nichts Besonderes enthält, das Sie Ihrem Plugin hinzugefügt haben. Du kannst gerne dein Plugin zum jsperf hinzufügen und wir werden sehen, welches das schnellste ist;)
mekwall
1
@MarcusEkwall Oh, tut mir leid, aus irgendeinem Grund habe ich dort eine while-Schleife gesehen. Ihr Ansatz ähnelt meinem eigenen und in der Tat wäre mein Ansatz etwas langsamer, da mein Plugin auch einige andere Aufgaben ausführt (z. B. das Anpassen an Höhe und Breite, das Zentralisieren des Texts und einige andere Optionen), nicht das ist wichtig, der wirklich langsame Teil ruft die .css-Funktion innerhalb einer Schleife auf.
Hoffmann
35

So sehr ich die gelegentlichen Upvotes liebe, die ich für diese Antwort bekomme (danke!), Dies ist wirklich nicht der beste Ansatz für dieses Problem. Schauen Sie sich hier einige der anderen wunderbaren Antworten an, insbesondere diejenigen, die Lösungen ohne Schleifen gefunden haben.


Als Referenz hier noch meine ursprüngliche Antwort :

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

Und hier ist eine Version mit Klassen :

<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>
Attacke
quelle
3
Ich fand, dass dies besser funktionierte offsetWidth, ich musste auch eine Variable für die Größe erstellen und dann den px anhängentextSpan.style.fontSize = size+"px";
Wez
2
sicher, dass '+ "px"' notwendig ist.
Sandun Dhammika
@IdanShechter Entschuldigung für das lange, lange Warten! Ein Beispiel hinzugefügt!
Angriff
Danke, Lebensretter! Ich kenne jQuery nicht, also
bleibe
32

Die meisten anderen Antworten verwenden eine Schleife, um die Schriftgröße zu verringern, bis sie auf das div passt. Dies ist SEHR langsam, da die Seite das Element jedes Mal neu rendern muss, wenn sich die Schriftgröße ändert. Ich musste schließlich meinen eigenen Algorithmus schreiben, damit er so funktioniert, dass ich seinen Inhalt regelmäßig aktualisieren konnte, ohne den Benutzerbrowser einzufrieren. Ich habe einige andere Funktionen hinzugefügt (Text drehen, Auffüllen hinzufügen) und es als jQuery-Plugin verpackt. Sie erhalten es unter:

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

einfach anrufen

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

und es passt gut auf Ihren Behälter.

Sehen Sie es hier in Aktion:

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

Im Moment gibt es einige Einschränkungen, das div muss eine feste Höhe und Breite haben und es wird nicht unterstützt, Text in mehrere Zeilen zu umbrechen.

Ich werde daran arbeiten, eine Option zum Festlegen der maximalen Schriftgröße zu erhalten.

Bearbeiten: Ich habe einige weitere Probleme mit dem Plugin gefunden, es behandelt kein anderes Box-Modell als das Standard-Modell und das Div kann keine Ränder oder Ränder haben. Ich werde daran arbeiten.

Edit2: Ich habe diese Probleme und Einschränkungen jetzt behoben und weitere Optionen hinzugefügt. Sie können die maximale Schriftgröße festlegen und die Schriftgröße entweder mit Breite, Höhe oder beidem begrenzen. Ich werde daran arbeiten, Werte für die maximale Breite und die maximale Höhe im Wrapper-Element zu akzeptieren.

Edit3: Ich habe das Plugin auf Version 1.2.0 aktualisiert. Umfassende Bereinigung des Codes und neuer Optionen (vertikalAlign, horizontalAlign, textAlign) und Unterstützung für innere Elemente innerhalb des Span-Tags (wie Zeilenumbrüche oder Symbole für großartige Schriftarten).

Hoffmann
quelle
1
Ich frage mich, warum das Umschließen von Text in mehrere Zeilen nicht unterstützt wird.
Manish Sapariya
1
@ManishSapariya Es wird unterstützt, aber Sie müssen die Zeilenumbrüche (br-Tags) manuell hinzufügen. Der Grund, warum ich den automatischen Textumbruch nicht unterstütze, ist, dass ich davon ausgehen muss, dass der Text nicht zwischen Wörtern umgebrochen wird, um ihn schnell zu machen (nur zweimal statt mehrmals die Schriftgröße ändern). Die Art und Weise, wie mein Plugin funktioniert, besteht darin, die Schriftgröße auf 1000px einzustellen und dann den Faktor der Größe zu sehen, mit der der Text mit dem Container verglichen wird. Dann reduziere ich die Schriftgröße um denselben Faktor. Um den normalen Textumbruch zu unterstützen, müsste ich den langsamen Ansatz verwenden (die Schriftgröße mehrmals reduzieren), der sehr langsam ist.
Hoffmann
Hallo! Da es hier bei StackOverflow keine privaten Nachrichten gibt, muss ich Sie fragen, indem ich Ihre Antwort kommentiere. Ich liebe dein jQuery-Plugin, aber ich kann es nicht für mich zum Laufen bringen. Ich habe die richtige jQuery-Bibliothek aufgenommen, Ihr Plugin heruntergeladen und aufgenommen. Wenn ich jetzt versuche, es zu verwenden, sagt die Konsole "Nicht gefangener Typfehler: undefiniert ist keine Funktion". Ist Ihnen das vertraut? Wissen Sie, wie Sie das beheben können? Danke
Gust van de Wal
@GustvandeWal Sie müssen das Plugin nach dem Einfügen der JQuery-Bibliothek einschließen
Hoffmann
Ich tat. Ich habe <script type = "text / javascript" src =“ code.jquery.com/jquery-2.1.1.min.js"></... src "js / jquery-bigtext.js"> </ script > Der Browser benachrichtigt mich nicht, dass er die jQuery-Bibliothek oder das Plugin nicht laden kann.
Gust van de Wal
9

Dies basiert auf dem, was GeekyMonkey oben veröffentlicht hat, mit einigen Änderungen.

; (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
quelle
Der Unterschied besteht darin, dass mehrere untergeordnete Elemente verwendet werden können und dass das Auffüllen berücksichtigt wird. Verwendet die Schriftgröße als Standardmaximalgröße, um das Mischen von Javascript und CSS zu vermeiden.
Sandstrom
5
Das ist großartig, aber wie verwende ich es? Ich mache $ ('. Outer'). Textfill (); und ich bekomme kein Wechselgeld.
Drew Baker
3
Danke, das ist eine sehr schöne Implementierung. Eine Sache, auf die ich gestoßen bin: Wenn Sie mit sehr langen Textzeichenfolgen und sehr schmalen Containern arbeiten, ragt die Textzeichenfolge aus dem Container heraus, aber die äußere Breite wird trotzdem so berechnet, als ob dies nicht der Fall wäre. Werfen Sie "Zeilenumbruch: Umbruchwort;" In Ihrem CSS für diesen Container wird dieses Problem behoben.
Jon
8

Hier ist eine verbesserte Schleifenmethode, bei der mithilfe der binären Suche in möglichst wenigen Schritten die größtmögliche Größe gefunden wird, die in das übergeordnete Element passt (dies ist schneller und genauer als das Schrittmachen einer festen Schriftgröße). Der Code ist auch auf verschiedene Arten für die Leistung optimiert.

Standardmäßig werden 10 binäre Suchschritte ausgeführt, die innerhalb von 0,1% der optimalen Größe liegen. Sie können stattdessen numIter auf einen Wert N setzen, um innerhalb von 1/2 ^ N der optimalen Größe zu sein.

Rufen Sie es mit einem CSS-Selektor auf, z. 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
quelle
Ich liebe diese Option. Ich habe es geändert, um Parameter für vAlignund hinzuzufügen padding. vAlign == trueLegt die Zeilenhöhe des ausgewählten Elements auf die Höhe der übergeordneten Elemente fest. Durch Auffüllen wird die endgültige Größe um den übergebenen Wert verringert. Der Standardwert ist 5. Ich denke, es sieht wirklich gut aus.
Devil's Advocate
6

Ich habe eine Direktive für AngularJS erstellt - stark inspiriert von GeekyMonkeys Antwort, aber ohne die jQuery-Abhängigkeit.

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

Markup

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

Richtlinie

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
quelle
Ein Problem, das ich damit hatte, ist, dass resizeTextes aufgerufen zu werden scheint, bevor der ng-bindText dem Element tatsächlich zugewiesen wird, was dazu führt, dass die Größe auf dem vorherigen Text und nicht auf dem aktuellen Text basiert. Dies ist in der obigen Demo nicht schlecht, in der es wiederholt aufgerufen wird, während der Benutzer eingibt. Wenn es jedoch einmal aufgerufen wird und von null auf den tatsächlichen Wert übergeht (wie bei einer Einwegbindung), bleibt es bei der maximalen Größe.
Miral
5

Ich habe das obige Skript von Marcus Ekwall gegabelt: https://gist.github.com/3945316 und es an meine Einstellungen angepasst. Es wird jetzt ausgelöst, wenn die Fenstergröße geändert wird, sodass das Kind immer in seinen Container passt. Ich habe das folgende Skript als Referenz eingefügt.

(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
quelle
2
Es ist großartig, dass Sie versuchen, dem Fragesteller zu helfen. In einigen Fällen kann es jedoch schädlich sein, eine Antwort nur mit einem Link zu hinterlassen. Während Ihre Antwort jetzt gut ist, würde Ihre Antwort ihren Wert verlieren, wenn der Link jemals sterben würde. Daher ist es hilfreich, wenn Sie den Inhalt des Artikels in Ihrer Antwort zusammenfassen. Siehe diese Frage zur Klarstellung.
Cody Guldner
5

Hier ist meine Änderung der Antwort des OP.

Kurz gesagt, viele Leute, die versuchten, dies zu optimieren, beklagten sich darüber, dass eine Schleife verwendet wurde. Ja, während Schleifen langsam sein können, können andere Ansätze ungenau sein.

Daher verwendet mein Ansatz die binäre Suche , um die beste Schriftgröße zu finden:

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

Dadurch kann das Element auch die minimale und maximale Schriftart angeben:

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

Darüber hinaus ist dieser Algorithmus einheitlos. Sie angeben em, rem,% usw. , und es wird , dass für sein endgültiges Ergebnis verwenden.

Hier ist die Geige: https://jsfiddle.net/fkhqhnqe/1/

Boom
quelle
2

Ich hatte genau das gleiche Problem mit meiner Website. Ich habe eine Seite, die auf einem Projektor, an Wänden, auf großen Bildschirmen angezeigt wird.

Da ich die maximale Schriftgröße nicht kenne, habe ich das obige Plugin von @GeekMonkey erneut verwendet, aber die Schriftgröße erhöht:

$.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
quelle
+1 für das einzige Plugin auf dieser Seite, das tatsächlich für mich funktioniert hat!
Skybondsor
2
Dieses Plugin stürzt die Seite für mich ab.
Jezen Thomas
1

Hier ist eine Version der akzeptierten Antwort, die auch einen minFontSize-Parameter annehmen kann.

(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
quelle
Vielen Dank, obwohl ich denke, Sie haben ein Semikolon am oberen Rand des Codes, das nicht vorhanden sein sollte
Patrick Moore
1

Sie können FitText.js ( Github-Seite) verwenden ) verwenden, um dieses Problem zu lösen. Ist im Vergleich zu TextFill sehr klein und effizient. TextFill verwendet eine teure while-Schleife und FitText nicht.

Auch FitText ist flexibler (ich benutze es in einem Projekt mit ganz besonderen Anforderungen und funktioniert wie ein Champion!).

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>

Sie können auch Optionen festlegen:

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

CSS:

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

Und wenn Sie es brauchen, hat FitText auch eine No-jQuery-Version .

eduludi
quelle
Berücksichtigt fittext die Höhe?
Manish Sapariya
1
@ ManishSapariya nein, tut es nicht. Es teilt nur die Containerbreite durch 10 und verwendet diese als Schriftgröße.
Dan H
1

BEARBEITEN: Dieser Code wurde verwendet, um Notizen über einem HTML5-Video anzuzeigen. Es ändert die Schriftgröße im laufenden Betrieb, wenn die Größe des Videos geändert wird (wenn die Größe des Browserfensters geändert wird). Die Notizen wurden mit dem Video verbunden (genau wie Notizen auf YouTube), weshalb der Code Instanzen anstelle eines DOM-Handles verwendet direkt.

Auf Anfrage werde ich einen Code eingeben, mit dem ich dies erreicht habe. (Textfelder über einem HTML5-Video.) Der Code wurde vor langer Zeit geschrieben, und ich denke ehrlich gesagt, dass er ziemlich chaotisch ist. Da die Frage bereits beantwortet wurde und eine Antwort bereits vor langer Zeit akzeptiert wurde, mache ich mir nicht die Mühe, dies neu zu schreiben. Aber wenn jemand dies ein wenig vereinfachen möchte, sind Sie herzlich willkommen!

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

Sie müssen diese Zahlen wahrscheinlich von Schriftfamilie zu Schriftfamilie anpassen. Ein guter Weg, dies zu tun, ist das Herunterladen eines kostenlosen Grafik-Visualisierers namens GeoGebra. Ändern Sie die Länge des Textes und die Größe des Feldes. Dann stellen Sie die Größe manuell ein. Zeichnen Sie die manuellen Ergebnisse in das Koordinatensystem. Dann geben Sie die beiden Gleichungen ein, die ich hier gepostet habe, und passen die Zahlen an, bis "mein" Diagramm zu Ihren eigenen manuell gezeichneten Punkten passt.

Freund von Kim
quelle
1

Die vorgeschlagenen iterativen Lösungen können an zwei Fronten dramatisch beschleunigt werden:

1) Multiplizieren Sie die Schriftgröße mit einer Konstanten, anstatt 1 zu addieren oder zu subtrahieren.

2) Zuerst Null, wenn Sie eine Kurskonstante verwenden, z. B. die doppelte Größe jeder Schleife. Machen Sie dann mit einer groben Vorstellung davon, wo Sie anfangen sollen, dasselbe mit einer feineren Einstellung, multiplizieren Sie beispielsweise mit 1,1. Während der Perfektionist möglicherweise die exakte ganzzahlige Pixelgröße der idealen Schriftart wünscht, bemerken die meisten Beobachter den Unterschied zwischen 100 und 110 Pixel nicht. Wenn Sie ein Perfektionist sind, wiederholen Sie ein drittes Mal mit einer noch feineren Einstellung.

Anstatt eine bestimmte Routine oder ein Plug-In zu schreiben, das die genaue Frage beantwortet, verlasse ich mich nur auf die Grundideen und schreibe Variationen des Codes, um alle Arten von Layoutproblemen zu behandeln, nicht nur Text, einschließlich passender Divs, Spans, Bilder. .. nach Breite, Höhe, Fläche, ... innerhalb eines Containers, passend zu einem anderen Element ....

Hier ist ein Beispiel:

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

Dies ist die eleganteste Lösung, die ich erstellt habe. Es verwendet die binäre Suche und führt 10 Iterationen durch. Der naive Weg bestand darin, eine while-Schleife durchzuführen und die Schriftgröße um 1 zu erhöhen, bis das Element überzulaufen begann. Mit element.offsetHeight und element.scrollHeight können Sie bestimmen, wann ein Element zu überlaufen beginnt . Wenn scrollHeight größer als offsetHeight ist, haben Sie eine zu große Schriftgröße.

Die binäre Suche ist hierfür ein viel besserer Algorithmus. Es ist auch durch die Anzahl der Iterationen begrenzt, die Sie ausführen möchten. Rufen Sie einfach flexFont auf und geben Sie die div-ID ein. Dadurch wird die Schriftgröße zwischen 8 und 96 Pixel angepasst .

Ich habe einige Zeit damit verbracht, dieses Thema zu erforschen und verschiedene Bibliotheken auszuprobieren, aber letztendlich denke ich, dass dies die einfachste und einfachste Lösung ist, die tatsächlich funktionieren wird.

Beachten Sie, wenn Sie möchten, können Sie die Verwendung von offsetWidthund ändern scrollWidthoder beide zu dieser Funktion hinzufügen.

// 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
quelle
Danke, das sieht gut aus! Ich bekomme gerade ReferenceError: lastSizeTooSmall is not defined. Vielleicht muss das irgendwo definiert werden?
ndbroadbent
0

Ich habe das gleiche Problem und die Lösung besteht darin, Javascript zu verwenden, um die Schriftgröße zu steuern. Überprüfen Sie dieses Beispiel auf Codepen:

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

Dies ist ein Beispiel, das nur für die Höhe gilt. Möglicherweise müssen Sie einige Angaben zur Breite machen.

Versuchen Sie, die Größe zu ändern

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

ich habe gemocht

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

Ich wollte nur meine Version für contenteditables hinzufügen.

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

Ich habe einen Weg gefunden, die Verwendung von Schleifen zum Verkleinern des Textes zu verhindern. Die Schriftgröße wird angepasst, indem die Rate zwischen Containerbreite und Inhaltsbreite multipliziert wird. Wenn die Breite des Containers 1/3 des Inhalts beträgt, wird die Schriftgröße um 1/3 reduziert und die Breite des Containers. Zum Skalieren habe ich eine while-Schleife verwendet, bis der Inhalt größer als der Container ist.

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

Dieser Code ist Teil eines Tests, den ich auf Github hochgeladen habe. Https://github.com/ricardobrg/fitText/

Ricardo BRGWeb
quelle
0

Ich habe mich für die geekMonkey-Lösung entschieden, aber sie ist zu langsam. Er passt die Schriftgröße auf Maximum (maxFontPixels) an und prüft dann, ob sie in den Container passt. Andernfalls wird die Schriftgröße um 1 Pixel reduziert und erneut überprüft. Warum nicht einfach den vorherigen Container auf die Höhe überprüfen und diesen Wert senden? (Ja, ich weiß warum, aber ich habe jetzt eine Lösung gefunden, die nur auf der Höhe funktioniert und auch eine Min / Max-Option hat)

Hier ist eine viel schnellere Lösung:

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

und das wäre der HTML:

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

Nochmals: Diese Lösung prüft NUR die Höhe des Behälters. Deshalb muss diese Funktion nicht prüfen, ob das Element hineinpasst. Ich habe aber auch einen Min / Max-Wert (40 Min., 150 Max.) Implementiert, sodass dies für mich einwandfrei funktioniert (und auch für die Fenstergröße).

honk31
quelle
-1

Hier ist eine andere Version dieser Lösung:

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
quelle