Ridimensiona automaticamente il testo dinamico per riempire il contenitore di dimensioni fisse


312

Devo visualizzare il testo inserito dall'utente in un div di dimensioni fisse. Quello che voglio è che la dimensione del carattere sia regolata automaticamente in modo che il testo riempia il riquadro il più possibile.

Quindi - Se il div è 400px x 300px. Se qualcuno entra in ABC, allora è un font davvero grande. Se inseriscono un paragrafo, sarebbe un carattere minuscolo.

Probabilmente vorrei iniziare con una dimensione massima del carattere - forse 32px, e mentre il testo è troppo grande per adattarsi al contenitore, ridurre la dimensione del carattere fino a quando non si adatta.


119
Probabilmente una delle caratteristiche più sorprendenti che avrebbero dovuto essere aggiunte a HTML5 / CSS3 senza la necessità di JS.
John Magnolia,

Ho fatto alcune misurazioni in cui ho cambiato la lunghezza di un testo dinamico e la dimensione del contenitore per capire quale dimensione del carattere avrebbe adattato perfettamente il testo. E dopo aver fatto alcune analisi di regressione, ho escogitato una semplice funzione matematica che genererà automaticamente la dimensione del carattere migliore.
Amico di Kim,

2
In realtà si scopre che il grafico che ti dà la dimensione del carattere migliore è dato da f (x) = g (lettere) * (x / 1000) ^ n, dove g (x) è una funzione semplice, n varia a seconda di il carattere che stai utilizzando. (Anche se può avere un valore standard per tutti i caratteri, se non vuoi modificarlo per renderlo assolutamente perfetto ...). x è la dimensione in pixel quadrati del contenitore.
Amico di Kim,

1
Se sei ancora interessato, posso aggiungere una risposta. Personalmente penso che sia un metodo molto migliore per generare la dimensione del carattere corretta in primo luogo, invece di provare e fallire fino a quando lo script "non funziona".
Amico di Kim,

1
Controlla la mia risposta per un modo migliore per farlo
Hoffmann,

Risposte:


167

Grazie Attacco . Volevo usare jQuery.

Mi hai indicato la giusta direzione, e questo è quello che ho finito con:

Ecco un link al plugin: https://plugins.jquery.com/textfill/
E un link alla fonte: 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 });
});

e il mio HTML è così

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

Questo è il mio primo plugin jquery, quindi probabilmente non è buono come dovrebbe essere. I puntatori sono sicuramente benvenuti.


8
In realtà l'ho appena ripulito e impacchettato come plug-in disponibile da jquery.com all'indirizzo plugins.jquery.com/project/TextFill
GeekyMonkey

3
@GeekyMonkey, hai estratto il plugin? Ho appena seguito un link dupe a questa pagina e ho pensato di dare un'occhiata, ma i collegamenti jQuery.com al tuo sito ritornano 404.
David dice di ripristinare Monica il

Nota: ho scoperto che per qualche motivo questo plugin funziona solo quando div ($ ('. Jtextfill') nell'esempio sopra) fa parte del documento radice. Sembra che .width () restituisca zero quando il div è incorporato in altri div.
Jayesh,

1
La linea "while" su quel loop mi sembra sbagliata - ci dovrebbero essere parentesi attorno al "||" sottoespressione. Nel modo in cui è scritto ora, la dimensione minima del carattere viene controllata solo quando la larghezza è troppo grande, non l'altezza.
Punta

4
Questo approccio è MOLTO lento, ogni volta che il font cambia dimensione richiede che l'elemento venga ridistribuito. Controlla la mia risposta per un modo migliore per farlo.
Hoffmann,

52

Non ho trovato nessuna delle soluzioni precedenti sufficientemente adeguata a causa di prestazioni scadenti, quindi ne ho creata una mia che utilizza la matematica semplice invece del looping. Dovrebbe funzionare bene anche in tutti i browser.

Secondo questo caso di test delle prestazioni è molto più veloce delle altre soluzioni trovate qui.

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

Se vuoi contribuire, l'ho aggiunto a Gist .


1
@Jon, grazie! Hai ragione sul fatto che il mio script non fa più righe, ma poi l'OP non lo ha specificamente richiesto, quindi la tua ipotesi potrebbe essere errata. Inoltre, quel tipo di comportamento non ha molto senso imo. Immagino che il modo migliore per aggiungere il supporto multi-linea sarebbe quello di dividere la stringa in base alla quantità di parole e quindi calcolare ogni parte con lo script sopra e molto probabilmente sarebbe più veloce comunque.
Mekwall,

4
@Jon, ho giocato un po 'con il riempimento di testo multilinea e ho finito con questa soluzione . Il metodo sandstorm è molto probabilmente più accurato ma questo è più veloce;)
mekwall

2
Ecco una versione con dimensione del carattere minima e massima: gist.github.com/1714284
Jess Telford,

1
@Hoffmann Hmm. La mia soluzione non chiama .css("font-size")in loop. Da dove l'hai preso? La mia soluzione è probabilmente più veloce poiché non ha nessuna delle cose fantasiose che hai aggiunto al tuo plugin. Puoi aggiungere il tuo plug-in a jsperf e vedremo qual è il più veloce;)
mekwall

1
@MarcusEkwall Oh scusa, per qualche motivo ho pensato di aver visto un po 'di loop lì. Il tuo approccio è simile al mio e in effetti il ​​mio sarebbe un po 'più lento dal momento che il mio plugin fa anche altre cose (come la regolazione per adattarsi sia in altezza che in larghezza, centralizza il testo e alcune altre opzioni), non importa, la parte davvero lenta sta chiamando la funzione .css all'interno di un ciclo.
Hoffmann,

35

Per quanto adoro i voti occasionali che ottengo per questa risposta (grazie!), Questo non è davvero il miglior approccio a questo problema. Controlla qui alcune delle altre meravigliose risposte, in particolare quelle che hanno trovato soluzioni senza loop.


Tuttavia, per motivi di riferimento, ecco la mia risposta originale :

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

Ed ecco una versione con le classi :

<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
Ho trovato che funzionava meglio con offsetWidth, ho anche dovuto creare una variabile per dimensioni e quindi aggiungere il pxtextSpan.style.fontSize = size+"px";
Wez

2
è necessario '+ "px"'.
sandun dhammika,

@IdanShechter Ci scusiamo per la lunga, lunga attesa! Aggiunto un esempio!
attacco

Grazie, salvavita! Non sono a conoscenza di jQuery, quindi mi attengo alla tua soluzione :)
vintproykt,

32

La maggior parte delle altre risposte utilizza un ciclo per ridurre la dimensione del carattere fino a quando non si adatta al div, questo è MOLTO lento poiché la pagina deve ripetere il rendering dell'elemento ogni volta che il carattere cambia dimensione. Alla fine ho dovuto scrivere il mio algoritmo per farlo funzionare in un modo che mi permettesse di aggiornare periodicamente i suoi contenuti senza congelare il browser dell'utente. Ho aggiunto alcune altre funzionalità (rotazione del testo, aggiunta di padding) e impacchettato come plug-in jQuery, puoi ottenerlo su:

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

chiama semplicemente

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

e si adatterà perfettamente al tuo contenitore.

Guardalo in azione qui:

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

Per ora ha alcune limitazioni, il div deve avere un'altezza e una larghezza fisse e non supporta il wrapping del testo in più righe.

Lavorerò per ottenere un'opzione per impostare la dimensione massima del carattere.

Modifica: ho riscontrato altri problemi con il plug-in, non gestisce altri modelli box oltre a quello standard e il div non può avere margini o bordi. Ci lavorerò sopra.

Edit2: ora ho risolto quei problemi e limitazioni e ho aggiunto più opzioni. Puoi impostare la dimensione massima del carattere e puoi anche scegliere di limitare la dimensione del carattere usando larghezza, altezza o entrambi. Lavorerò per accettare i valori di larghezza massima e altezza massima nell'elemento wrapper.

Edit3: ho aggiornato il plug-in alla versione 1.2.0. Importante pulizia del codice e nuove opzioni (verticalAlign, horizontalAlign, textAlign) e supporto per gli elementi interni all'interno del tag span (come interruzioni di riga o icone fantastiche per i font).


1
Mi chiedo perché il wrapping del testo su più righe non sia supportato?
Manish Sapariya,

1
@ManishSapariya È supportato, ma è necessario aggiungere manualmente le interruzioni di riga (tag br). Il motivo per cui non supporta il ritorno a capo automatico del testo è perché per renderlo più veloce (modificare solo la dimensione del carattere due volte anziché più volte), è necessario supporre che il testo non si sposti tra le parole. Il modo in cui funziona il mio plugin è impostare la dimensione del carattere su 1000px quindi vedere il fattore della dimensione del testo rispetto al contenitore, quindi riduco la dimensione del carattere dello stesso fattore. Per supportare il normale ritorno a capo del testo avrei bisogno di usare l'approccio lento (ridurre più volte la dimensione del carattere) che è molto lento.
Hoffmann,

Hey! Dato che non ci sono messaggi privati ​​qui, su StackOverflow, dovrò chiederti commentando la tua risposta. Adoro il tuo plugin jQuery, ma non riesco a farlo funzionare per me. Ho incluso la libreria jQuery giusta, scaricato il plug-in e incluso. Ora, quando provo ad usarlo, la console dice "Uncaught TypeError: undefined non è una funzione". È qualcosa che conosci? Sai come aggiustarlo? Grazie
Gust van de Wal,

@GustvandeWal Devi includere il plugin dopo aver incluso la libreria jquery
Hoffmann,

L'ho fatto. Ho <script type = "text / javascript" src = " code.jquery.com/jquery-2.1.1.min.js"></… src" js / jquery-bigtext.js "> </ script > Il browser non mi avvisa che non può caricare la libreria jQuery o il plug-in.
Gust van de Wal,

9

Questo si basa su ciò che GeekyMonkey ha pubblicato sopra, con alcune modifiche.

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

la differenza è che può prendere più elementi figlio e che tiene conto dell'imbottitura. Utilizza la dimensione del carattere come dimensione massima predefinita, per evitare di mescolare javascript e css.
sandstrom,

5
Questo è fantastico, ma come lo uso? Faccio $ ('. Outer'). Textfill (); e non ottengo alcun cambiamento.
Ha disegnato Baker il

3
Grazie, questa è un'implementazione molto bella. Mi sono imbattuto in una cosa: se hai a che fare con stringhe di testo molto lunghe e contenitori molto stretti, la stringa di testo sporgerà dal contenitore, ma la larghezza esterna verrà comunque calcolata come se non lo fosse. Toss "parola-a capo: break-word;" nel tuo CSS per quel contenitore, risolverà questo problema.
Jon

8

Ecco un metodo di looping migliorato che utilizza la ricerca binaria per trovare la dimensione più grande possibile che si adatta al genitore nel minor numero di passaggi possibile (questo è più veloce e più accurato rispetto al passaggio da una dimensione del carattere fissa). Il codice è inoltre ottimizzato in vari modi per le prestazioni.

Per impostazione predefinita, verranno eseguite 10 fasi di ricerca binaria, che raggiungeranno lo 0,1% della dimensione ottimale. È possibile invece impostare NumIter su un valore N per ottenere entro 1/2 ^ N della dimensione ottimale.

Chiamalo con un selettore CSS, ad esempio: 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);
        }
    });
};

Adoro questa opzione. L'ho modificato per aggiungere parametri per vAligne padding. vAlign == trueimposta l'altezza della linea dell'elemento selezionato su quella dell'altezza principale. Il riempimento riduce la dimensione finale del valore passato. Il valore predefinito è 5. Penso che sia davvero bello.
Devil's Advocate,

6

Ho creato una direttiva per AngularJS - fortemente ispirata dalla risposta di GeekyMonkey ma senza la dipendenza jQuery.

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

markup

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

Direttiva

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

Un problema che ho riscontrato in questo è che resizeTextsembra essere chiamato prima ng-bindche il testo assegni effettivamente l'elemento, risultando in un dimensionamento basato sul testo precedente anziché sul testo corrente. Questo non è male nella demo sopra dove viene chiamato ripetutamente mentre l'utente digita, ma se viene chiamato una volta passando da null al valore reale (come in un'associazione unidirezionale) rimarrà alla dimensione massima.
Miral,

5

Ho modificato la sceneggiatura sopra di Marcus Ekwall: https://gist.github.com/3945316 e l'ho modificata in base alle mie preferenze, ora si attiva quando la finestra viene ridimensionata, in modo che il bambino si adatti sempre al suo contenitore. Ho incollato lo script qui sotto per riferimento.

(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
È bello che tu stia cercando di aiutare chi lo chiede. Tuttavia, lasciare una risposta con solo un collegamento può essere dannoso in alcuni casi. Mentre la tua risposta è buona ora, se il link dovesse mai morire, la tua risposta perderebbe il suo valore. Quindi sarà utile se riassumi il contenuto dell'articolo nella tua risposta. Vedi questa domanda per chiarimenti.
Cody Guldner,

5

Ecco la mia modifica della risposta del PO.

In breve, molte persone che hanno cercato di ottimizzare questo si sono lamentate del fatto che fosse utilizzato un loop. Sì, mentre i loop possono essere lenti, altri approcci possono essere inaccurati.

Pertanto, il mio approccio utilizza la ricerca binaria per trovare la dimensione del carattere migliore:

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

Ciò consente inoltre all'elemento di specificare il carattere minimo e massimo:

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

Inoltre, questo algoritmo è privo di unità. È possibile specificare em, rem, %, ecc e sarà l'uso che per il suo risultato finale.

Ecco il violino: https://jsfiddle.net/fkhqhnqe/1/


2

Ho avuto esattamente lo stesso problema con il mio sito web. Ho una pagina che viene visualizzata su un proiettore, su pareti, grandi schermi ..

Poiché non conosco la dimensione massima del mio carattere, ho riutilizzato il plug-in sopra di @GeekMonkey ma aumentando la dimensione del carattere:

$.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 per essere l'unico plug-in in questa pagina che ha funzionato per me!
skybondsor,

2
Questo plugin blocca la pagina per me.
Jezen Thomas,

1

Ecco una versione della risposta accettata che può anche prendere un parametro 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);

grazie, anche se penso che tu abbia un punto e virgola all'inizio del codice che non dovrebbe essere lì
Patrick Moore

1

È possibile utilizzare FitText.js ( pagina github ) per risolvere questo problema. È davvero piccolo ed efficiente rispetto a TextFill. TextFill utilizza un ciclo while costoso e FitText no.

Inoltre FitText è più flessibile (lo uso in un progetto con requisiti molto speciali e funziona come un campione!).

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>

Puoi anche impostare le opzioni su di esso:

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

CSS:

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

E se ne hai bisogno, FitText ha anche una versione no-jQuery .


Fittext tiene conto dell'altezza?
Manish Sapariya,

1
@ManishSapariya no non lo fa. Divide solo la larghezza del contenitore per 10 e la utilizza come dimensione del carattere.
Dan H

1

EDIT: questo codice è stato usato per mostrare le note sopra un video HTML5. Cambia la dimensione del carattere al volo quando il video viene ridimensionato (quando viene ridimensionata la finestra del browser.) Le note sono state collegate al video (proprio come le note su YouTube), motivo per cui il codice utilizza istanze anziché un handle DOM direttamente.

Come da richiesta, inserirò un codice che ho usato per raggiungere questo obiettivo. (Caselle di testo su un video HTML5.) Il codice è stato scritto molto tempo fa e francamente penso che sia piuttosto disordinato. Poiché la domanda ha già una risposta e una risposta è già stata accettata molto tempo fa, non mi preoccupo di riscriverla. Ma se qualcuno vuole semplificare un po 'questo, sei più che benvenuto!

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

Probabilmente dovrai modificare questi numeri da famiglia di caratteri a famiglia di caratteri. Un buon modo per farlo è scaricare un visualizzatore di grafici gratuito chiamato GeoGebra. Modifica la lunghezza del testo e la dimensione della casella. Quindi imposta manualmente la dimensione. Traccia i risultati manuali nel sistema di coordinate. Quindi inserisci le due equazioni che ho pubblicato qui e modifichi i numeri fino a quando il "mio" grafico si adatta ai tuoi punti tracciati manualmente.


1

Le soluzioni iterative proposte possono essere velocizzate notevolmente su due fronti:

1) Moltiplicare la dimensione del carattere per una costante, anziché aggiungere o sottrarre 1.

2) Innanzitutto, zero nell'uso di una costante di corso, ad esempio, raddoppia la dimensione di ogni ciclo. Quindi, con una vaga idea di dove iniziare, fai la stessa cosa con una regolazione più precisa, diciamo, moltiplica per 1.1. Mentre il perfezionista potrebbe desiderare l'esatta dimensione intera dei pixel del font ideale, la maggior parte degli osservatori non nota la differenza tra 100 e 110 pixel. Se sei un perfezionista, ripeti una terza volta con una regolazione ancora più precisa.

Piuttosto che scrivere una routine o un plug-in specifici che rispondano alla domanda esatta, mi baso solo sulle idee di base e scrivo variazioni del codice per gestire tutti i tipi di problemi di layout, non solo il testo, inclusi adattamenti di div, span, immagini. .. per larghezza, altezza, area, ... all'interno di un contenitore, abbinando un altro elemento ....

Ecco un esempio:

  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

Questa è la soluzione più elegante che ho creato. Usa la ricerca binaria, facendo 10 iterazioni. Il modo ingenuo era fare un ciclo while e aumentare la dimensione del carattere di 1 fino a quando l'elemento ha iniziato a traboccare. Puoi determinare quando un elemento inizia a traboccare usando element.offsetHeight e element.scrollHeight . Se scrollHeight è più grande di offsetHeight, hai una dimensione del carattere troppo grande.

La ricerca binaria è un algoritmo molto migliore per questo. Inoltre è limitato dal numero di iterazioni che si desidera eseguire. Basta chiamare flexFont e inserire il div id e regolerà la dimensione del carattere tra 8px e 96px .

Ho trascorso un po 'di tempo a cercare questo argomento e provare diverse librerie, ma alla fine penso che questa sia la soluzione più semplice e diretta che effettivamente funzionerà.

Nota se vuoi puoi cambiare da usare offsetWidthe scrollWidth, o aggiungere entrambi a questa funzione.

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

Grazie, sembra fantastico! Sto solo arrivando ReferenceError: lastSizeTooSmall is not defined. Forse questo deve essere definito da qualche parte?
ndbroadbent

0

Ho avuto lo stesso problema e la soluzione è fondamentalmente utilizzare JavaScript per controllare la dimensione del carattere. Controlla questo esempio su codepen:

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

Questo esempio è solo per l'altezza, forse devi mettere alcuni if ​​sulla larghezza.

prova a ridimensionarlo

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

mi è piaciuto

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

Volevo solo aggiungere la mia versione per contenteditables.

$.fn.fitInText = function() {
  this.each(function() {

    let textbox = $(this);
    let textboxNode = this;

    let mutationCallback = function(mutationsList, observer) {
      if (observer) {
        observer.disconnect();
      }
      textbox.css('font-size', 0);
      let desiredHeight = textbox.css('height');
      for (i = 12; i < 50; i++) {
        textbox.css('font-size', i);
        if (textbox.css('height') > desiredHeight) {
          textbox.css('font-size', i - 1);
          break;
        }
      }

      var config = {
        attributes: true,
        childList: true,
        subtree: true,
        characterData: true
      };
      let newobserver = new MutationObserver(mutationCallback);
      newobserver.observe(textboxNode, config);

    };

    mutationCallback();

  });
}

$('#inner').fitInText();
#outer {
  display: table;
  width: 100%;
}

#inner {
  border: 1px solid black;
  height: 170px;
  text-align: center;
  display: table-cell;
  vertical-align: middle;
  word-break: break-all;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="outer">
  <div id="inner" contenteditable=true>
    TEST
  </div>
</div>


0

Ho trovato un modo per impedire l'uso di loop per ridurre il testo. Regola la dimensione del carattere moltiplicandolo per la velocità tra larghezza del contenitore e larghezza del contenuto. Pertanto, se la larghezza del contenitore è 1/3 del contenuto, la dimensione del carattere verrà ridotta di 1/3 e la larghezza del contenitore. Per aumentare la scala, ho usato un ciclo while, fino a quando il contenuto non è più grande del contenitore.

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

Questo codice fa parte di un test che ho caricato su Github https://github.com/ricardobrg/fitText/


0

Sono andato con la soluzione geekMonkey, ma è troppo lento. Quello che fa è che regola la dimensione del carattere al massimo (maxFontPixels) e quindi controlla se si adatta all'interno del contenitore. altrimenti riduce la dimensione del carattere di 1 px e controlla di nuovo. Perché non controllare semplicemente il contenitore precedente per l'altezza e inviare quel valore? (sì, lo so perché, ma ora ho fatto una soluzione, che funziona solo sull'altezza e ha anche un'opzione min / max)

Ecco una soluzione molto più veloce:

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

e questo sarebbe l'HTML:

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

Ancora una volta: questa soluzione controlla SOLO l'altezza del contenitore. Ecco perché questa funzione non deve verificare se l'elemento si adatta all'interno. Ma ho anche implementato un valore min / max (40min, 150max), quindi per me funziona perfettamente (e funziona anche con il ridimensionamento delle finestre).


-1

Ecco un'altra versione di questa soluzione:

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";
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.