Texte dynamique de taille automatique pour remplir le conteneur de taille fixe

312

Je dois afficher le texte saisi par l'utilisateur dans une div de taille fixe. Ce que je veux, c'est que la taille de la police soit automatiquement ajustée afin que le texte remplisse la boîte autant que possible.

Donc - Si le div est de 400 px x 300 px. Si quelqu'un entre dans ABC, c'est vraiment une grosse police. S'ils entrent dans un paragraphe, ce serait une police minuscule.

Je voudrais probablement commencer avec une taille de police maximale - peut-être 32 pixels, et bien que le texte soit trop gros pour tenir dans le conteneur, réduisez la taille de la police jusqu'à ce qu'elle tienne.

GeekyMonkey
la source
119
Probablement l'une des fonctionnalités les plus étonnantes qui devraient être ajoutées à HTML5 / CSS3 sans avoir besoin de JS.
John Magnolia
J'ai fait quelques mesures où j'ai changé la longueur d'un texte dynamique et la taille du conteneur pour déterminer quelle taille de police rendra le texte parfaitement. Et après avoir fait une analyse de régression, j'ai trouvé une fonction mathématique simple qui générera automatiquement la meilleure taille de police.
Ami de Kim
2
Il s'avère en fait que le graphique qui vous donne la meilleure taille de police est donné par f (x) = g (lettres) * (x / 1000) ^ n, où g (x) est une fonction simple, n varie en fonction de la police que vous utilisez. (Bien qu'il puisse avoir une valeur standard pour toutes les polices, si vous ne voulez pas le modifier pour le rendre absolument parfait ...). x est la taille en pixels carrés du conteneur.
Ami de Kim
1
Si vous êtes toujours intéressé, je peux ajouter une réponse. Personnellement, je pense que c'est une bien meilleure méthode pour générer la taille de police correcte en premier lieu, au lieu d'essayer et d'échouer jusqu'à ce que le script "réussisse".
Ami de Kim
1
Vérifiez ma réponse pour une meilleure façon de le faire
Hoffmann

Réponses:

167

Merci Attack . Je voulais utiliser jQuery.

Vous m'avez pointé dans la bonne direction, et voici ce que j'ai fini avec:

Voici un lien vers le plugin: https://plugins.jquery.com/textfill/
Et un lien vers la source: 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 });
});

et mon html est comme ça

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

Ceci est mon premier plugin jquery, donc ce n'est probablement pas aussi bon qu'il devrait l'être. Les pointeurs sont certainement les bienvenus.

GeekyMonkey
la source
8
En fait, je viens de le nettoyer et de le conditionner en tant que plugin disponible sur jquery.com à plugins.jquery.com/project/TextFill
GeekyMonkey
3
@GeekyMonkey, avez-vous tiré le plugin? Je viens de suivre un lien dupe vers cette page, et j'ai pensé y jeter un œil, mais les liens jQuery.com vers votre site reviennent 404.
David dit de réintégrer Monica
Remarque: J'ai trouvé que pour une raison quelconque, ce plugin ne fonctionne que lorsque le div ($ ('. Jtextfill') dans l'exemple ci-dessus) fait partie du document racine. Il semble que .width () renvoie zéro lorsque le div est intégré dans d'autres divs.
Jayesh
1
La ligne "while" sur cette boucle me semble incorrecte - il devrait y avoir des parenthèses autour du "||" sous-expression. La façon dont elle est écrite maintenant, la taille de police minimale n'est vérifiée que lorsque la largeur est trop grande, pas la hauteur.
Pointy
4
Cette approche est TRÈS lente, chaque fois que la police change de taille, il faut que l'élément soit restitué. Vérifiez ma réponse pour une meilleure façon de procéder.
Hoffmann
52

Je n'ai trouvé aucune des solutions précédentes suffisamment adéquate en raison de mauvaises performances, j'ai donc créé la mienne qui utilise des mathématiques simples au lieu de boucler. Devrait également fonctionner correctement dans tous les navigateurs.

Selon ce cas de test de performance, il est beaucoup plus rapide que les autres solutions trouvées ici.

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

Si vous voulez contribuer, je l'ai ajouté à Gist .

mekwall
la source
1
@ Jon, merci! Vous avez raison de dire que mon script ne comporte pas plusieurs lignes, mais là encore, l'OP n'a pas spécifiquement demandé cela, donc votre hypothèse pourrait être fausse. De plus, ce genre de comportement n'a pas beaucoup de sens imo. Je suppose que la meilleure façon d'ajouter un support multi-lignes serait de diviser la chaîne en fonction de la quantité de mots, puis de calculer chaque partie avec le script ci-dessus et ce serait probablement plus rapide de toute façon.
mekwall
4
@Jon, j'ai joué un peu avec le remplissage de texte multiligne et je me suis retrouvé avec cette solution . La méthode de
Sandstorm
2
Voici une version avec une taille de police minimale et maximale: gist.github.com/1714284
Jess Telford
1
@Hoffmann Hmm. Ma solution n'appelle pas .css("font-size")en boucle. Où est-ce que tu as trouvé ça? Ma solution est probablement plus rapide car elle n'a aucun des éléments fantaisie que vous avez ajoutés à votre plugin. Vous êtes les bienvenus pour ajouter votre plugin au jsperf et nous verrons lequel est le plus rapide;)
mekwall
1
@MarcusEkwall Oh désolé, pour une raison quelconque, je pensais avoir vu une boucle pendant un certain temps. Votre approche est similaire à la mienne et en effet la mienne serait un peu plus lente car mon plugin fait aussi d'autres choses (comme l'ajustement pour s'adapter à la hauteur et à la largeur, centralise le texte et quelques autres options), peu importe, la partie vraiment lente appelle la fonction .css dans une boucle.
Hoffmann
35

Autant j'aime les votes positifs occasionnels que j'obtiens pour cette réponse (merci!), Ce n'est vraiment pas la meilleure approche de ce problème. Veuillez consulter quelques-unes des autres merveilleuses réponses ici, en particulier celles qui ont trouvé des solutions sans boucle.


Pourtant, à titre de référence, voici ma réponse 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>

Et voici une version avec des classes :

<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>
attaque
la source
3
J'ai trouvé que cela fonctionnait mieux avec offsetWidth, j'ai aussi dû créer une variable pour la taille et ensuite ajouter le pxtextSpan.style.fontSize = size+"px";
Wez
2
assurez-vous que '+ "px"' est nécessaire.
sandun dhammika
@IdanShechter Désolé pour la longue, longue attente! Ajout d'un exemple!
attaque
Merci, épargnant de vie! Je ne suis pas au courant de jQuery, donc je m'en tiens à votre solution :)
vintproykt
32

La plupart des autres réponses utilisent une boucle pour réduire la taille de la police jusqu'à ce qu'elle tienne sur le div, ce qui est TRÈS lent car la page doit restituer l'élément à chaque fois que la police change de taille. J'ai finalement dû écrire mon propre algorithme pour le faire fonctionner d'une manière qui m'a permis de mettre à jour son contenu périodiquement sans geler le navigateur de l'utilisateur. J'ai ajouté quelques autres fonctionnalités (rotation du texte, ajout de remplissage) et je l'ai empaqueté en tant que plugin jQuery, vous pouvez l'obtenir à:

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

appelez simplement

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

et il s'adaptera parfaitement à votre conteneur.

Voyez-le en action ici:

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

Pour l'instant, il a quelques limitations, le div doit avoir une hauteur et une largeur fixes et il ne prend pas en charge l'habillage du texte sur plusieurs lignes.

Je vais travailler sur l'obtention d'une option pour définir la taille de police maximale.

Edit: J'ai trouvé quelques problèmes avec le plugin, il ne gère pas d'autres modèles de boîtes en plus du modèle standard et le div ne peut pas avoir de marges ou de bordures. J'y travaillerai.

Edit2: J'ai maintenant corrigé ces problèmes et limitations et ajouté plus d'options. Vous pouvez définir la taille de police maximale et vous pouvez également choisir de limiter la taille de police en utilisant soit la largeur, la hauteur ou les deux. Je vais travailler à accepter des valeurs max-width et max-height dans l'élément wrapper.

Edit3: J'ai mis à jour le plugin vers la version 1.2.0. Nettoyage majeur du code et de nouvelles options (verticalAlign, horizontalAlign, textAlign) et prise en charge des éléments internes à l'intérieur de la balise span (comme les sauts de ligne ou les icônes impressionnantes de police.)

Hoffmann
la source
1
Je me demande pourquoi l'emballage de texte sur plusieurs lignes n'est pas pris en charge?
Manish Sapariya
1
@ManishSapariya Il est pris en charge, mais vous devez ajouter les sauts de ligne (balises br) manuellement. La raison pour laquelle je ne prends pas en charge le retour automatique à la ligne est parce que pour le rendre rapide (ne changez la taille de police que deux fois au lieu de plusieurs fois), je dois faire l'hypothèse que le texte ne passera pas entre les mots. La façon dont mon plugin fonctionne est de définir la taille de la police à 1000 pixels, puis de voir le facteur de la taille du texte par rapport au conteneur, je réduis ensuite la taille de la police du même facteur. Pour prendre en charge l'habillage de texte normal, je devrais utiliser l'approche lente (réduire la taille de la police plusieurs fois) qui est très lente.
Hoffmann
Hey! Puisqu'il n'y a pas de messagerie privée ici, sur StackOverflow, je vais devoir vous demander en commentant votre réponse. J'adore votre plugin jQuery, mais je n'arrive pas à le faire fonctionner pour moi. J'ai inclus la bonne bibliothèque jQuery, téléchargé votre plugin et l'ai inclus. Maintenant, quand j'essaye de l'utiliser, la console dit "Uncaught TypeError: undefined n'est pas une fonction". Est-ce quelque chose que vous connaissez? Savez-vous comment résoudre ce problème? Merci
Gust van de Wal
@GustvandeWal Vous devez inclure le plugin après avoir inclus la bibliothèque jquery
Hoffmann
J'ai fait. J'ai <script type = "text / javascript" src = " code.jquery.com/jquery-2.1.1.min.js"></… src" js / jquery-bigtext.js "> </ script > Le navigateur ne m'informe pas qu'il ne peut pas charger la bibliothèque jQuery ou le plugin.
Gust van de Wal
9

Ceci est basé sur ce que GeekyMonkey a publié ci-dessus, avec quelques modifications.

; (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
la source
la différence est qu'il peut prendre plusieurs éléments enfants et qu'il prend en compte le remplissage. Utilise la taille de police comme taille maximale par défaut, pour éviter de mélanger javascript et css.
sandstrom
5
C'est génial, mais comment l'utiliser? Je fais $ ('. Externe'). Textfill (); et je n'ai aucun changement.
Drew Baker
3
Merci, c'est une très belle implémentation. Une chose que j'ai rencontrée: si vous avez affaire à des chaînes de texte très longues et à des conteneurs très étroits, la chaîne de texte sortira du conteneur, mais la largeur externe sera toujours calculée comme si ce n'était pas le cas. Lancez "word-wrap: break-word;" dans votre CSS pour ce conteneur, il résoudra ce problème.
Jon
8

Voici une méthode de bouclage améliorée qui utilise la recherche binaire pour trouver la plus grande taille possible qui s'adapte au parent dans le moins d'étapes possible (c'est plus rapide et plus précis que de marcher avec une taille de police fixe). Le code est également optimisé de plusieurs manières pour les performances.

Par défaut, 10 étapes de recherche binaire seront effectuées, ce qui correspondra à 0,1% de la taille optimale. Vous pouvez plutôt définir numIter sur une valeur N pour obtenir à l'intérieur de 1/2 ^ N de la taille optimale.

Appelez-le avec un sélecteur CSS, par exemple: 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
la source
J'adore cette option. Je l'ai modifié pour ajouter des paramètres pour vAlignet padding. vAlign == truedéfinit la hauteur de ligne de l'élément sélectionné sur celle de la hauteur des parents. Le remplissage diminue la taille finale de la valeur transmise. Il est par défaut à 5. Je pense que ça a l'air vraiment sympa.
Devil's Advocate
6

J'ai créé une directive pour AngularJS - très inspirée par la réponse de GeekyMonkey mais sans la dépendance jQuery.

Démo: http://plnkr.co/edit/8tPCZIjvO3VSApSeTtYr?p=preview

Marquage

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

Directif

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
la source
Un problème que j'ai rencontré avec cela est qu'il resizeTextsemble être appelé avant d' ng-bindassigner réellement le texte à l'élément, ce qui entraîne un dimensionnement basé sur le texte précédent plutôt que sur le texte actuel. Ce n'est pas trop mal dans la démo ci-dessus où il est appelé à plusieurs reprises au fur et à mesure que l'utilisateur tape, mais s'il est appelé une fois en passant de null à la valeur réelle (comme dans une liaison à sens unique), il reste à la taille maximale.
Miral
5

J'ai bifurqué le script ci-dessus de Marcus Ekwall: https://gist.github.com/3945316 et l'ai ajusté à mes préférences, il se déclenche maintenant lorsque la fenêtre est redimensionnée, de sorte que l'enfant tient toujours son conteneur. J'ai collé le script ci-dessous pour référence.

(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
la source
2
C'est formidable que vous essayiez d'aider le demandeur. Cependant, laisser une réponse avec seulement un lien peut être dangereux dans certains cas. Bien que votre réponse soit bonne maintenant, si le lien venait à mourir, votre réponse perdrait sa valeur. Il sera donc utile de résumer le contenu de l'article dans votre réponse. Voir cette question pour des éclaircissements.
Cody Guldner
5

Voici ma modification de la réponse du PO.

En bref, de nombreuses personnes qui ont tenté d'optimiser cela se sont plaintes de l'utilisation d'une boucle. Oui, alors que les boucles peuvent être lentes, d'autres approches peuvent être inexactes.

Par conséquent, mon approche utilise la recherche binaire pour trouver la meilleure taille de police:

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

Cela permet également à l'élément de spécifier la police minimale et maximale:

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

De plus, cet algorithme est sans unité. Vous pouvez spécifier em, rem,% , etc. , et il utilisera que pour son résultat final.

Voici le violon: https://jsfiddle.net/fkhqhnqe/1/

Boom
la source
2

J'ai eu exactement le même problème avec mon site Web. J'ai une page qui s'affiche sur un projecteur, sur des murs, sur de grands écrans ..

Comme je ne connais pas la taille maximale de ma police, j'ai réutilisé le plugin ci-dessus de @GeekMonkey mais en incrémentant la taille de la police:

$.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
la source
+1 pour être le seul plugin de cette page qui a vraiment fonctionné pour moi!
skybondsor
2
Ce plugin plante la page pour moi.
Jezen Thomas
1

Voici une version de la réponse acceptée qui peut également prendre un paramètre 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
la source
merci, même si je pense que vous avez un point-virgule en haut du code qui ne devrait pas être là
Patrick Moore
1

Vous pouvez utiliser FitText.js ( page github ) pour résoudre ce problème. Est vraiment petit et efficace par rapport à TextFill. TextFill utilise une boucle while coûteuse et FitText non.

FitText est également plus flexible (je l'utilise dans un projet avec des exigences très spéciales et fonctionne comme un 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>

Vous pouvez également lui définir des options:

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

CSS:

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

Et si vous en avez besoin, FitText a également une version sans jQuery .

eduludi
la source
Fittext tient-il compte de la hauteur?
Manish Sapariya
1
@ManishSapariya non, ce n'est pas le cas. Il divise simplement la largeur du conteneur par 10 et l'utilise comme taille de police.
Dan H
1

EDIT: Ce code a été utilisé pour afficher des notes au-dessus d'une vidéo HTML5. Il change la taille de la police à la volée lorsque la vidéo est redimensionnée (lorsque la fenêtre du navigateur est redimensionnée.) Les notes étaient connectées à la vidéo (tout comme les notes sur YouTube), c'est pourquoi le code utilise des instances au lieu d'une poignée DOM directement.

Selon la demande, je vais ajouter du code que j'ai utilisé pour y parvenir. (Zones de texte sur une vidéo HTML5.) Le code a été écrit il y a longtemps, et je pense franchement que c'est assez compliqué. Étant donné que la question a déjà reçu une réponse et qu'une réponse est déjà acceptée il y a longtemps, je ne me soucie pas de la réécrire. Mais si quelqu'un veut simplifier un peu cela, vous êtes le bienvenu!

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

Vous devrez probablement modifier ces chiffres de la famille de polices à la famille de polices. Un bon moyen de le faire est de télécharger un visualiseur graphique gratuit appelé GeoGebra. Modifiez la longueur du texte et la taille de la boîte. Ensuite, vous définissez manuellement la taille. Tracez les résultats manuels dans le système de coordonnées. Ensuite, vous entrez les deux équations que j'ai publiées ici et vous ajustez les nombres jusqu'à ce que "mon" graphique corresponde à vos propres points tracés manuellement.

Ami de Kim
la source
1

Les solutions itératives proposées peuvent être accélérées de façon spectaculaire sur deux fronts:

1) Multipliez la taille de la police par une constante, plutôt que d'ajouter ou de soustraire 1.

2) D'abord, zéro en utilisant une constante de cap, par exemple, doublez la taille de chaque boucle. Ensuite, avec une idée approximative de l'endroit où commencer, faites la même chose avec un ajustement plus fin, disons, multipliez par 1,1. Bien que le perfectionniste puisse vouloir la taille exacte en pixels entiers de la police idéale, la plupart des observateurs ne remarquent pas la différence entre 100 et 110 pixels. Si vous êtes perfectionniste, répétez une troisième fois avec un réglage encore plus fin.

Plutôt que d'écrire une routine ou un plug-in spécifique qui répond à la question exacte, je m'appuie simplement sur les idées de base et j'écris des variantes du code pour gérer toutes sortes de problèmes de mise en page, pas seulement le texte, y compris les divs, les étendues, les images. .. par largeur, hauteur, surface, ... dans un conteneur, correspondant à un autre élément ....

Voici un exemple:

  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
la source
1

C'est la solution la plus élégante que j'ai créée. Il utilise la recherche binaire, faisant 10 itérations. La manière naïve était de faire une boucle while et d'augmenter la taille de la police de 1 jusqu'à ce que l'élément commence à déborder. Vous pouvez déterminer quand un élément commence à déborder en utilisant element.offsetHeight et element.scrollHeight . Si scrollHeight est plus grand que offsetHeight, votre taille de police est trop grande.

La recherche binaire est un bien meilleur algorithme pour cela. Il est également limité par le nombre d'itérations que vous souhaitez effectuer. Appelez simplement flexFont et insérez l'ID div et cela ajustera la taille de la police entre 8px et 96px .

J'ai passé un peu de temps à faire des recherches sur ce sujet et à essayer différentes bibliothèques, mais en fin de compte, je pense que c'est la solution la plus simple et la plus simple qui fonctionnera réellement.

Notez que si vous le souhaitez, vous pouvez modifier pour utiliser offsetWidthet scrollWidth, ou ajouter les deux à cette fonction.

// 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
la source
Merci, ça a l'air super! Je commence juste ReferenceError: lastSizeTooSmall is not defined. Peut-être que cela doit être défini quelque part?
ndbroadbent
0

J'ai le même problème et la solution consiste essentiellement à utiliser javascript pour contrôler la taille de la police. Vérifiez cet exemple sur codepen:

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

Cet exemple ne concerne que la hauteur, vous devrez peut-être mettre quelques if sur la largeur.

essayez de le redimensionner

<!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
la source
0

J'ai aimé

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
la source
0

Je voulais juste ajouter ma version pour 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
la source
0

J'ai trouvé un moyen d'empêcher l'utilisation de boucles pour réduire le texte. Il ajuste la taille de la police en la multipliant par le taux entre la largeur du conteneur et la largeur du contenu. Donc, si la largeur du conteneur est 1/3 du contenu, la taille de la police sera réduite de 1/3 et la largeur du conteneur. Pour évoluer, j'ai utilisé une boucle while, jusqu'à ce que le contenu soit plus grand que le conteneur.

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

Ce code fait partie d'un test que j'ai téléchargé sur Github https://github.com/ricardobrg/fitText/

Ricardo BRGWeb
la source
0

Je suis allé avec la solution geekMonkey, mais c'est trop lent. Ce qu'il fait, c'est qu'il ajuste la taille de la police au maximum (maxFontPixels) puis vérifie si elle tient à l'intérieur du conteneur. sinon, il réduit la taille de la police de 1px et vérifie à nouveau. Pourquoi ne pas simplement vérifier la hauteur du conteneur précédent et soumettre cette valeur? (oui, je sais pourquoi, mais j'ai maintenant fait une solution, qui ne fonctionne que sur la hauteur et a également une option min / max)

Voici une solution beaucoup plus rapide:

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

et ce serait le HTML:

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

Encore une fois: cette solution vérifie UNIQUEMENT la hauteur du conteneur. C'est pourquoi cette fonction n'a pas à vérifier si l'élément rentre à l'intérieur. Mais j'ai également implémenté une valeur min / max (40min, 150max) donc pour moi cela fonctionne parfaitement bien (et fonctionne également sur le redimensionnement des fenêtres).

honk31
la source
-1

Voici une autre version de cette solution:

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