我有一些文字,我想在特定点分割,这样你就不需要向下滚动了。
这意味着我想知道文本何时变得比窗口的可用高度更长。在显示文本之前需要知道这一点。问题在于我需要知道在渲染之前我的布局会是什么样子,因为整个事物应该响应宽度和高度。
我还打算稍微调整字体大小。所以考虑到所有这一点,你们中的任何人都知道你知道如何在正确的点分割文本吗?
提前谢谢。
PS:文本实际上是一个数组,例如:
text = [{content: Hello, wordID: ..., offsetBegin: 0, offsetEnd: 5,
...},{content: World!, wordID: ..., offsetBeding: 7, offsetEnd: 12,...}]
所以我唯一需要知道的是索引在哪里拆分文本,以便主窗口上没有滚动条。拆分文本也可以多次出现。
整个事情将开始在mounted()钩子中显示和计算,并且每次触发窗口的'resize'事件时都会重新计算。
答案 0 :(得分:4)
这个问题似乎有XY problem。您可能想重新考虑这是否真的是您想要解决问题的方式。
但是,如果你真的想在JS中获得截止点:
文本的高度取决于多种因素,例如元素的宽度,字体大小和重量,字距,等等。需要考虑很多变量,如果不进行任何渲染,就不可能进行计算。
相反,您应该要求浏览器呈现您的元素,然后在向用户显示呈现之前将其删除。一旦您插入文本,测量文本应该结束的位置,然后在删除文本后强制重新进行重排,这将由forcing a reflow完成。
棘手的部分是测量文本应该结束的位置。我个人会通过在每个我想要截止的位置插入元素来解决这个问题(例如在每个单词之后),然后循环遍历它们以查看哪个溢出容器。
以下是该想法的香草JS实现。您应该能够在Vue中轻松实现它。
const DEBUG = true;
const $textInp = document.getElementById("text-inp");
const $target = document.getElementById("target");
const $outp = document.getElementById("outp");
document.getElementById("calc-btn").addEventListener("click", () => {
const text = $textInp.value;
const data = getTextCutoff(text, $target);
$outp.textContent = JSON.stringify(data);
if (!DEBUG) { $target.textContent = text.substr(0, data.end); }
});
/**
* Returns an object of format { end: <Number> }
* Where `end` is the last index which can be displayed inside $elm without overflow
*/
function getTextCutoff(text, $elm) {
$elm.innerHTML = ""; // empty the element
const prevOverflow = $elm.style.overflow;
const prevPosition = $elm.style.position;
$elm.style.overflow = "visible";
$elm.style.position = "relative"; // to make sure offsetHeight gives relative to parent
const $indicators = [];
const $nodes = [];
// turn our text into an array of text nodes, with an indicator node after each
let currentIndex = 0;
const words = text.split(" ");
words.forEach(
(word, i) => {
if (i > 0) {
word = " "+word;
}
currentIndex += word.length;
const $wordNode = document.createTextNode(word);
$nodes.push($wordNode);
const $indicatorNode = document.createElement("span");
$indicatorNode.strIndex = currentIndex;
if (DEBUG) { $indicatorNode.classList.add("text-cutoff-indicator"); }
$indicators.push($indicatorNode);
$nodes.push($indicatorNode);
}
);
// insert our elements
$nodes.forEach($node => $elm.appendChild($node));
// then find the first indicator that is overflown
const maxHeight = $elm.offsetHeight;
let $lastIndicator = $indicators[$indicators.length - 1];
for (let i = 0; i < $indicators.length; ++i) {
const $indicator = $indicators[i];
const bottomPos = $indicator.offsetTop + $indicator.offsetHeight;
if (bottomPos > maxHeight) {
break;
} else { $lastIndicator = $indicator; }
}
if (DEBUG) {
$lastIndicator.style.borderColor = "green";
$lastIndicator.classList.add("overflown");
}
// then reset everything - this also forces reflow
if (!DEBUG) { $elm.innerHTML = ""; }
$elm.style.overflow = prevOverflow;
$elm.style.position = prevPosition;
// then return
return {
end: $lastIndicator.strIndex
};
}
&#13;
#target {
height: 128px;
background: #ddd;
}
.text-cutoff-indicator {
margin-left: -2px;
border-left: 2px solid red;
}
.text-cutoff-indicator.overflown ~ .text-cutoff-indicator {
opacity: 0.5;
}
&#13;
<div>
<textarea id="text-inp" placeholder="Enter text here"></textarea>
<button id="calc-btn">Apply text</button>
<pre id="outp"></pre>
</div>
<div id="target"></div>
&#13;