Недавно получил заказ, где между всего прочего нужно было сделать блок, в котором текст должен масштабироваться внутри блока. Т.е не зависит, сколько текста в блоке – весь текст должен быть видимым! Сначала думал считать символы, строки… Сверстал блок в котором должен находиться контент и при вводе текста заметил, что блок растягивается в зависимости от наполнения. Родилась идея сделать функцию, которая будет подбирать размер шрифта, сравнивая исходную высоту блока и настоящую. Так родилась вот эта функция:

image


function font_size_determination($block,height,width,font_size){
        $block.css('font-size',font_size);
        var block_height = $block.height();
        var block_width = $block.width();
        if (block_height > height || block_width > width){
            font_size = font_size*0.9;
            return font_size_determination($block,height,width,font_size);
        } else {
            return font_size;
        }
    }

Если подробно – то это рекурсивная функция – с помощью .css() подставляет размер шрифта блоку $block, который передан в функцию, потом проверяет его ширину и высоту. Если они соответствуют первоначально заданным параметрам – функция возвращает размер шрифта, если нет – уменьшает шрифт на 10% и проверяет, и так, пока блок не будет соответствовать заданным первоначально ширине и высоте.

Пример того, что получилось можно увидеть здесь. Начинайте вводить текст в текстовом поле.

Вот код. CSS:

.left{
  width:220px;
  float:left;
}
.right{
  width:100%;
  padding-left:220px;
}
.right textarea{
  width:100%;
  height:100px;
}
.frame{
  width:200px;
  height:300px;
  border:3px solid silver;
  font-family:Arial;
}
.frame span {
    display: table-cell;
    vertical-align: middle;
}
.frame-content{
  display:table;
   width:100%;
   height:30%;
   background-color:black;
   color:white;
    padding:0 4px;
    text-align:center;
}
.frame-content-2{
  display:table;
   width:100%;
   height:70%;
   background-color:white;
   padding:0 4px;
}

HTML:

<div class="left">
  <div class="frame">
    <div class="frame-content">
      <span></span>
    </div>
    <div class="frame-content-2">
      <span></span>
    </div>
  </div>
</div>
<div class="right">
  <textarea id="title-text" placeholder="Title"></textarea>
  <textarea id="text" placeholder="Content"></textarea>
</div>

JAVASCRIPT:


$('#text').keyup(function(){
        var text = $(this).val();
        var $block = $('.left').find('.frame').find('.frame-content-2');
        text_scaling($block,text);
    });
    $('#title-text').keyup(function(){
        var text = $(this).val();
        var $block = $('.left').find('.frame').find('.frame-content');
        text_scaling($block,text);
    });
function text_scaling($block,text){
        var width_block = $block.width();
        var height_block = $block.height();
        text = text.replace(new RegExp('\r?\n','g'), '<br />');
        $block.find('span').html(text);
        if(text){
            var start_font = height_block*0.8;
            var font_size = font_size_determination($block,height_block,width_block,start_font);
            $block.css('font-size',font_size);
        }
     
    }
    function font_size_determination($block,height,width,font_size){
        $block.css('font-size',font_size);
        var block_height = $block.height();
        var block_width = $block.width();
        if (block_height > height || block_width > width){
            font_size = font_size*0.9;
            return font_size_determination($block,height,width,font_size);
        } else {
            return font_size;
        }
    }

Функцию font_size_determination я описал выше, кстати, ее можно использовать одну. Но для удобства (у меня три блока в которые необходимо вводить текст – на примере их 2) я сделал функцию text_scaling, в которую передается блок и текст. Текст берется из textarea с помощью обработчика событий .keyup. Эта функция определяет стартовые высоту и ширину, к которым будет стремиться функция font_size_determination. Начальная высота шрифта задана в 80% от высоты блока.

После сохранения блока мне необходимо его вывести в шаблоне, чтобы текст располагался в нем как на образце во время ввода.

Для этого я использовал функцию text_scaling, только теперь с помощью .each — обходя все блоки и подбирая размер шрифта в них.

$('.left').find('.frame').each(function(){
        var $this = $(this);
        var $title = $this.find('.frame-content');
        var $content = $this.find('.frame-content-2');

        $title.css('font-size','1px');
        $content.css('font-size','1px');

        var text_title = $title.find('span').html();
        var text_content = $content.find('span').html();

        text_scaling($title,text_title);
        text_scaling($content,text_content);
    });

Здесь пример (в примере 1 блок, но может быть больше).

Сначала шрифт уменьшается до размера 1 пиксель, чтобы не нарушились размеры блока, а потом функция сама подбирает необходимый шрифт для каждого блока.

Такой вот получился простой, но в то же время полезный скрипт. Надеюсь пригодится и вам.
Поделиться с друзьями
-->

Комментарии (13)


  1. DmitrySikorsky
    11.09.2016 02:11
    +1

    Когда-то тоже делал что-то подобное. Рекомендую обратить внимание на «новые» единицы измерения в CSS — vw и vh.

    Кстати, rem имеют лучшую поддержку в браузерах и мы уже повсеместно используем их в рабочих проектах, добавляя правила с px для совместимости.


  1. Finesse
    11.09.2016 06:35
    +10

    Простите за нескромный вопрос, но зачем здесь jQuery? Указанную функцию можно легко и кроссбраузерно реализовать без jQuery:

    function font_size_determination($block,height,width,font_size){
        $block.style.fontSize = fontSize + 'px';
        var block_height = $block.clientHeight;
        var block_width = $block.clientWidth;
        if (block_height > height || block_width > width){
            font_size = font_size*0.9;
            return font_size_determination($block,height,width,font_size);
        } else {
            return font_size;
        }
    }
    
    font_size_determination(document.querySelector('.left .frame .frame-content'), 500, 300, 25);
    


    1. BlenderRU
      12.09.2016 11:26

      К сожалению, как уже писалось в одной из статей на хабре, многие подгружают jQuery «на автомате», не зависимо от решаемой задачи, даже если от библиотеки используется только селектор и обработчик. Для кого-то это дело вкуса или удобства, а кому-то и лишний трафик с ненужным кодом.


  1. bay73
    11.09.2016 11:00
    +1

    А если вместо константы 0.9 использовать что-то вроде Math.sqrt(height*width/(block_height*block_width)), то количество итераций сократится до парочки.


    1. Sergey_Peschekhonow
      16.09.2016 15:23
      +1

      Спасибо за замечание.



  1. radist2s
    11.09.2016 15:21

    Vanila решение в лоб.


    <div class="contain" style="width: 100px; height: 100px">
      <div class="containInner">
        Some text inside
        Some text inside
      </div>
    </div>

    var increamentRatio = 1.1,
        decreamentRatio = 1 / increamentRatio
    
    function textContaint(containerNode, textContainerNode, ratio) {
      var width = containerNode.clientWidth,
          height = containerNode.clientHeight,
          innerHeight = textContainerNode.clientHeight,
          innerWidth = textContainerNode.clientWidth
    
      if (width < innerWidth || height < innerHeight) {
        ratio = (ratio && ratio > decreamentRatio) ? false : decreamentRatio
      }
      else if (width > innerWidth || height > innerHeight) {
        ratio = (ratio && ratio < increamentRatio) ? false : increamentRatio
      }
      else {
        return false
      }
    
      if (!ratio) {
        return false
      }
    
      var fontSize = parseFloat(getComputedStyle(containerNode).fontSize)
      containerNode.style.fontSize = fontSize * ratio + 'px'
    
      return ratio
    }
    
    var textNode = document.querySelector('.contain'),
        textContainerNode = textNode.querySelector('.containInner'),
        fontSizeRatio
    
    while(fontSizeRatio = textContaint(textNode, textContainerNode, fontSizeRatio)) {}


  1. Sulin
    11.09.2016 16:42

    http://fittextjs.com/ — помоему этот плагин делает то же самое, при этом есть пару полезных функций, таких как мин. и макс. размер шрифта, коэфициент можно изменять не только строго 10%.


    1. Sergey_Peschekhonow
      11.09.2016 16:54

      В принципе здесь тоже можно сделать максимальный и минимальный размер шрифта. К сожалению не знал об этом плагине раньше.


  1. nexcode
    11.09.2016 16:42
    -1

    Рекомендую делать контентно-независимую верстку, чем такое.


    1. Sergey_Peschekhonow
      11.09.2016 16:52

      При чем здесь контенто-независимая верстка? Здесь говорится о том, чтобы вписать текст в блок со строго определенным размером. Как вписать в блоки одного размера текст с разным количеством символов, при этом, чтобы он был видимым?
      Например как здесь https://jsfiddle.net/fqzsevfd/3/?


      1. extempl
        12.09.2016 07:12

        Кстати, если блок заранее должен быть определённой высоты, то можно в переборе сравнивать scrollHeight и height. Ну и да, ладно бы это был плагин jQuery, тогда заголовок бы себя оправдал. А так — только на Vanilla JS.