亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

如何在只知道它的索引的情況下獲得段落內單詞的邊界矩形?

如何在只知道它的索引的情況下獲得段落內單詞的邊界矩形?

繁星淼淼 2024-01-11 14:16:34
我正在一個 React 應用程序中創建一個文本轉語音功能,該功能可以通過在其后面放置背景來突出顯示當前所說的單詞。該功能與Firefox 閱讀器視圖非常相似。我實現的解決方案只是剪切段落字符串,并在每次渲染時在口語單詞周圍放置一個跨度,這使得資源占用很大并且無法制作動畫。這是代碼:(我打算廢棄)export interface SpeakEvent {    start: number;    end: number;    type: string;}export default function TextNode({ content }: TextNodeProps) {    const [highlight, setHighlight] = useState<SpeakEvent | null>(null);    useEffect(() => {        registerText((ev) => {            if (ev?.type === 'word' || !ev)                setHighlight((old) => {                    /* Irrelevant code */                    return ev;                });        }, content);    }, [content]);    const { start, end } = highlight ?? {};    let segments = [content];    if (highlight) {        segments = [            segments[0].slice(0, start),            segments[0].slice(start, end),            segments[0].slice(end),        ];    }    return (        <>            {segments.map((seg, i) =>                i === 1 ? (                    <span key={i} className={'highlight'}>                        {seg}                    </span>                ) : (                    seg                )            )}        </>    );}Firefox 閱讀器使用更智能的方式來做到這一點。它使用放置在口語單詞后面的 div,然后將其四處移動:包含高亮效果的div直接使用絕對坐標放置。他們如何在只知道字符串索引的情況下訪問段落內單詞的邊界矩形?
查看完整描述

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下圖中)

https://img1.sycdn.imooc.com/659f882d0001a30306230148.jpg

生成的框占據一個矩形,覆蓋單詞的兩個部分。

相反,使用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>

? ? ))}

結果 :

https://img1.sycdn.imooc.com/659f88430001e4b606250174.jpg

我最終能夠使用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;

}


查看完整回答
反對 回復 2024-01-11
  • 1 回答
  • 0 關注
  • 122 瀏覽

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號