2013-04-06 2 views
2

Я использую функцию jQuery ниже с именем textfill на сотнях div. В основном это изменяет размер внутреннего текста, чтобы он соответствовал охватывающему div таким образом, чтобы размер шрифта текста был максимальным, поэтому более длинные тексты меньше, чем короткие, но имеют максимальный размер шрифта, который они могут быть без переполнения из div.jQuery script efficiency

; (function($) { 
    /** 
    * Resizes an inner element's font so that the inner element completely fills the outer element. 
    * @version 0.1 
    * @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, 
      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 > 3); 
      var pos = (maxHeight-textHeight)/2; 
      ourText.css('top', pos +'px'); 
     }); 
    }; 
})(jQuery); 

Потому что я запускаю этот скрипт на сотни дивы, которые выглядят как:

<div class="textDiv"><span>text appears here</span></div> 

В то же время с помощью:

$('.textDiv').each(function() { $(this).textfill({ maxFontPixels: 28 })}); 

Это занимает от 40 до 70 секунд в зависимости от количества из divs. Мне отчаянно нужно настроить код, чтобы он работал быстрее. Я пробовал последние два часа, но не могу заставить его работать быстрее. Может кто-нибудь помочь?

EDIT:

Взял некоторый вклад от комментариев и изменил код:

var items = document.getElementsByClassName("textDiv"); 
for (var i = items.length; i--;) { 
    $(items[i]).textfill({ maxFontPixels: 28 }); 
} 

Это, кажется, немного быстрее, но по-прежнему очень медленно.

+0

То, что вы пытаетесь, является дорогостоящей операцией. Но я помню, как однажды читал (извините, нет источника), что «каждая» функция довольно неэффективна. Вы пробовали использовать обычный цикл javascript for? – Ruben

+0

Другое дело, что я пытался использовать '$ ('. TextDiv'). Textfill ({maxFontPixels: 28});', но я думаю, что это неявный вызов 'each' – Tom

+0

Tom, что пример в вашем комментарии будет более эффективным. В вашем плагине 'textfill()' это _explicit_ вызов '.each()' независимо от того, какой объект jQuery вы проходите, но способ комментария не позволяет настраивать значения по умолчанию повторно в плагине. '.each()' в плагине можно ускорить, переведя в стандартный цикл 'for', как предложил Рубен. – nnnnnn

ответ

1

$('.textDiv').each(function() { $(this).textfill({ maxFontPixels: 28 })});

Вы неправильно используете эту функцию. Каждый (правильный) плагин уже работает над коллекциями jQuery и имеет встроенный each, так что вам не нужно размещать его вокруг вызова. Просто сделайте

$('.textDiv').textfill({ maxFontPixels: 28 }); 

Но я думаю, что это не ваша настоящая проблема; петли довольно быстрые и даже для сотен предметов это не займет секунд.Проблема заключается в

  ourText.css('font-size', fontSize); 
      textHeight = ourText.height(); 
      textWidth = ourText.width(); 

внутри цикла (на самом деле в двух вложенных циклах), так как она требует полного оплавления браузера. Вам нужно будет минимизировать вызовы этой части, например, используя какой-то бинарный поиск (bisection) и/или путем применения показателя interpolation, который приблизительно соответствует размеру шрифта (количество символов, разделенных на область, например?), Чтобы получить хорошее начальное значение.

Кроме того, может быть и другие незначительными оптимизациями:

  • кэша $(this)
  • $(Opts.innerTag + ':visible:first', this); выглядит как довольно сложный селектор. Действительно ли это необходимо, вы ожидаете скрытых элементов? Переместите эти дополнительные параметры в параметры и по умолчанию используйте $(this).children().first().
  • Я не уверен в ваших CSS, но как вы устанавливаете размеры ваших divs (которые вы получаете как maxHeight/maxWidth)? Для уменьшения затрат на оплату при изменении шрифта внутри может помочь дополнительный overflow:hidden.
0

Очевидно, что горлышко бутылки является самым внутренним циклом (следующий является родителем самого внутреннего и так далее).

Почему бы не использовать "биективности" для того, чтобы узнать размер шрифта ?:

За 200 дивы:


решение Bisect (необходим рефакторинг):

http://jsfiddle.net/nx2n2/8/

Time: ~700 

Текущее решение:

http://jsfiddle.net/pXL5z/3/

Time: ~1400 

Самый важный код:

 var change = Math.ceil(fontSize/2); 
     while(true) { 
      change = Math.ceil(change/2); 

      var prev = fontSize; 
      do { 
       fontSize = fontSize - change; 
       ourText.css('font-size', fontSize); 

       textHeight = ourText.height(); 
       textWidth = ourText.width(); 
      } while ((textHeight > maxHeight || textWidth > maxWidth) && fontSize > 3); 

      change = Math.ceil(change/2);    
      while (textHeight < maxHeight && textWidth < maxWidth) { 
       fontSize = fontSize + change; 
       ourText.css('font-size', fontSize); 

       textHeight = ourText.height(); 
       textWidth = ourText.width(); 
      } 

      var current = fontSize; 
      if(prev == current) { 
       break; 
      } 
     } 

     // this is because you subtract after change in your original solution 
     // only for 'compatibility' with original solution 
     fontSize = fontSize - 1;