jQuery 自动调整动态文本大小以填充固定大小的容器
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/687998/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Auto-size dynamic text to fill fixed size container
提问by GeekyMonkey
I need to display user entered text into a fixed size div. What i want is for the font size to be automatically adjusted so that the text fills the box as much as possible.
我需要将用户输入的文本显示到固定大小的 div 中。我想要的是自动调整字体大小,以便文本尽可能地填充框。
So - If the div is 400px x 300px. If someone enters ABC then it's really big font. If they enter a paragraph, then it would be a tiny font.
所以 - 如果 div 是 400px x 300px。如果有人输入 ABC 那么它的字体真的很大。如果他们输入一个段落,那么它将是一个小字体。
I'd probably want to start with a maximum font size - maybe 32px, and while the text is too big to fit the container, shrink the font size until it fits.
我可能想从最大字体大小开始 - 可能是 32px,虽然文本太大而无法容纳容器,但缩小字体大小直到适合。
采纳答案by GeekyMonkey
Thanks Attack. I wanted to use jQuery.
感谢攻击。我想使用 jQuery。
You pointed me in the right direction, and this is what I ended up with:
你给我指明了正确的方向,这就是我的结果:
Here is a link to the plugin: https://plugins.jquery.com/textfill/
And a link to the source: http://jquery-textfill.github.io/
这是插件的链接:https: //plugins.jquery.com/textfill/
和源链接: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 });
});
and my html is like this
我的 html 是这样的
<div class='jtextfill' style='width:100px;height:50px;'>
<span>My Text Here</span>
</div>
This is my first jquery plugin, so it's probably not as good as it should be. Pointers are certainly welcome.
这是我的第一个 jquery 插件,所以它可能没有它应该的那么好。当然欢迎指点。
回答by mekwall
I didn't find any of the previous solutions to be adequate enough due to bad performance, so I made my own that uses simple math instead of looping. Should work fine in all browsers as well.
由于性能不佳,我没有发现以前的任何解决方案都足够,所以我自己制作了使用简单数学而不是循环的解决方案。在所有浏览器中也应该可以正常工作。
According to this performance test caseit is much faster then the other solutions found here.
根据此性能测试用例,它比此处找到的其他解决方案要快得多。
(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);
If you want to contribute I've added this to Gist.
如果您想做出贡献,我已将其添加到 Gist。
回答by attack
As much as I love the occasional upvotes I get for this answer (thanks!), this is really not the greatest approach to this problem. Please check out some of the other wonderful answers here, especially the ones that have found solutions without looping.
尽管我喜欢偶尔为这个答案点赞(谢谢!),但这真的不是解决这个问题的最好方法。请在此处查看其他一些精彩的答案,尤其是那些无需循环即可找到解决方案的答案。
Still, for the sake of reference, here's my original answer:
不过,为了参考起见,这是我的原始答案:
<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>
And here's a version with 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>
回答by Hoffmann
Most of the other answers use a loop to reduce the font-size until it fits on the div, this is VERY slow since the page needs to re-render the element each time the font changes size. I eventually had to write my own algorithm to make it perform in a way that allowed me to update its contents periodically without freezing the user browser. I added some other functionality (rotating text, adding padding) and packaged it as a jQuery plugin, you can get it at:
大多数其他答案使用循环来减小字体大小,直到它适合 div,这非常慢,因为每次字体更改大小时页面都需要重新渲染元素。我最终不得不编写自己的算法,使其以一种允许我定期更新其内容的方式执行,而不会冻结用户浏览器。我添加了一些其他功能(旋转文本、添加填充)并将其打包为 jQuery 插件,您可以在以下位置获取它:
https://github.com/DanielHoffmann/jquery-bigtext
https://github.com/DanielHoffmann/jquery-bigtext
simply call
简单地打电话
$("#text").bigText();
and it will fit nicely on your container.
它非常适合您的容器。
See it in action here:
在这里查看它的实际效果:
http://danielhoffmann.github.io/jquery-bigtext/
http://danielhoffmann.github.io/jquery-bigtext/
For now it has some limitations, the div must have a fixed height and width and it does not support wrapping text into multiple lines.
现在它有一些限制,div 必须有固定的高度和宽度,它不支持将文本换行成多行。
I will work on getting an option to set the maximum font-size.
我将努力获得设置最大字体大小的选项。
Edit: I have found some more problems with the plugin, it does not handle other box-model besides the standard one and the div can't have margins or borders. I will work on it.
编辑:我发现该插件还有一些问题,除了标准框模型之外,它无法处理其他框模型,并且 div 不能有边距或边框。我会努力的。
Edit2: I have now fixed those problems and limitations and added more options. You can set maximum font-size and you can also choose to limit the font-size using either width, height or both. I will work into accepting a max-width and max-height values in the wrapper element.
Edit2:我现在已经解决了这些问题和限制,并添加了更多选项。您可以设置最大字体大小,也可以选择使用宽度、高度或两者来限制字体大小。我将努力接受包装元素中的 max-width 和 max-height 值。
Edit3: I have updated the plugin to version 1.2.0. Major cleanup on the code and new options (verticalAlign, horizontalAlign, textAlign) and support for inner elements inside the span tag (like line-breaks or font-awesome icons.)
Edit3:我已将插件更新到 1.2.0 版。主要清理代码和新选项(verticalAlign、horizontalAlign、textAlign)并支持 span 标签内的内部元素(如换行符或字体很棒的图标。)
回答by sandstrom
This is based on what GeekyMonkey posted above, with some modifications.
这是基于 GeekyMonkey 上面发布的内容,并进行了一些修改。
; (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);
回答by Luke Hutchison
Here's an improved looping method that uses binary search to find the largest possible size that fits into the parent in the fewest steps possible (this is faster and more accurate than stepping by a fixed font size). The code is also optimized in several ways for performance.
这是一种改进的循环方法,它使用二进制搜索以尽可能少的步骤找到适合父级的最大可能大小(这比按固定字体大小步进更快、更准确)。代码还以多种方式优化了性能。
By default, 10 binary search steps will be performed, which will get within 0.1% of the optimal size. You could instead set numIter to some value N to get within 1/2^N of the optimal size.
默认情况下,将执行 10 个二分搜索步骤,这将在最佳大小的 0.1% 以内。您可以将 numIter 设置为某个值 N 以获得最佳大小的 1/2^N 以内。
Call it with a CSS selector, e.g.: fitToParent('.title-span');
使用 CSS 选择器调用它,例如: 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);
}
});
};
回答by sqren
I've created a directive for AngularJS - heavely inspired by GeekyMonkey's answer but without the jQuery dependency.
我为 AngularJS 创建了一个指令 - 受到 GeekyMonkey 答案的极大启发,但没有 jQuery 依赖。
Demo:http://plnkr.co/edit/8tPCZIjvO3VSApSeTtYr?p=preview
演示:http : //plnkr.co/edit/8tPCZIjvO3VSApSeTtYr?p=preview
Markup
标记
<div class="fittext" max-font-size="50" text="Your text goes here..."></div>
<div class="fittext" max-font-size="50" text="Your text goes here..."></div>
Directive
指示
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();
});
}
};
});
回答by nimrod
I forked the script above from Marcus Ekwall: https://gist.github.com/3945316and tweaked it to my preferences, it now fires when the window is resized, so that the child always fits its container. I've pasted the script below for reference.
我从 Marcus Ekwall 分叉了上面的脚本:https: //gist.github.com/3945316 并根据我的喜好对其进行了调整,现在它会在调整窗口大小时触发,以便子项始终适合其容器。我已经粘贴了下面的脚本以供参考。
(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);
回答by Boom
Here's my modification of the OP's answer.
这是我对 OP 答案的修改。
In short, many people who tried to optimize this complained that a loop was being used. Yes, while loops can be slow, other approaches can be inaccurate.
简而言之,许多试图优化它的人抱怨正在使用循环。是的,虽然循环可能很慢,但其他方法可能不准确。
Therefore, my approach uses Binary Searchto find the best Font Size:
因此,我的方法使用二进制搜索来找到最佳字体大小:
$.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);
This also allows the element to specify the minimum and maximum font:
这也允许元素指定最小和最大字体:
<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>
Furthermore, this algorithm is unitless. You may specify em
, rem
, %
, etc. and it will use that for its final result.
此外,该算法是无单位的。您可以指定em
、rem
、%
等,它会将其用于最终结果。
Here's the Fiddle: https://jsfiddle.net/fkhqhnqe/1/
这是小提琴:https: //jsfiddle.net/fkhqhnqe/1/
回答by guillaumepotier
I had exactly the same problem with my website. I have a page that is displayed on a projector, on walls, big screens..
我的网站遇到了完全相同的问题。我有一个页面显示在投影仪、墙壁、大屏幕上。
As I don't know the max size of my font, I re-used the plugin above of @GeekMonkey but incrementing the fontsize :
由于我不知道字体的最大大小,我重新使用了 @GeekMonkey 上面的插件,但增加了 fontsize :
$.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 );
});
};