摘要:它的原理是深度優(yōu)先遞歸遍歷這個元素以及其子元素,通過不斷試探選中區(qū)域,并與鼠標(biāo)座標(biāo)對比來定位確切位置。原理現(xiàn)在總結(jié)一下原理通過獲得鼠標(biāo)所指最接近的元素以及文本位置。驗(yàn)證鼠標(biāo)此時在單詞區(qū)域范圍中。
最近終于抽空給 Saladict 實(shí)現(xiàn)了鼠標(biāo)懸浮取詞功能,使用了較為簡潔的實(shí)現(xiàn)方式,這里分享一下原理以及坑的處理。
初嘗試這個需求其實(shí)很早就被人提 issue 了,當(dāng)時做了一番搜索,最后嘗試了 document.caretPositionFromPoint / document.caretRangeFromPoint ,效果不太理想。
如果看 mdn 給的例子,就會發(fā)現(xiàn),它是遍歷每個元素添加事件的。這么做的原因是當(dāng)使用這個方法的時候,如果鼠標(biāo)指向元素空白的地方,它會就近取位置。所以例子通過給粒度更細(xì)的元素綁定來避免這個問題。然而實(shí)際上這么做還是不足夠的,一個段落末行也許只有幾個字符,這時空出接近一行,也會有上面的問題。
所以當(dāng)時就擱置了這個功能。
靈感直到最近,看到一個同類的開源劃詞翻譯擴(kuò)展 FairyDict 實(shí)現(xiàn)了取詞功能,遍觀摩了一番源碼。
它的原理是深度優(yōu)先遞歸遍歷這個元素以及其子元素,通過不斷試探選中區(qū)域,并與鼠標(biāo)座標(biāo)對比來定位確切位置。
有沒有發(fā)現(xiàn)問題,這個遍歷過程不正是上面 document.caretPositionFromPoint 干的事么,那么我們只需要最后量一下鼠標(biāo)是否在取詞范圍中即可。
原理現(xiàn)在總結(jié)一下原理:
通過 document.caretPositionFromPoint 獲得鼠標(biāo)所指最接近的元素以及文本位置 offset。
找出 offset 最接近的單詞。
通過 Range 獲得部分文本(單詞)的尺寸和座標(biāo)。
驗(yàn)證鼠標(biāo)此時在單詞區(qū)域范圍中。
選中這個單詞。Selection 支持直接添加 Range 。
實(shí)現(xiàn)按原理來實(shí)現(xiàn)就很簡單了。本文上按 alt 可體驗(yàn)取詞效果。
/** * @param {MouseEvent} e * @returns {void} */ function selectCursorWord (e) { const x = e.clientX const y = e.clientY let offsetNode let offset const sel = window.getSelection() sel.removeAllRanges() if (document["caretPositionFromPoint"]) { const pos = document["caretPositionFromPoint"](x, y) if (!pos) { return } offsetNode = pos.offsetNode offset = pos.offset } else if (document["caretRangeFromPoint"]) { const pos = document["caretRangeFromPoint"](x, y) if (!pos) { return } offsetNode = pos.startContainer offset = pos.startOffset } else { return } if (offsetNode.nodeType === Node.TEXT_NODE) { const textNode = offsetNode const content = textNode.data const head = (content.slice(0, offset).match(/[-_a-z]+$/i) || [""])[0] const tail = (content.slice(offset).match(/^([-_a-z]+|[u4e00-u9fa5])/i) || [""])[0] if (head.length <= 0 && tail.length <= 0) { return } const range = document.createRange() range.setStart(textNode, offset - head.length) range.setEnd(textNode, offset + tail.length) const rangeRect = range.getBoundingClientRect() if (rangeRect.left <= x && rangeRect.right >= x && rangeRect.top <= y && rangeRect.bottom >= y ) { sel.addRange(range) } range.detach() } }交互
最后,如果要提供功能開關(guān)或者設(shè)置不同按鍵的話,簡單的處理可以參考 FairyDict 讓事件處理空轉(zhuǎn)。但對于 mousemove 這類比較頻繁的事件,在關(guān)閉的時候取消事件監(jiān)聽可能更好一些。在 Saladict 中甚至將“面板被釘住”跟“普通情況”分開為不同的模式,這里借助 RxJS 來處理復(fù)雜的邏輯,可參考源碼。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/95615.html
摘要:在命令面板中你可以輸入命令進(jìn)行搜索中英文都可以,然后執(zhí)行。命名面板中可以執(zhí)行各種命令,包括編輯器自帶的功能和插件提供的功能。 本文主要介紹vscode在工作中常用的快捷鍵及插件,目標(biāo)在于提高工作效率本文的快捷鍵是基于mac的,windows下的快捷鍵放在括號里 Cmd+Shift+P(win Ctrl+Shift+P) [TOC] 零、快速入門 有經(jīng)驗(yàn)的可以跳過快速入門或者大致瀏覽一...
摘要:主要采用了原生與調(diào)用結(jié)合的功能實(shí)現(xiàn)功能。所以根據(jù)這種方法,讀者可以根據(jù)自己的需求添加更多的功能,比如在編輯框里面插入一個可以點(diǎn)擊的標(biāo)簽或者添加一個代碼塊希望能讀到此文章的讀者,能在下方一起交流,更希望大佬提出錯誤,謝謝地址 因?yàn)橐粋€同學(xué),要做一個能加入圖片的留言板功能,類型與QQ空間留言板和百度貼吧發(fā)帖的那種形式,同時在網(wǎng)上找了找發(fā)生網(wǎng)上對這方面的交流很少,所以發(fā)表這篇文章拋磚引玉,希...
摘要:主要采用了原生與調(diào)用結(jié)合的功能實(shí)現(xiàn)功能。所以根據(jù)這種方法,讀者可以根據(jù)自己的需求添加更多的功能,比如在編輯框里面插入一個可以點(diǎn)擊的標(biāo)簽或者添加一個代碼塊希望能讀到此文章的讀者,能在下方一起交流,更希望大佬提出錯誤,謝謝地址 因?yàn)橐粋€同學(xué),要做一個能加入圖片的留言板功能,類型與QQ空間留言板和百度貼吧發(fā)帖的那種形式,同時在網(wǎng)上找了找發(fā)生網(wǎng)上對這方面的交流很少,所以發(fā)表這篇文章拋磚引玉,希...
閱讀 3557·2021-08-02 13:41
閱讀 2390·2019-08-30 15:56
閱讀 1520·2019-08-30 11:17
閱讀 1174·2019-08-29 15:18
閱讀 580·2019-08-29 11:10
閱讀 2671·2019-08-26 13:52
閱讀 508·2019-08-26 13:22
閱讀 2949·2019-08-23 15:41