Я рисую текстовые метки в SVG. У меня есть фиксированная ширина (скажем, 200 пикселей). Когда текст слишком длинный, как его можно обрезать?
Идеальное решение также добавит многоточие (...), где текст будет разрезан. Но я могу жить и без него.
Я рисую текстовые метки в SVG. У меня есть фиксированная ширина (скажем, 200 пикселей). Когда текст слишком длинный, как его можно обрезать?
Идеальное решение также добавит многоточие (...), где текст будет разрезан. Но я могу жить и без него.
Один из способов сделать это - использовать элемент textPath, поскольку все символы, которые падают с пути, будут автоматически удалены. См. Примеры текстового пути из SVG testsuite.
Другой способ - использовать CSS3 text-overflow для текстовых элементов svg, например здесь. Opera 11 поддерживает это, но вы, скорее всего, обнаружите, что другие браузеры поддерживают его только на элементах html в это время.
Вы также можете измерить текстовые строки и вставить эллипсис самостоятельно с помощью script, я бы предложил использовать метод getSubStringLength в текстовом элементе, увеличив параметр nchars, пока не найдете подходящую длину.
Использование библиотеки d3
функция обертки для переполнения текста:
function wrap() {
var self = d3.select(this),
textLength = self.node().getComputedTextLength(),
text = self.text();
while (textLength > (width - 2 * padding) && text.length > 0) {
text = text.slice(0, -1);
self.text(text + '...');
textLength = self.node().getComputedTextLength();
}
}
использование:
text.append('tspan').text(function(d) { return d.name; }).each(wrap);
Реализация третьего предложения Эрика. Я придумал что-то вроде этого:
//places textString in textObj, adds an ellipsis if text can't fit in width
function placeTextWithEllipsis(textObj,textString,width){
textObj.textContent=textString;
//ellipsis is needed
if (textObj.getSubStringLength(0,textString.length)>=width){
for (var x=textString.length-3;x>0;x-=3){
if (textObj.getSubStringLength(0,x)<=width){
textObj.textContent=textString.substring(0,x)+"...";
return;
}
}
textObj.textContent="..."; //can't place at all
}
}
Кажется, делает трюк:)
@user2846569 покажите мне, как это сделать (да, используя d3.js). Но я должен внести некоторые небольшие изменения в работу:
function wrap( d ) {
var self = d3.select(this),
textLength = self.node().getComputedTextLength(),
text = self.text();
while ( ( textLength > self.attr('width') )&& text.length > 0) {
text = text.slice(0, -1);
self.text(text + '...');
textLength = self.node().getComputedTextLength();
}
}
svg.append('text')
.append('tspan')
.text(function(d) { return d; })
.attr('width', 200 )
.each( wrap );
Попробуйте это, я использую эту функцию в своей библиотеке диаграмм:
function textEllipsis(el, text, width) {
if (typeof el.getSubStringLength !== "undefined") {
el.textContent = text;
var len = text.length;
while (el.getSubStringLength(0, len--) > width) {}
el.textContent = text.slice(0, len) + "...";
} else if (typeof el.getComputedTextLength !== "undefined") {
while (el.getComputedTextLength() > width) {
text = text.slice(0,-1);
el.textContent = text + "...";
}
} else {
// the last fallback
while (el.getBBox().width > width) {
text = text.slice(0,-1);
// we need to update the textContent to update the boundary width
el.textContent = text + "...";
}
}
}
Мой подход был похож на OpherV, но я попытался сделать это с помощью JQuery
function getWidthOfText(text, fontSize, fontFamily) {
var span = $('<span></span>');
span.css({
'font-family': fontFamily,
'font-size' : fontSize
}).text(text);
$('body').append(span);
var w = span.width();
span.remove();
return w;
}
function getStringForSize(text, desiredSize, fontSize, fontFamily) {
var curSize = getWidthOfText(text, fontSize, fontFamily);
if(curSize > size)
{
var curText = text.substring(0,text.length-5) + '...';
return getStringForSize(curText, size, fontSize, fontFamily);
}
else
{
return text;
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Элемент linearGradient может использоваться для получения чистого решения SVG. Этот пример затеняет усеченный текст (без многоточия):
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<defs>
<linearGradient gradientUnits="userSpaceOnUse" x1="0" x2="200" y1="0" y2="0" id="truncateText">
<stop offset="90%" stop-opacity="1" />
<stop offset="100%" stop-opacity="0" />
</linearGradient>
<linearGradient id="truncateLegendText0" gradientTransform="translate(0)" xlink:href="#truncateText" />
<linearGradient id="truncateLegendText1" gradientTransform="translate(200)" xlink:href="#truncateText" />
</defs>
<text fill="url(#truncateLegendText0)" font-size="50" x="0" y="50">0123456789</text>
<text fill="url(#truncateLegendText1)" font-size="50" x="200" y="150">0123456789</text>
</svg>
Есть несколько вариантов, использующих d3 и циклы для поиска меньшего текста, которые подходят. Это может быть достигнуто без петель, и это работает быстрее. textNode - узел d3.
clipText(textNode, maxWidth, postfix) {
const textWidth = textNode.getComputedTextLength();
if (textWidth > maxWidth) {
let text = textNode.textContent;
const newLength = Math.round(text.length * (1 - (textWidth - maxWidth) / textWidth));
text = text.substring(0, newLength);
textNode.textContent = text.trim() + postfix;
}
}