1 回答

TA貢獻(xiàn)1810條經(jīng)驗(yàn) 獲得超4個(gè)贊
這是以下解決方案的結(jié)果
編輯2:
正如評(píng)論中提到的,當(dāng)屏幕改變尺寸以及用戶縮放或滾動(dòng)時(shí),固定定位會(huì)導(dǎo)致問題。
要?jiǎng)?chuàng)建相對(duì)定位,可以首先獲取父元素的偏移量:const { offsetTop, offsetLeft } = containerEl.current;
然后將它們減去以獲取 DomRect :
return Array.from(range.getClientRects()).map(
? ? ({ top, left, width, height }) => ({
? ? ? ? top: top - offsetTop,
? ? ? ? left: left - offsetLeft,
? ? ? ? width,
? ? ? ? height,
? ? })
);
只需應(yīng)用于position: relative
文本父級(jí),然后position: absolute
應(yīng)用于文本疊加即可。
編輯:
下面的解決方案不適用于換行字(例如non-violent
下圖中)
生成的框占據(jù)一個(gè)矩形,覆蓋單詞的兩個(gè)部分。
相反,使用getClientRects
獲取呈現(xiàn)相同字符串的所有框,然后將其映射到相同的覆蓋層:
狀態(tài)類型:const [highlighst, setHighlights] = useState<DOMRect[] | null>(null);
在高亮設(shè)置中:return Array.from(range.getBoundingClientRect());
渲染圖:
{highlights &&
? ? highlights.map(({ top, left, width, height }) => (
? ? ? ? <span
? ? ? ? ? ? className='text-highlight'
? ? ? ? ? ? style={{
? ? ? ? ? ? ? ? top,
? ? ? ? ? ? ? ? left,
? ? ? ? ? ? ? ? width,
? ? ? ? ? ? ? ? height,
? ? ? ? ? ? }}
? ? ? ? ></span>
? ? ))}
結(jié)果 :
我最終能夠使用Range API來做到這一點(diǎn)。
setStart
和方法setEnd
可以接受索引變量作為第二個(gè)參數(shù)。
getBoundingClientRect
然后,我獲取范圍本身使用的文本坐標(biāo),并將其放入我的狀態(tài)中。
我現(xiàn)在可以將這些值應(yīng)用到渲染中的固定 div 上:
const range = document.createRange();
export default function TextNode({ content, footnote }: TextNodeProps) {
? ? const [highlight, setHighlight] = useState<DOMRect | null>(null);
? ? const containerEl = useRef<HTMLSpanElement>(null);
? ? useEffect(() => {
? ? ? ? registerText((ev) => {
? ? ? ? ? ? if (!ev) {
? ? ? ? ? ? ? ? setHighlight(null);
? ? ? ? ? ? ? ? return;
? ? ? ? ? ? }
? ? ? ? ? ? if (ev.type === 'sentence') {
? ? ? ? ? ? ? ? (textEl.current as HTMLSpanElement | null)?.scrollIntoView(
? ? ? ? ? ? ? ? ? ? scrollOptions
? ? ? ? ? ? ? ? );
? ? ? ? ? ? }
? ? ? ? ? ? if (ev.type === 'word')
? ? ? ? ? ? ? ? setHighlight((old) => {
? ? ? ? ? ? ? ? ? ? const txtNode = containerEl.current?.firstChild as Node;
? ? ? ? ? ? ? ? ? ? range.setStart(txtNode, ev.start);
? ? ? ? ? ? ? ? ? ? range.setEnd(txtNode, ev.end);
? ? ? ? ? ? ? ? ? ? if (!old) {
? ? ? ? ? ? ? ? ? ? ? ? (containerEl.current as HTMLSpanElement | null)?.scrollIntoView(
? ? ? ? ? ? ? ? ? ? ? ? ? ? scrollOptions
? ? ? ? ? ? ? ? ? ? ? ? );
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? return range.getBoundingClientRect();
? ? ? ? ? ? ? ? });
? ? ? ? }, content);
? ? }, [content]);
? ? return (
? ? ? ? <span ref={containerEl}>
? ? ? ? ? ? {content}
? ? ? ? ? ? {highlight && (
? ? ? ? ? ? ? ? <div
? ? ? ? ? ? ? ? ? ? className='text-highlight'
? ? ? ? ? ? ? ? ? ? style={{
? ? ? ? ? ? ? ? ? ? ? ? top: highlight.top,
? ? ? ? ? ? ? ? ? ? ? ? left: highlight.left,
? ? ? ? ? ? ? ? ? ? ? ? width: highlight.width,
? ? ? ? ? ? ? ? ? ? ? ? height: highlight.height,
? ? ? ? ? ? ? ? ? ? }}
? ? ? ? ? ? ? ? ></div>
? ? ? ? ? ? )}
? ? ? ? </span>
? ? );
}
移動(dòng) div 的 CSS :
.text-highlight {
? ? position: fixed;
? ? border-bottom: 4px solid blue;
? ? opacity: 0.7;
? ? transition-property: top, left, height, width;
? ? transition-duration: 0.2s;
? ? transform-style: ease-in-out;
}
- 1 回答
- 0 關(guān)注
- 134 瀏覽
添加回答
舉報(bào)