1 回答
TA贡献1810条经验 获得超4个赞
这是以下解决方案的结果
编辑2:
正如评论中提到的,当屏幕改变尺寸以及用户缩放或滚动时,固定定位会导致问题。
要创建相对定位,可以首先获取父元素的偏移量:const { offsetTop, offsetLeft } = containerEl.current;
然后将它们减去以获取 DomRect :
return Array.from(range.getClientRects()).map(
({ top, left, width, height }) => ({
top: top - offsetTop,
left: left - offsetLeft,
width,
height,
})
);
只需应用于position: relative
文本父级,然后position: absolute
应用于文本叠加即可。
编辑:
下面的解决方案不适用于换行字(例如non-violent
下图中)
生成的框占据一个矩形,覆盖单词的两个部分。
相反,使用getClientRects
获取呈现相同字符串的所有框,然后将其映射到相同的覆盖层:
状态类型:const [highlighst, setHighlights] = useState<DOMRect[] | null>(null);
在高亮设置中:return Array.from(range.getBoundingClientRect());
渲染图:
{highlights &&
highlights.map(({ top, left, width, height }) => (
<span
className='text-highlight'
style={{
top,
left,
width,
height,
}}
></span>
))}
结果 :
我最终能够使用Range API来做到这一点。
setStart
和方法setEnd
可以接受索引变量作为第二个参数。
getBoundingClientRect
然后,我获取范围本身使用的文本坐标,并将其放入我的状态中。
我现在可以将这些值应用到渲染中的固定 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>
);
}
移动 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 关注
- 74 浏览
添加回答
举报