Wilson@思源

目 录

给编辑器增加标尺

js
// 功能:给编辑器增加标尺 // see https://ld246.com/article/1725849206361 (()=>{ //////////// 配置区 //////////////////// // 标尺的间隔大小,默认50 (50代表50px高度) const rulerGaps = 50; //////////// 主逻辑区 //////////////////// addRulerStyle(); whenElementExist('.layout__center').then(async element => { // 等待笔记列表加载完毕 await sleep(40); // 防止死循环 let locking = false; // 监听编辑器加载事件 observeDomChange(element, async (mutation) => { // 添加标尺本身则不监听(暂无用到) // if(mutation.addedNodes && Array.from(mutation.addedNodes)?.some(item=>item.classList?.contains("ruler"))){ // return; // } if (mutation.target.classList?.contains("protyle-wysiwyg")) { const editor = mutation.target; if(editor && editor.closest){ // 等待编辑器加载完毕 const protyle = editor.closest(".protyle"); if(protyle.dataset.loading !== 'finished'){ await whenElementExist(()=>editor?.closest(".protyle") === 'finished'); } // 编辑器加载完毕(包括编辑器内容被修改) //console.log('editor loaded'); // TODO 这里写你要进行的操作 if(locking) return; locking = true; //console.log('runed'); //添加标尺 addRuler(editor, protyle); setTimeout(()=>{ locking = false; // 加载时,防止编辑器未完全加载,再执行一次 if(!document.querySelector('#ruler-'+protyle.dataset.id)) addRuler(editor, protyle); }, 1000) } } }); }); //////////// 函数辑区 //////////////////// async function addRuler(editor, protyle) { let ruler = document.querySelector('#ruler-'+protyle.dataset.id); const first = !ruler; let containner; if(!ruler) { containner = document.createElement('div'); containner.className = 'protyle-ruler'; ruler = document.createElement('div'); ruler.id = 'ruler-'+protyle.dataset.id; ruler.className = 'ruler'; ruler.contentEditable = false; } if(first) await sleep(500); // 根据 editor 的高度计算需要多少个刻度线 const numberOfTicks = Math.floor(editor.clientHeight / rulerGaps); //ruler.style.height = (rulerGaps * numberOfTicks) + 'px'; if(ruler.children.length > numberOfTicks) { while (ruler.children.length > numberOfTicks) { ruler.removeChild(ruler.children[ruler.children.length-1]); } return; } for (let i = ruler.children.length; i <= numberOfTicks; i++) { const tick = document.createElement('div'); tick.className = 'tick'; const tickNumber = document.createElement('span'); tickNumber.className = 'tick-number'; tickNumber.textContent = i; // 显示间隔数 const tickLine = document.createElement('div'); tickLine.className = 'tick-line'; tick.appendChild(tickNumber); tick.appendChild(tickLine); ruler.appendChild(tick); } if(first) { //editor.prepend(ruler); containner.appendChild(ruler); editor.parentElement.insertBefore(containner, editor); } } function addRulerStyle() { if(document.querySelector("#rulerStyle")) return; const style = document.createElement('style'); style.id = 'rulerStyle'; style.textContent = ` .protyle-ruler { position:relative; .ruler { position: absolute; left: 0; top: 0; width: 30px; /* 宽度包括数字和短横线 */ display: flex; flex-direction: column; justify-content: flex-start; align-items: flex-start; /* 让内容靠左对齐 */ border-right: 0.5px solid var(--b3-border-color); font-size:13px; cursor: default; user-select: none; /*overflow:hidden;*/ } .tick { position: relative; width: 100%; height: ${rulerGaps||50}px; /* 每个刻度线的高度 */ display: flex; justify-content: flex-end; align-items: center; } .tick-line { width: 8px; height: 1px; /*background-color: var(--b3-theme-on-surface-light);*/ background-color: var(--b3-border-color); } .tick-number { writing-mode: vertical-rl; /* 文字垂直书写 */ transform: rotate(180deg); /* 旋转文字 */ margin-right: 1px; /* 与数字之间留点空隙 */ color: var(--b3-theme-on-surface-light); } } ` document.head.appendChild(style); } // 观察元素被添加 function observeDomChange(selector, callback) { // 定义一个回调函数,当DOM发生变化时调用 const onChange = function(mutationsList, observer) { for (const mutation of mutationsList) { if (mutation.type === 'childList') { callback(mutation); } } }; // 创建一个观察器实例,并传入回调函数 const observer = new MutationObserver(onChange); // 配置观察选项:指定需要观察哪些变动 const config = { attributes: false, childList: true, subtree: true }; // 获取目标节点 const targetNode = typeof selector === 'string' ? document.querySelector(selector) : selector; // 如果目标节点存在,则开始观察 if (targetNode) { observer.observe(targetNode, config); } // 返回一个函数,用于停止观察 return () => { observer.disconnect(); }; } // 延迟执行 function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } // 等待元素渲染完成后执行 function whenElementExist(selector) { return new Promise(resolve => { const checkForElement = () => { let element = null; if (typeof selector === 'function') { element = selector(); } else { element = document.querySelector(selector); } if (element) { resolve(element); } else { requestAnimationFrame(checkForElement); } }; checkForElement(); }); } })();