Javascript/Jquery 使文本完美地适合 div 给定尺寸和换行符(如果需要)
Posted
技术标签:
【中文标题】Javascript/Jquery 使文本完美地适合 div 给定尺寸和换行符(如果需要)【英文标题】:Javascript/Jquery making text to fit perfectly on div given dimensions with line break(if needed) 【发布时间】:2019-06-09 18:09:48 【问题描述】:我很难解决这个问题:我使用这些函数来将单行文本适应给定的尺寸。
function getFontSize(width, height, text, font, callback)
var n = 100;
var ctxfont = n + 'px ' + font;
var result = measureTextHeight(ctxfont, text);
while (result.width > width || result.height > height)
n--;
var ctxfont = n + 'px ' + font;
var result = measureTextHeight(ctxfont, text);
callback(
'width': result.width,
'height': result.height,
'size': n
);
function measureTextHeight(ctxFont, text)
var width = 1500;
var height = 500;
var canvas = document.createElement("canvas");
canvas.width = width;
canvas.height = height;
var ctx = canvas.getContext("2d");
ctx.save();
ctx.font = ctxFont;
ctx.clearRect(0, 0, width, height);
ctx.fillText(text, parseInt(width * 0.1, 10), parseInt(height / 2, 10));
ctx.restore();
document.body.appendChild(canvas);
var data = ctx.getImageData(0, 0, width, height).data;
var topMost = false;
var bottomMost = false;
var leftMost = false;
var rightMost = false;
for (var x = 0; x < width; x++)
for (var y = 0; (y < height) && (!leftMost); y++)
//console.log("x: %s y: %s index: %s", x,y, getAlphaIndexForCoordinates(x,y,width,height).toString() );
if (data[getAlphaIndexForCoordinates(x, y, width, height)] != 0)
leftMost = x;
for (var y = 0; y < height; y++)
for (var x = 0; (x < width) && (!topMost); x++)
//console.log("x: %s y: %s index: %s", x,y, getAlphaIndexForCoordinates(x,y,width,height).toString() );
if (data[getAlphaIndexForCoordinates(x, y, width, height)] != 0)
topMost = y;
for (var x = width - 1; x >= 0; x--)
for (var y = height - 1; (y >= 0) && (!rightMost); y--)
//console.log("x: %s y: %s index: %s", x,y, getAlphaIndexForCoordinates(x,y,width,height).toString() );
if (data[getAlphaIndexForCoordinates(x, y, width, height)] != 0)
rightMost = x;
for (var y = height - 1; y >= 0; y--)
for (var x = width - 1; (x >= 0) && (!bottomMost); x--)
//console.log("x: %s y: %s index: %s", x,y, getAlphaIndexForCoordinates(x,y,width,height).toString() );
if (data[getAlphaIndexForCoordinates(x, y, width, height)] != 0)
bottomMost = y;
canvas.remove();
return (
width: (rightMost - leftMost) + 1
, height: (bottomMost - topMost) + 1
);
function getAlphaIndexForCoordinates(x, y, width, height)
return (((width * 4 * y) + 4 * x) + 3);
我将所需的尺寸和字体传递给 getFontSize 函数,它会返回文本的实际宽度和高度以及完成它所需的字体大小。这样,我可以使用 ctx.filltext() 函数将文本绘制到画布上,这是最合适的,并根据其宽度和高度将其对齐在中心。我不确定这是否是实现预期结果的最有效方式,但它正在发挥作用。
我现在想要做的不是让它单行,我可以为 div 设置给定的宽度和高度,然后给出文本,它会完全适合那些尺寸,如果需要添加换行符,并且仍然返回文本的实际宽度和高度,因此我可以像这样将它正确绘制到画布上:http://jsfiddle.net/vkgjrd3e/ 尽管在小提琴中它仍然在 div 的底部有一些空间。
考虑到它的字体和容器尺寸,我想要实现的是最适合文本;如果需要,添加换行符。
【问题讨论】:
你的主要问题是什么?? @Sushil 我正在寻找的是当我将尺寸和文本传递给函数时,它太长了,如果需要,它会自动换行。假设我向函数传递了一个大文本,宽度和高度为 200px。我希望它返回正确的字体大小和带有换行符的文本(如果需要),以便它很好地适应度量。我的代码已经这样做了,但它没有添加换行符,这就是我坚持的地方。如果我需要在画布上添加长文本,它只会缩小它直到它适合一行。检查当前发生的事情:jsfiddle.net/pqhudmj0 @MarcielFonseca 你需要这样的东西吗? jsfiddle.net/bpyqfhaz @Sushil 正是如此。文本将从我的 API 中获取,然后将给定的宽度和高度传递给函数 【参考方案1】:最简单的方法可能是使用 CSS 的强大功能,而不是尝试自己动手。
您可以创建一个虚拟 div,在其中附加一个 ,然后增加它的字体大小,直到它不再适合。 之前的字体大小是正确的。
然后,您可以使用 Range 对象遍历此 的 textContent,以获取 CSS 将生成的换行符。 Range API 提供了一个方便的getBoundingClientRect 方法,让我们可以找到光标所在的位置。我们只需要记住最后一个 y 位置,当它发生变化时,我们就知道之前的字符是该行的最后一个字符。
不过,这种技术也有一些缺点。
在 DOM 中,多个空格字符被压缩为一个。 CanvasContext API 没有相同的行为,所以我们需要在解析文本之前去掉这些。
测量是基于容器边界框进行的,这意味着它不会检查绘制的像素,所以你可能有一些字符溢出(例如 Zalgo͚̠͓ͣ̔͐̽)和其他一些不完全适合 在框中(取决于每个字符的上升和下降)。
...可能是其他人。
function getBestFontSize(text, width, height, userstyles)
if(!text) return null;
const cont = document.createElement('div');
cont.classList.add('best-font-size-tester');
const style = cont.style;
if(typeof width === 'number') width += 'px';
if(typeof height === 'number') height += 'px';
Object.assign(style, width, height, userstyles);
const span = document.createElement('span');
span.textContent = text;
cont.appendChild(span);
document.body.appendChild(cont);
let size = 0;
const max = cont.getBoundingClientRect();
while(true)
style.fontSize = size + 'px';
let rect = span.getBoundingClientRect();
if(rect.bottom > max.bottom || rect.right > max.right)
// overflown
size -= 1; // the correct size was the one before
break;
size++;
if(size === 0)
// even at 0 it doesn't fit...
return null;
// now we'll get the line breaks by walking through our text content
style.fontSize = size + 'px';
const lines = getLineBreaks(span.childNodes[0], max.top);
// cleanup
document.body.removeChild(cont);
return
fontSize: size,
lines: lines
;
function getLineBreaks(node, contTop)
if(!node) return [];
const range = document.createRange();
const lines = [];
range.setStart(node, 0);
let prevBottom = range.getBoundingClientRect().bottom;
let str = node.textContent;
let current = 1;
let lastFound = 0;
let bottom = 0;
while(current <= str.length)
range.setStart(node, current);
if(current < str.length -1)
range.setEnd(node, current+1);
bottom = range.getBoundingClientRect().bottom;
if(bottom > prevBottom)
lines.push(
y: prevBottom - (contTop || 0),
text: str.substr(lastFound , current - lastFound)
);
prevBottom = bottom;
lastFound = current;
current++;
// push the last line
lines.push(
y: bottom - (contTop || 0),
text: str.substr(lastFound)
);
return lines;
const ctx = canvas.getContext('2d');
ctx.textBaseline = 'bottom';
txt_area.oninput = e =>
const input = txt_area.value
.replace(/(\s)(?=\1)/g, ''); // remove all double spaces
ctx.setTransform(1,0,0,1,0,0);
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.translate(19.5,19.5);
ctx.strokeRect(0,0,100,100);
if(!input.length) return;
const bestFit = getBestFontSize(input, 100, 100,
fontFamily: 'sans-serif',
fontWeight: '600',
textAlign: 'center'
);
// apply thesame options we passed
ctx.font = '600 ' + bestFit.fontSize + 'px sans-serif';
ctx.textAlign = 'center';
// translate again because text-align: center
ctx.translate(50.5,0);
bestFit.lines.forEach((text, y) => ctx.fillText(text, 0, y));
;
txt_area.oninput();
.best-font-size-tester
border: 1px solid;
position: absolute;
overflow: visible;
opacity: 0;
z-index: -1;
pointer-events: none;
<textarea id="txt_area">This is an example text to fit the div even with line breaks</textarea>
<canvas id="canvas"></canvas>
【讨论】:
这是一些很棒的东西。我一直在寻找什么。有没有办法避免用重音和垂直居中的小词溢出字符?在您的 sn-p 中,如果您键入 Á 和单个单词,例如“Testing”,您可以看到它们不会垂直居中,并且“Á”重音将开箱即用,这将使我的脚本错误地测量x 和 y 作为结果 @MarcielFonseca 对于垂直居中,您可以将一些样式规则传递给函数的userstyles
参数,例如 display: 'flex', 'align-items': 'center'
will do it 。对于重音字符,不幸的是,不,这是我谈到的缺点之一,边界框不关心绘制的内容,它们只看字体声明的上升和下降。
我明白了。无论如何,这就是我一直在寻找的,我接受答案,谢谢以上是关于Javascript/Jquery 使文本完美地适合 div 给定尺寸和换行符(如果需要)的主要内容,如果未能解决你的问题,请参考以下文章
Typeahead 不适用于 javascript/jquery
如何使用 jquery/javascript 将文本插入 json [重复]
当您使用 javascript 或 jquery 将鼠标悬停时,如何使图像或按钮发光?