2025-01-15 16:35:20 +08:00
|
|
|
export const cursors = []
|
|
|
|
|
|
|
|
export class Cursor {
|
|
|
|
constructor(targetNode = null, insertIndex = null) {
|
|
|
|
this.targetNode = targetNode // 初始时可以直接指定目标文本节点
|
|
|
|
this.insertIndex = insertIndex // 初始时可以直接指定插入位置索引
|
|
|
|
this.range = document.createRange()
|
|
|
|
this.cursorElement = this.createCursorElement()
|
|
|
|
document.body.appendChild(this.cursorElement)
|
|
|
|
}
|
|
|
|
|
|
|
|
// 创建光标元素
|
|
|
|
createCursorElement() {
|
|
|
|
const cursorElement = document.createElement("div")
|
|
|
|
Object.assign(cursorElement.style, {
|
|
|
|
position: "absolute",
|
|
|
|
width: "2px",
|
|
|
|
height: "1em",
|
|
|
|
backgroundColor: "black",
|
|
|
|
zIndex: "9999",
|
|
|
|
pointerEvents: "none",
|
|
|
|
display: "none", // 初始隐藏
|
|
|
|
transition: "all 0.1s ease"
|
|
|
|
})
|
|
|
|
return cursorElement
|
|
|
|
}
|
|
|
|
|
|
|
|
// 设置目标文本节点和插入位置
|
|
|
|
setTarget(node, index) {
|
|
|
|
this.targetNode = node
|
|
|
|
this.insertIndex = index
|
|
|
|
this.updateRange()
|
|
|
|
}
|
|
|
|
|
|
|
|
// 更新范围
|
|
|
|
updateRange() {
|
|
|
|
if (this.targetNode && this.insertIndex !== null) {
|
|
|
|
this.range.setStart(this.targetNode, this.insertIndex)
|
|
|
|
this.range.setEnd(this.targetNode, this.insertIndex)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 插入文本
|
|
|
|
insertText(text) {
|
|
|
|
const insertionNode = document.createTextNode(text)
|
|
|
|
this.updateRange()
|
|
|
|
this.range.insertNode(insertionNode)
|
|
|
|
// 更新插入位置
|
|
|
|
this.insertIndex += text.length
|
|
|
|
this.updateRange() // 更新光标范围
|
|
|
|
return insertionNode
|
|
|
|
}
|
|
|
|
|
|
|
|
// 获取当前光标的矩形位置
|
|
|
|
getBoundingClientRect() {
|
2025-01-16 06:05:50 +08:00
|
|
|
this.range.setStart(this.targetNode, this.insertIndex)
|
|
|
|
this.range.setEnd(this.targetNode, this.insertIndex)
|
2025-01-15 16:35:20 +08:00
|
|
|
return this.range.getBoundingClientRect()
|
|
|
|
}
|
|
|
|
|
|
|
|
// 更新光标位置
|
2025-01-16 06:05:50 +08:00
|
|
|
updatePosition({ left, top }) {
|
|
|
|
this.cursorElement.style.left = `${left}px`
|
|
|
|
this.cursorElement.style.top = `${top}px`
|
2025-01-15 16:35:20 +08:00
|
|
|
this.cursorElement.style.display = "block"
|
|
|
|
}
|
|
|
|
|
|
|
|
// 移除光标元素
|
|
|
|
remove() {
|
|
|
|
this.cursorElement.remove()
|
|
|
|
}
|
|
|
|
|
2025-01-16 06:05:50 +08:00
|
|
|
oninput({ value }) {
|
|
|
|
const text = this.targetNode.textContent
|
|
|
|
const left = text.slice(0, this.insertIndex)
|
|
|
|
const right = text.slice(this.insertIndex)
|
|
|
|
this.insertIndex += value.length
|
|
|
|
this.targetNode.textContent = left + value + right
|
|
|
|
this.updatePosition(this.getBoundingClientRect())
|
|
|
|
}
|
2025-01-15 16:35:20 +08:00
|
|
|
|
2025-01-16 06:05:50 +08:00
|
|
|
onkeydown({ key }) {
|
|
|
|
if (key === "ArrowUp" && this.targetNode.parentNode.previousElementSibling) {
|
|
|
|
// 先取兄弟元素的最后一个子元素, 没有则取兄弟元素, 没有则向上回溯
|
|
|
|
this.targetNode = Array.from(this.targetNode.parentNode.previousElementSibling.childNodes).find(node => node.nodeType === Node.TEXT_NODE)
|
|
|
|
this.insertIndex = Math.max(0, Math.min(this.targetNode.textContent.length, this.insertIndex))
|
|
|
|
this.updatePosition(this.getBoundingClientRect())
|
2025-01-15 16:35:20 +08:00
|
|
|
}
|
2025-01-16 06:05:50 +08:00
|
|
|
if (key === "ArrowDown" && this.targetNode.parentNode.nextElementSibling) {
|
|
|
|
// 先取当前子元素, 没有则取下一个兄弟元素, 没有则向上回溯
|
|
|
|
this.targetNode = Array.from(this.targetNode.parentNode.nextElementSibling.childNodes).find(node => node.nodeType === Node.TEXT_NODE)
|
|
|
|
this.insertIndex = Math.max(0, Math.min(this.targetNode.textContent.length, this.insertIndex))
|
|
|
|
this.updatePosition(this.getBoundingClientRect())
|
2025-01-15 16:35:20 +08:00
|
|
|
}
|
2025-01-16 06:05:50 +08:00
|
|
|
if (key === "ArrowLeft") {
|
2025-01-15 16:35:20 +08:00
|
|
|
this.insertIndex = Math.max(0, Math.min(this.targetNode.textContent.length, this.insertIndex - 1))
|
2025-01-16 06:05:50 +08:00
|
|
|
return this.updatePosition(this.getBoundingClientRect())
|
2025-01-15 16:35:20 +08:00
|
|
|
}
|
2025-01-16 06:05:50 +08:00
|
|
|
if (key === "ArrowRight") {
|
2025-01-15 16:35:20 +08:00
|
|
|
this.insertIndex = Math.max(0, Math.min(this.targetNode.textContent.length, this.insertIndex + 1))
|
2025-01-16 06:05:50 +08:00
|
|
|
return this.updatePosition(this.getBoundingClientRect())
|
2025-01-15 16:35:20 +08:00
|
|
|
}
|
2025-01-16 06:21:14 +08:00
|
|
|
if (key === "Backspace") {
|
|
|
|
const text = this.targetNode.textContent
|
|
|
|
const left = text.slice(0, Math.max(0, this.insertIndex - 1))
|
|
|
|
const right = text.slice(this.insertIndex)
|
|
|
|
this.targetNode.textContent = left + right
|
|
|
|
this.insertIndex = Math.max(0, this.insertIndex - 1)
|
|
|
|
return this.updatePosition(this.getBoundingClientRect())
|
|
|
|
}
|
2025-01-15 16:35:20 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export default { cursors, Cursor }
|