cooperation/cursor.js

115 lines
4.4 KiB
JavaScript
Raw Normal View History

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 }