Vue组合式API中创建自定义HTML的DynamicMarker及添加远程用户标记
Ace编辑器远程协作标记实现方案
针对你基于Vue组合式API和Ace编辑器的场景,以下是两个问题的具体实现步骤:
1. 创建带自定义HTML的DynamicMarker
Ace的DynamicMarker需要继承自ace.Marker,通过重写render方法注入自定义HTML结构。在Vue组合式API中,可直接在<script setup>里定义这个自定义标记类:
// 自定义动态标记类,支持自定义HTML class CustomRemoteMarker extends ace.Marker { constructor(range, htmlContent, cssClass) { super(range); this.htmlContent = htmlContent; this.cssClass = cssClass || 'remote-user-marker'; } update(range) { this.range = range; this.session._signal('changeBackMarker'); } render(session, layer, className) { const range = this.range; if (!range || !range.isValid()) return; const start = session.documentToScreenPosition(range.start.row, range.start.column); const end = session.documentToScreenPosition(range.end.row, range.end.column); // 创建自定义DOM元素 const el = document.createElement('div'); el.className = `${this.cssClass} ${className}`; el.innerHTML = this.htmlContent; // 设置元素位置(基于Ace的图层坐标) const top = layer.$getTop(start.row, 0); const left = layer.$padding + session.characterWidth * start.column; const height = session.lineHeight; const width = session.characterWidth * (end.column - start.column); el.style.position = 'absolute'; el.style.top = `${top}px`; el.style.left = `${left}px`; el.style.height = `${height}px`; el.style.width = `${width}px`; el.style.pointerEvents = 'none'; // 避免干扰编辑器操作 layer.element.appendChild(el); } }
你可以按需修改htmlContent,比如展示远程用户的头像或用户名:<span class="user-avatar">👤</span><span class="user-name">User1</span>,同时在样式中定义.remote-user-marker的背景色、透明度等样式。
2. 添加远程用户标记到编辑器
在Vue的onMounted钩子中,编辑器初始化完成后,实例化自定义标记并通过addDynamicMarker添加到编辑器,同时处理远程用户位置更新逻辑:
<script setup> import ace, { Range } from 'ace-builds' import "ace-builds/webpack-resolver"; import 'ace-builds/src-noconflict/snippets/javascript' import { reactive, ref, onMounted } from 'vue' const ace_text_area = ref(null) const props = defineProps({ value: String }) const state = reactive({ aceEditor: null, value: "", remoteMarkers: new Map() // 存储远程用户标记,key为用户ID }) // 自定义动态标记类(同上) class CustomRemoteMarker extends ace.Marker { // ... 上述代码 } onMounted(() => { state.value = props.value state.aceEditor = ace.edit(ace_text_area.value, { value: state.value }); const editor = state.aceEditor; // 添加远程用户标记的方法 const addRemoteUserMarker = (userId, range, userInfo) => { // 若用户已有标记,先移除 if (state.remoteMarkers.has(userId)) { editor.removeMarker(state.remoteMarkers.get(userId)); } // 创建自定义HTML内容 const html = `<span style="color: #fff; font-size: 12px; background: #2563eb; padding: 2px 4px; border-radius: 3px;">${userInfo.name}</span>`; // 实例化标记 const marker = new CustomRemoteMarker(range, html); // 添加到编辑器,第二个参数设为true表示在文本层上方渲染 const markerId = editor.addDynamicMarker(marker, true); // 存储标记ID,方便后续更新或移除 state.remoteMarkers.set(userId, markerId); } // 更新远程用户位置的方法 const updateRemoteUserPosition = (userId, newRange) => { const markerId = state.remoteMarkers.get(userId); if (!markerId) return; // 获取标记实例并更新范围 const marker = editor.getMarker(markerId); marker.update(newRange); } // 测试:添加一个远程用户标记(第2行第0列到第2行第5列) const testRange = new Range(1, 0, 1, 5); addRemoteUserMarker('user123', testRange, { name: 'RemoteUser' }); // 实际场景中,通过WebSocket监听远程用户位置变化,调用updateRemoteUserPosition更新 }) </script> <template> <div ref="ace_text_area" style="width: 100%; height: 500px;"></div> </template> <style scoped> /* 自定义标记样式 */ .remote-user-marker { background-color: rgba(37, 99, 235, 0.2); border-left: 3px solid #2563eb; } </style>
关键说明:
addDynamicMarker的第二个参数设为true,确保标记在文本层上方渲染,不会被文本覆盖- 通过
remoteMarkersMap存储用户ID和标记ID的映射,方便后续更新或移除远程用户的标记 - 实际协作场景中,远程用户的位置信息需通过WebSocket等实时通信协议获取,调用
updateRemoteUserPosition更新标记位置
内容的提问来源于stack exchange,提问作者iamafasha
相关产品推荐
相关产品推荐

