WishMeLz

生活其实很有趣

划词弹框

鼠标选中文本弹出悬浮框,计算选中文字的位置。
@mouseup.stop="handleMouseUp"

<div class="cont" id="textareaCont">
    <textarea @select.stop="onSelect" @keydown="onKeyDown" ref="origintext" v-model="origin"></textarea>
</div>

<div class="crossword" ref="crossword" v-if="crossword" :style="crosswordStyle"></div>
mounted() {
    document.addEventListener("mousedown", this.handleOutsideClick)
}
beforeUnmount(){
    document.removeEventListener("mousedown", this.handleOutsideClick)
}

选中事件

handleMouseUp(event) {
    // 设置划词区域
    if (!this.$refs.resultmain.contains(event.target)) {
        return
    }
    const selection = window.getSelection()
    const selectedText = selection.toString().trim()
    if (!selectedText) {
        return
    }
    const {
        startContainer, // 起始节点
        startOffset, // 起始节点偏移量
        endContainer, // 终止节点
        endOffset, // 终止节点偏移量
    } = document.getSelection().getRangeAt(0)

    // 创建一个 range 对象
    const range = document.createRange()
    // 设置需要获取位置信息的文本节点以及偏移量
    range.setStart(startContainer, startOffset)
    range.setEnd(startContainer, startContainer.textContent.length)
    // 通过 getBoundingClientRect 获取位置信息
    const rect = range.getBoundingClientRect()
    if (selectedText) {
        // 设置弹框样式 并且设置偏移量
        this.crosswordStyle = {
            top: rect.top + 40 - 130 + "px",
            left: rect.left + 40 + "px",
        }
    }
},

输入框选中事件

onSelect(){
 // 获取 textarea DOM 元素
        const textarea = this.$refs.origintext
        const selection = window.getSelection()
        // 获取选中文本的起始位置和结束位置
        const selectionStart = textarea.selectionStart
        const selectionEnd = textarea.selectionEnd

        const selectedText = selection.toString().trim()
        if (!selectedText) {
            return
        }
        // 获取选中文本的绝对定位
        const absolutePosition = this.getAbsolutePosition(
            textarea,
            selectionStart,
            selectionEnd
        )

        this.crosswordStyle = {
            top: absolutePosition.top + 40 - 130 + "px",
            left: absolutePosition.left + 40 + "px",
        }
}

getAbsolutePosition(textarea, start, end){
    // 创建一个临时的 div 元素,用于计算选中文本的位置
    const div = document.createElement("div")
    // document.body.appendChild(div)

    // 设置 div 的样式,使其与 textarea 相似
    const style = getComputedStyle(textarea)

    div.style.cssText = style.cssText
    div.style.position = "absolute"
    // div.style.visibility = 'hidden'
    div.style.whiteSpace = "pre-wrap"
    div.style.wordWrap = "break-word"
    div.style.width = textarea.clientWidth + "px"
    div.style.top = 0
    div.style.background = "transparent"
    div.style.zIndex = 2
    div.style.color = "transparent"
    // 将 textarea 的文本内容添加到 div 中,并插入一个 span 元素表示选中文本
    const text =
        textarea.value.substring(0, start) +
        "<span >" +
        textarea.value.substring(start, end) +
        "</span>" +
        textarea.value.substring(end)
    div.innerHTML = text.replace(/\n/g, "<br>")

    let parent = document.querySelector("#textareaCont")
    parent.insertBefore(div, parent.firstChild)
    // 计算选中文本的位置
    const span = div.querySelector("span")
    const rect = span.getBoundingClientRect()
    // 移除 div 元素
    document.querySelector("#textareaCont").removeChild(div)

    // 返回选中文本的绝对定位
    return {
        top: rect.top,
        left: rect.left,
        width: rect.width,
        height: rect.height,
    }
}

关闭弹框

handleOutsideClick(event){
    // 如果点击弹框不关闭弹框
    if (event.target.className == "crossword") {
        return
    }
    if (this.$refs.crossword && this.$refs.crossword.contains(event.target)) {
        return
    }
    this.crossword = false
    const selection = window.getSelection()
    // 手动取消选中
    selection.removeAllRanges()
}