Texto dinámico de tamaño automático para llenar el contenedor de tamaño fijo

312

Necesito mostrar el texto ingresado por el usuario en un div de tamaño fijo. Lo que quiero es que el tamaño de fuente se ajuste automáticamente para que el texto llene el cuadro tanto como sea posible.

Entonces, si el div es 400px x 300px. Si alguien ingresa a ABC, entonces es una fuente realmente grande. Si ingresan un párrafo, entonces sería una fuente pequeña.

Probablemente quiera comenzar con un tamaño de fuente máximo, tal vez 32px, y aunque el texto es demasiado grande para caber en el contenedor, reduzca el tamaño de la fuente hasta que encaje.

GeekyMonkey
fuente
119
Probablemente las características más sorprendentes que deberían haberse agregado a HTML5 / CSS3 sin la necesidad de JS.
John Magnolia
Hice algunas mediciones en las que cambié la longitud de un texto dinámico y el tamaño del contenedor para determinar qué tamaño de fuente hará que el texto se ajuste perfectamente. Y después de hacer un análisis de regresión, se me ocurrió una función matemática simple que generará automáticamente el mejor tamaño de fuente.
Amigo de Kim
2
En realidad, resulta que el gráfico que le da el mejor tamaño de fuente está dado por f (x) = g (letras) * (x / 1000) ^ n, donde g (x) es una función simple, n varía dependiendo de La fuente que está utilizando. (Aunque puede tener un valor estándar para todas las fuentes, si no desea modificarlo para que sea absolutamente perfecto ...). x es el tamaño en píxeles cuadrados del contenedor.
Amigo de Kim
1
Si todavía está interesado, puedo agregar una respuesta. Personalmente, creo que es un método mucho mejor para generar el tamaño de fuente correcto en primer lugar, en lugar de intentarlo y fallar hasta que el script "lo haga bien".
Amigo de Kim
1
Verifique mi respuesta para una mejor manera de hacer esto
Hoffmann

Respuestas:

167

Gracias ataque . Quería usar jQuery.

Me apuntaste en la dirección correcta, y esto es con lo que terminé:

Aquí hay un enlace al complemento: https://plugins.jquery.com/textfill/
Y un enlace a la fuente: 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 });
});

y mi html es así

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

Este es mi primer complemento de jquery, por lo que probablemente no sea tan bueno como debería ser. Los punteros son ciertamente bienvenidos.

GeekyMonkey
fuente
8
De hecho, lo limpié y lo empaqueté como un complemento disponible en jquery.com en plugins.jquery.com/project/TextFill
GeekyMonkey el
3
@GeekyMonkey, ¿sacaste el complemento? Simplemente seguí un enlace dupe a esta página, y pensé en echar un vistazo, pero los enlaces jQuery.com a su sitio regresan 404.
David dice que reinstale a Mónica el
Nota: descubrí que, por alguna razón, este complemento funciona solo cuando el div ($ ('. Jtextfill') en el ejemplo anterior) es parte del documento raíz. Parece que .width () devuelve cero cuando el div está incrustado dentro de otros divs.
Jayesh
1
La línea "while" en ese bucle me parece incorrecta: debería haber paréntesis alrededor de "||" subexpresión De la forma en que está escrito ahora, el tamaño mínimo de la fuente solo se verifica cuando el ancho es demasiado grande, no la altura.
Pointy
44
Este enfoque es MUY lento, cada vez que la fuente cambia de tamaño requiere que el elemento se vuelva a representar. Mira mi respuesta para una mejor manera de hacer esto.
Hoffmann
52

No encontré que ninguna de las soluciones anteriores fuera lo suficientemente adecuada debido al mal rendimiento, así que hice la mía que usa matemáticas simples en lugar de bucles. Debería funcionar bien en todos los navegadores también.

Según este caso de prueba de rendimiento , es mucho más rápido que las otras soluciones encontradas aquí.

(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 quieres contribuir, he agregado esto a Gist .

Mekwall
fuente
1
@ Jon, gracias! Tiene razón en que mi script no hace varias líneas, pero nuevamente el OP no lo solicitó específicamente, por lo que su suposición podría estar equivocada. Además, ese tipo de comportamiento no tiene mucho sentido. Supongo que la mejor manera de agregar soporte de varias líneas sería dividir la cadena en función de la cantidad de palabras y luego calcular cada parte con el script anterior y, de todos modos, lo más probable es que sea más rápido.
mekwall
44
@ Jon, jugué un poco con el relleno de texto multilínea y terminé con esta solución . El método de tormenta de arena es probablemente más preciso pero este es más rápido;)
mekwall
2
Aquí hay una versión con un tamaño de fuente mínimo y máximo: gist.github.com/1714284
Jess Telford
1
@Hoffmann Hmm. Mi solución no llama .css("font-size")en un bucle. ¿De dónde has sacado eso? Mi solución es probablemente más rápida ya que no tiene ninguna de las cosas elegantes que agregaste a tu complemento. Puede agregar su complemento a jsperf y veremos cuál es el más rápido;)
mekwall
1
@MarcusEkwall Oh, lo siento, por alguna razón, pensé que vi un ciclo de tiempo allí. Su enfoque es similar al mío y, de hecho, mi sería un poco más lento ya que mi complemento también hace algunas otras cosas (como ajustar para adaptarse tanto a la altura como al ancho, centraliza el texto y algunas otras opciones), eso no importa, la parte realmente lenta llama a la función .css dentro de un bucle.
Hoffmann
35

Por mucho que me encanten los votos positivos ocasionales que recibo por esta respuesta (¡gracias!), Este no es realmente el mejor enfoque para este problema. Consulte algunas de las otras respuestas maravillosas aquí, especialmente las que han encontrado soluciones sin bucles.


Aún así, como referencia, aquí está mi respuesta original :

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

Y aquí hay una versión con clases :

<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>
ataque
fuente
3
Encontré que esto funcionó mejor offsetWidth, también tuve que crear una variable para el tamaño y luego agregar el pxtextSpan.style.fontSize = size+"px";
Wez
2
seguro '+ "px"' es necesario.
sandun dhammika
@IdanShechter ¡Perdón por la larga, larga espera! ¡Agregó un ejemplo!
ataque el
¡Gracias salvavidas! No conozco jQuery, así que sigo con su solución :)
vintproykt
32

La mayoría de las otras respuestas usan un bucle para reducir el tamaño de la fuente hasta que encaje en el div, esto es MUY lento ya que la página necesita volver a representar el elemento cada vez que la fuente cambia de tamaño. Eventualmente tuve que escribir mi propio algoritmo para que funcionara de una manera que me permitiera actualizar su contenido periódicamente sin congelar el navegador del usuario. Agregué alguna otra funcionalidad (rotar texto, agregar relleno) y lo empaqueté como un complemento jQuery, puede obtenerlo en:

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

simplemente llama

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

y encajará bien en su contenedor.

Véalo en acción aquí:

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

Por ahora tiene algunas limitaciones, el div debe tener una altura y un ancho fijos y no admite el ajuste de texto en varias líneas.

Trabajaré para obtener una opción para establecer el tamaño máximo de fuente.

Editar: He encontrado algunos problemas más con el complemento, no maneja otro modelo de caja además del estándar y el div no puede tener márgenes o bordes. Voy a trabajar en él.

Edit2: ahora he solucionado esos problemas y limitaciones y he agregado más opciones. Puede establecer el tamaño máximo de fuente y también puede elegir limitar el tamaño de fuente usando ancho, alto o ambos. Trabajaré para aceptar valores de ancho máximo y alto máximo en el elemento contenedor.

Edit3: he actualizado el complemento a la versión 1.2.0. Limpieza importante en el código y nuevas opciones (verticalAlign, horizontalAlign, textAlign) y soporte para elementos internos dentro de la etiqueta span (como saltos de línea o iconos de fuente impresionante).

Hoffmann
fuente
1
Me pregunto por qué no se admite el ajuste de texto en varias líneas.
Manish Sapariya
1
@ManishSapariya Es compatible, pero debe agregar los saltos de línea (etiquetas br) manualmente. La razón por la que no soporto el ajuste automático de texto es porque para que sea rápido (solo cambie el tamaño de fuente dos veces en lugar de varias veces) necesito asumir que el texto no se ajustará entre palabras. La forma en que funciona mi complemento es establecer el tamaño de fuente en 1000 px y luego ver el factor del tamaño del texto que se compara con el contenedor, luego reduzco el tamaño de fuente en el mismo factor. Para admitir el ajuste de texto normal, necesitaría usar el enfoque lento (reducir el tamaño de fuente varias veces) que es muy lento.
Hoffmann
¡Oye! Como no hay mensajes privados aquí, en StackOverflow, tendré que preguntarle comentando su respuesta. Me encanta tu complemento jQuery, pero no puedo hacer que funcione para mí. Incluí la biblioteca jQuery correcta, descargué tu complemento y lo incluí. Ahora, cuando intento usarlo, la consola dice 'Error de tipo no capturado: indefinido no es una función'. ¿Es esto algo con lo que estás familiarizado? ¿Sabes cómo arreglar ésto? Gracias
Gust van de Wal
@GustvandeWal Debe incluir el complemento después de incluir la biblioteca jquery
Hoffmann
Yo hice. Tengo <script type = "text / javascript" src = " code.jquery.com/jquery-2.1.1.min.js"></… src" js / jquery-bigtext.js "> </ script > El navegador no me notifica que no puede cargar la biblioteca jQuery o el complemento.
Gust van de Wal
9

Esto se basa en lo que GeekyMonkey publicó anteriormente, con algunas modificaciones.

; (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
fuente
La diferencia es que puede tomar múltiples elementos secundarios y que tiene en cuenta el relleno. Utiliza el tamaño de fuente como tamaño máximo predeterminado, para evitar mezclar JavaScript y CSS.
Sandstrom
55
Esto es genial, pero ¿cómo lo uso? Hago $ ('. Exterior'). Textfill (); Y no tengo cambio.
Drew Baker,
3
Gracias, esta es una muy buena implementación. Una cosa con la que me encontré: si se trata de cadenas de texto muy largas y contenedores muy estrechos, la cadena de texto sobresaldrá del contenedor, pero el ancho externo todavía se calculará como si no fuera así. Mezcle "word-wrap: break-word"; en su CSS para ese contenedor, solucionará este problema.
Jon
8

Aquí hay un método de bucle mejorado que utiliza la búsqueda binaria para encontrar el tamaño más grande posible que se ajuste al padre en la menor cantidad de pasos posible (esto es más rápido y más preciso que pasar por un tamaño de fuente fijo). El código también está optimizado de varias maneras para el rendimiento.

De forma predeterminada, se realizarán 10 pasos de búsqueda binaria, que obtendrán un 0.1% del tamaño óptimo. En su lugar, puede establecer numIter en algún valor N para obtener 1/2 ^ N del tamaño óptimo.

Llámalo con un selector CSS, por ejemplo: 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
fuente
Amo esta opción. Lo modifiqué para agregar parámetros para vAligny padding. vAlign == trueestablece la altura de línea del elemento seleccionado a la altura de los padres. El relleno disminuye el tamaño final por el valor pasado. El valor predeterminado es 5. Creo que se ve muy bien.
Abogado del Diablo
6

He creado una directiva para AngularJS, inspirada en gran medida por la respuesta de GeekyMonkey pero sin la dependencia de jQuery.

Demostración: http://plnkr.co/edit/8tPCZIjvO3VSApSeTtYr?p=preview

Margen

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

Directiva

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
fuente
Un problema que he tenido con esto es que resizeTextparece ser llamado antes de que ng-bindrealmente asigne el texto al elemento, lo que resulta en un tamaño basado en el texto anterior en lugar del texto actual. Esto no es tan malo en la demostración anterior donde se llama repetidamente a medida que el usuario escribe, pero si se llama una vez que pasa del valor nulo al valor real (como en un enlace unidireccional) permanece en el tamaño máximo.
Miral
5

Bifurqué el script anterior de Marcus Ekwall: https://gist.github.com/3945316 y lo ajusté a mis preferencias, ahora se dispara cuando se cambia el tamaño de la ventana, para que el niño siempre se ajuste a su contenedor. He pegado el script a continuación para referencia.

(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
fuente
2
Es genial que estés tratando de ayudar al que pregunta. Sin embargo, dejar una respuesta solo con un enlace puede ser perjudicial en algunos casos. Si bien su respuesta es buena ahora, si el enlace alguna vez muriera, su respuesta perdería su valor. Por lo tanto, será útil si resume el contenido del artículo en su respuesta. Ver esta pregunta para aclaraciones.
Cody Guldner
5

Aquí está mi modificación de la respuesta del OP.

En resumen, muchas personas que intentaron optimizar esto se quejaron de que se estaba utilizando un bucle. Sí, aunque los bucles pueden ser lentos, otros enfoques pueden ser inexactos.

Por lo tanto, mi enfoque utiliza la búsqueda binaria para encontrar el mejor tamaño de fuente:

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

Esto también permite que el elemento especifique la fuente mínima y máxima:

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

Además, este algoritmo no tiene unidades. Se puede especificar em, rem, %, etc, y se va a utilizar para que su resultado final.

Aquí está el violín: https://jsfiddle.net/fkhqhnqe/1/

Auge
fuente
2

Tuve exactamente el mismo problema con mi sitio web. Tengo una página que se muestra en un proyector, en paredes, pantallas grandes ...

Como no sé el tamaño máximo de mi fuente, reutilicé el complemento anterior de @GeekMonkey pero incrementé el tamaño de la fuente:

$.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
fuente
¡+1 por ser el único complemento en esta página que realmente funcionó para mí!
skybondsor
2
Este complemento bloquea la página para mí.
Jezen Thomas
1

Aquí hay una versión de la respuesta aceptada que también puede tomar un parámetro 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
fuente
gracias, aunque creo que tienes un punto y coma en la parte superior del código que no debería estar allí
Patrick Moore
1

Puede usar FitText.js ( página github ) para resolver este problema. Es realmente pequeño y eficiente en comparación con TextFill. TextFill utiliza un costoso loop while y FitText no.

Además, FitText es más flexible (¡lo uso en un proyecto con requisitos muy especiales y funciona como un campeón!).

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>

También puede configurar opciones para ello:

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

CSS:

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

Y si lo necesita, FitText también tiene una versión sin jQuery .

eduludi
fuente
¿Fittext tiene en cuenta la altura?
Manish Sapariya
1
@ManishSapariya no, no lo hace. Simplemente divide el ancho del contenedor por 10 y lo usa como tamaño de fuente.
Dan H
1

EDITAR: Este código se usó para mostrar notas en la parte superior de un video HTML5. Cambia el tamaño de fuente sobre la marcha cuando se cambia el tamaño del video (cuando se cambia el tamaño de la ventana del navegador). Las notas se conectaron al video (al igual que las notas en YouTube), por lo que el código usa instancias en lugar de un controlador DOM directamente.

Según la solicitud, agregaré un código que usé para lograr esto. (Cuadros de texto sobre un video HTML5.) El código fue escrito hace mucho tiempo, y francamente creo que es bastante desordenado. Dado que la pregunta ya está respondida y una respuesta ya fue aceptada hace mucho tiempo, no me molesto en volver a escribir esto. Pero si alguien quiere simplificar esto un poco, ¡eres más que bienvenido!

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

Probablemente necesite ajustar estos números de font-family a font-family. Una buena manera de hacerlo es descargar un visualizador gráfico gratuito llamado GeoGebra. Cambia la longitud del texto y el tamaño del cuadro. Luego establece manualmente el tamaño. Trace los resultados manuales en el sistema de coordenadas. Luego ingresa las dos ecuaciones que he publicado aquí y ajusta los números hasta que "mi" gráfico se ajuste a sus propios puntos trazados manualmente.

Amigo de kim
fuente
1

Las soluciones iterativas propuestas se pueden acelerar dramáticamente en dos frentes:

1) Multiplique el tamaño de la fuente por alguna constante, en lugar de sumar o restar 1.

2) Primero, cero en el uso de una constante de curso, digamos, duplicar el tamaño de cada ciclo. Luego, con una idea aproximada de por dónde empezar, haga lo mismo con un ajuste más fino, digamos, multiplique por 1.1. Si bien el perfeccionista puede querer el tamaño exacto de píxel entero de la fuente ideal, la mayoría de los observadores no notan la diferencia entre 100 y 110 píxeles. Si eres un perfeccionista, repite una tercera vez con un ajuste aún más fino.

En lugar de escribir una rutina específica o un complemento que responda a la pregunta exacta, solo confío en las ideas básicas y escribo variaciones del código para manejar todo tipo de problemas de diseño, no solo texto, incluidos los divisores de ajuste, los tramos, las imágenes, etc. .. por ancho, alto, área, ... dentro de un contenedor, haciendo coincidir otro elemento ...

Aquí hay un ejemplo:

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

Esta es la solución más elegante que he creado. Utiliza la búsqueda binaria, haciendo 10 iteraciones. La forma ingenua era hacer un ciclo while y aumentar el tamaño de la fuente en 1 hasta que el elemento comenzara a desbordarse. Puede determinar cuándo un elemento comienza a desbordarse utilizando element.offsetHeight y element.scrollHeight . Si scrollHeight es más grande que offsetHeight, tiene un tamaño de fuente que es demasiado grande.

La búsqueda binaria es un algoritmo mucho mejor para esto. También está limitado por el número de iteraciones que desea realizar. Simplemente llame a flexFont e inserte la identificación div y ajustará el tamaño de fuente entre 8px y 96px .

He pasado algún tiempo investigando este tema y probando diferentes bibliotecas, pero finalmente creo que esta es la solución más sencilla y directa que realmente funcionará.

Tenga en cuenta que si lo desea, puede cambiar el uso offsetWidthy scrollWidth, o agregar ambos a esta función.

// 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
fuente
Gracias, esto se ve genial! Solo estoy entrando ReferenceError: lastSizeTooSmall is not defined. Tal vez eso necesita ser definido en alguna parte?
ndbroadbent
0

Tengo el mismo problema y la solución es básicamente usar JavaScript para controlar el tamaño de fuente. Verifique este ejemplo en codepen:

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

Este es un ejemplo solo para la altura, tal vez necesite poner algunos si es sobre el ancho.

intenta cambiar el tamaño

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

Me gustó

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

Solo quería agregar mi versión para 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
fuente
0

He encontrado una manera de evitar el uso de bucles para reducir el texto. Ajusta el tamaño de fuente multiplicándolo por la velocidad entre el ancho del contenedor y el ancho del contenido. Entonces, si el ancho del contenedor es 1/3 del contenido, el tamaño de la fuente se reducirá en 1/3 y el ancho del contenedor. Para escalar, he usado un ciclo while, hasta que el contenido es más grande que el contenedor.

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

Este código es parte de una prueba que he subido a Github https://github.com/ricardobrg/fitText/

Ricardo BRGWeb
fuente
0

Fui con la solución geekMonkey, pero es demasiado lenta. Lo que hace es ajustar el tamaño de fuente al máximo (maxFontPixels) y luego verifica si cabe dentro del contenedor. de lo contrario, reduce el tamaño de la fuente en 1 px y vuelve a verificar. ¿Por qué no simplemente verifica la altura del contenedor anterior y envía ese valor? (sí, sé por qué, pero ahora hice una solución, que solo funciona en la altura y también tiene una opción min / max)

Aquí hay una solución mucho más rápida:

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

y este sería el HTML:

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

Nuevamente: esta solución SOLO verifica la altura del contenedor. Es por eso que esta función no tiene que verificar si el elemento encaja dentro. Pero también implementé un valor min / max (40min, 150max), así que para mí esto funciona perfectamente bien (y también funciona en el cambio de tamaño de la ventana).

honk31
fuente
-1

Aquí hay otra versión de esta solución:

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
fuente