光标移动
This commit is contained in:
		
							
								
								
									
										121
									
								
								cursor.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								cursor.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,121 @@
 | 
				
			|||||||
 | 
					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() {
 | 
				
			||||||
 | 
					        this.updateRange()
 | 
				
			||||||
 | 
					        return this.range.getBoundingClientRect()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 更新光标位置
 | 
				
			||||||
 | 
					    updatePosition(rect) {
 | 
				
			||||||
 | 
					        if (!rect) {
 | 
				
			||||||
 | 
					            this.hide()
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        this.cursorElement.style.left = `${rect.left}px`
 | 
				
			||||||
 | 
					        this.cursorElement.style.top = `${rect.top}px`
 | 
				
			||||||
 | 
					        this.cursorElement.style.display = "block"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 隐藏光标
 | 
				
			||||||
 | 
					    hide() {
 | 
				
			||||||
 | 
					        this.cursorElement.style.display = "none"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 移除光标元素
 | 
				
			||||||
 | 
					    remove() {
 | 
				
			||||||
 | 
					        this.cursorElement.remove()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    move(key) {
 | 
				
			||||||
 | 
					        // 获取目标节点的上下兄弟节点(li元素)
 | 
				
			||||||
 | 
					        const children = Array.from(this.targetNode.parentNode.parentNode.children)
 | 
				
			||||||
 | 
					        const siblings = children.filter(node => {
 | 
				
			||||||
 | 
					            return node !== this.targetNode && node.nodeType === Node.ELEMENT_NODE
 | 
				
			||||||
 | 
					        }).map(item => {
 | 
				
			||||||
 | 
					            return Array.from(item.childNodes).find(node => node.nodeType === Node.TEXT_NODE)
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        const currentIndex = siblings.indexOf(this.targetNode)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (key === "ArrowUp" && currentIndex > 0) {
 | 
				
			||||||
 | 
					            const prevSibling = siblings[currentIndex - 1]
 | 
				
			||||||
 | 
					            const length = prevSibling.textContent.length
 | 
				
			||||||
 | 
					            const index = length < this.insertIndex ? length : this.insertIndex
 | 
				
			||||||
 | 
					            this.setTarget(prevSibling, index)  // 光标在上一个兄弟元素的起始位置
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (key === "ArrowDown" && currentIndex >= 0 && currentIndex < siblings.length - 1) {
 | 
				
			||||||
 | 
					            const nextSibling = siblings[currentIndex + 1]
 | 
				
			||||||
 | 
					            const length = nextSibling.textContent.length
 | 
				
			||||||
 | 
					            const index = length < this.insertIndex ? length : this.insertIndex
 | 
				
			||||||
 | 
					            this.setTarget(nextSibling, index)  // 光标在下一个兄弟元素的起始位置
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (key === "ArrowLeft" && this.insertIndex > 0) {
 | 
				
			||||||
 | 
					            this.insertIndex = Math.max(0, Math.min(this.targetNode.textContent.length, this.insertIndex - 1))
 | 
				
			||||||
 | 
					            this.updateRange()
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (key === "ArrowRight" && this.insertIndex < this.targetNode.textContent.length) {
 | 
				
			||||||
 | 
					            this.insertIndex = Math.max(0, Math.min(this.targetNode.textContent.length, this.insertIndex + 1))
 | 
				
			||||||
 | 
					            this.updateRange()
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default { cursors, Cursor }
 | 
				
			||||||
							
								
								
									
										156
									
								
								markdown.js
									
									
									
									
									
								
							
							
						
						
									
										156
									
								
								markdown.js
									
									
									
									
									
								
							@@ -1,4 +1,5 @@
 | 
				
			|||||||
import { Marked } from "marked"
 | 
					import { Marked } from "marked"
 | 
				
			||||||
 | 
					import { cursors, Cursor } from "./cursor"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 示例 Markdown 文本
 | 
					// 示例 Markdown 文本
 | 
				
			||||||
const markdown = `
 | 
					const markdown = `
 | 
				
			||||||
@@ -17,125 +18,6 @@ const markdown = `
 | 
				
			|||||||
const marked = new Marked()
 | 
					const marked = new Marked()
 | 
				
			||||||
const tokens = marked.lexer(markdown)
 | 
					const tokens = marked.lexer(markdown)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 光标
 | 
					 | 
				
			||||||
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", // 初始隐藏
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
        return cursorElement
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // 设置目标文本节点和插入位置
 | 
					 | 
				
			||||||
    setTarget(node, index) {
 | 
					 | 
				
			||||||
        this.targetNode = node
 | 
					 | 
				
			||||||
        this.insertIndex = index
 | 
					 | 
				
			||||||
        this.updateRange()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // 更新插入位置索引
 | 
					 | 
				
			||||||
    updateInsertIndex(offset) {
 | 
					 | 
				
			||||||
        this.insertIndex = Math.max(0, Math.min(this.targetNode.textContent.length, this.insertIndex + offset))
 | 
					 | 
				
			||||||
        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() {
 | 
					 | 
				
			||||||
        this.updateRange()
 | 
					 | 
				
			||||||
        return this.range.getBoundingClientRect()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // 更新光标位置
 | 
					 | 
				
			||||||
    updatePosition(rect) {
 | 
					 | 
				
			||||||
        if (!rect) {
 | 
					 | 
				
			||||||
            this.hide()
 | 
					 | 
				
			||||||
            return
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        this.cursorElement.style.left = `${rect.left}px`
 | 
					 | 
				
			||||||
        this.cursorElement.style.top = `${rect.top}px`
 | 
					 | 
				
			||||||
        this.cursorElement.style.display = "block"
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // 隐藏光标
 | 
					 | 
				
			||||||
    hide() {
 | 
					 | 
				
			||||||
        this.cursorElement.style.display = "none"
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // 移除光标元素
 | 
					 | 
				
			||||||
    remove() {
 | 
					 | 
				
			||||||
        this.cursorElement.remove()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // 获取目标节点的上下兄弟节点
 | 
					 | 
				
			||||||
    getSiblingNodes() {
 | 
					 | 
				
			||||||
        const parent = this.targetNode.parentNode
 | 
					 | 
				
			||||||
        const siblings = Array.from(parent.childNodes).filter(
 | 
					 | 
				
			||||||
            (node) => node !== this.targetNode && node.nodeType === Node.TEXT_NODE
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        return siblings
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // 根据当前光标位置移动到上下兄弟节点
 | 
					 | 
				
			||||||
    moveUp() {
 | 
					 | 
				
			||||||
        console.log('根据当前光标位置移动到上下兄弟节点')
 | 
					 | 
				
			||||||
        const siblings = this.getSiblingNodes()
 | 
					 | 
				
			||||||
        const currentIndex = siblings.indexOf(this.targetNode)
 | 
					 | 
				
			||||||
        if (currentIndex > 0) {
 | 
					 | 
				
			||||||
            const prevSibling = siblings[currentIndex - 1]
 | 
					 | 
				
			||||||
            this.setTarget(prevSibling, prevSibling.textContent.length)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    moveDown() {
 | 
					 | 
				
			||||||
        const siblings = this.getSiblingNodes()
 | 
					 | 
				
			||||||
        const currentIndex = siblings.indexOf(this.targetNode)
 | 
					 | 
				
			||||||
        if (currentIndex < siblings.length - 1) {
 | 
					 | 
				
			||||||
            const nextSibling = siblings[currentIndex + 1]
 | 
					 | 
				
			||||||
            this.setTarget(nextSibling, 0)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// 光标集合,存储所有光标
 | 
					 | 
				
			||||||
const cursors = []
 | 
					 | 
				
			||||||
let defaultCursor = new Cursor() // 初始创建默认光标
 | 
					 | 
				
			||||||
cursors.push(defaultCursor) // 将默认光标添加到光标集合
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// 创建 textarea 接受输入
 | 
					// 创建 textarea 接受输入
 | 
				
			||||||
const textarea = document.createElement("textarea")
 | 
					const textarea = document.createElement("textarea")
 | 
				
			||||||
Object.assign(textarea.style, {
 | 
					Object.assign(textarea.style, {
 | 
				
			||||||
@@ -174,26 +56,10 @@ textarea.oninput = () => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// 处理方向键移动插入点
 | 
					// 处理方向键移动插入点
 | 
				
			||||||
textarea.onkeydown = (event) => {
 | 
					textarea.onkeydown = (event) => {
 | 
				
			||||||
    console.log(event.key)
 | 
					    cursors.filter(cursor => cursor.targetNode && cursor.insertIndex !== null).forEach(cursor => {
 | 
				
			||||||
 | 
					        cursor.move(event.key)
 | 
				
			||||||
    if (!cursors.length) return
 | 
					        updateCursors()
 | 
				
			||||||
 | 
					 | 
				
			||||||
    cursors.forEach(cursor => {
 | 
					 | 
				
			||||||
        if (!cursor.targetNode || cursor.insertIndex === null) return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (event.key === "ArrowLeft") {
 | 
					 | 
				
			||||||
            cursor.updateInsertIndex(-1)
 | 
					 | 
				
			||||||
        } else if (event.key === "ArrowRight") {
 | 
					 | 
				
			||||||
            cursor.updateInsertIndex(1)
 | 
					 | 
				
			||||||
        } else if (event.key === "ArrowUp") {
 | 
					 | 
				
			||||||
            cursor.moveUp()
 | 
					 | 
				
			||||||
        } else if (event.key === "ArrowDown") {
 | 
					 | 
				
			||||||
            cursor.moveDown()
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
 | 
					 | 
				
			||||||
    // 更新光标位置
 | 
					 | 
				
			||||||
    updateCursors()
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 更新所有光标的位置
 | 
					// 更新所有光标的位置
 | 
				
			||||||
@@ -260,19 +126,19 @@ element.onclick = (event) => {
 | 
				
			|||||||
    // 设置光标的目标节点和插入位置
 | 
					    // 设置光标的目标节点和插入位置
 | 
				
			||||||
    cursors.forEach(cursor => cursor.setTarget(targetNode, insertIndex))
 | 
					    cursors.forEach(cursor => cursor.setTarget(targetNode, insertIndex))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 如果是点击多个光标的情况,增加新光标
 | 
				
			||||||
 | 
					    if (event.ctrlKey || !cursors.length) {
 | 
				
			||||||
 | 
					        const newCursor = new Cursor(targetNode, insertIndex)
 | 
				
			||||||
 | 
					        cursors.push(newCursor)
 | 
				
			||||||
 | 
					        console.log("新增光标")
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // 更新光标位置
 | 
					    // 更新光标位置
 | 
				
			||||||
    updateCursors()
 | 
					    updateCursors()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // 聚焦输入框
 | 
					    // 聚焦输入框
 | 
				
			||||||
    textarea.value = ""
 | 
					    textarea.value = ""
 | 
				
			||||||
    textarea.focus()
 | 
					    textarea.focus()
 | 
				
			||||||
 | 
					 | 
				
			||||||
    // 如果是点击多个光标的情况,增加新光标
 | 
					 | 
				
			||||||
    if (event.ctrlKey) {
 | 
					 | 
				
			||||||
        const newCursor = new Cursor(targetNode, insertIndex)
 | 
					 | 
				
			||||||
        cursors.push(newCursor)
 | 
					 | 
				
			||||||
        console.log("新增光标")
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 按删除键移除光标
 | 
					// 按删除键移除光标
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user