六、工具栏
最后我们就再实现一下编辑器的工具栏部分的工具(加粗、斜体、有序列表等等),因为这几个工具的实现思路都一致,我们就拿 「「加粗」」 这个工具举例子,其余的就可以模仿着写出来了
加粗工具的实现思路:
- 光标是否选中文字?
- 是。将选中文字的两侧加上
**
- 否。在光标所在处添加文字
**加粗文字**
动图效果演示:
import React, { useState, useRef, useEffect } from 'react' import markdownIt from 'markdown-it' import './theme/github-theme.css' import hljs from 'highlight.js' import 'highlight.js/styles/github.css' const md = new markdownIt({ highlight: function (code, language) { if (language && hljs.getLanguage(language)) { try { return `<pre><code class="hljs language-${language}">` + hljs.highlight(code, { language }).value + '</code></pre>'; } catch (__) {} } return '<pre class="hljs"><code>' + md.utils.escapeHtml(code) + '</code></pre>'; } }) let scrolling: 0 | 1 | 2 = 0 let scrollTimer; export default function MarkdownEdit() { const [htmlString, setHtmlString] = useState('') const [value, setValue] = useState('') // 编辑区的文字内容 const edit = useRef(null) const show = useRef(null) const handleScroll = (block: number, event) => { let { scrollHeight, scrollTop, clientHeight } = event.target let scale = scrollTop / (scrollHeight - clientHeight) if(block === 1) { if(scrolling === 0) scrolling = 1; if(scrolling === 2) return; driveScroll(scale, showRef.current) } else if(block === 2) { if(scrolling === 0) scrolling = 2; if(scrolling === 1) return; driveScroll(scale, editRef.current) } } // 驱动一个元素进行滚动 const driveScroll = (scale: number, el: HTMLElement) => { let { scrollHeight, clientHeight } = el el.scrollTop = (scrollHeight - clientHeight) * scale if(scrollTimer) clearTimeout(scrollTimer); scrollTimer = setTimeout(() => { scrolling = 0 clearTimeout(scrollTimer) }, 200) } // 加粗工具 const addBlod = () => { // 获取编辑区光标的位置。未选中文字时:selectionStart === selectionEnd ;选中文字时:selectionStart < selectionEnd let { selectionStart, selectionEnd } = edit.current let newValue = selectionStart === selectionEnd ? value.slice(0, start) + '**加粗文字**' + value.slice(end) : value.slice(0, start) + '**' + value.slice(start, end) + '**' + value.slice(end) setValue(newValue) } useEffect(() => { // 编辑区内容改变,更新value的值,并同步渲染 setHtmlString(md.render(value)) }, [value]) return ( <div className="markdownEditConainer"> <button onClick={addBlod}>加粗</button> {/* 假设一个加粗的按钮 */} <textarea className="edit" ref={edit} onScroll={(e) => handleScroll(1, e)} onChange={(e) => setValue(e.target.value)} // 直接修改value的值,useEffect会同步渲染展示区的内容 value={value} /> <div className="show" id="write" ref={show} onScroll={(e) => handleScroll(2, e)} dangerouslySetInnerHTML={{ __html: htmlString }} /> </div> ) }
借助这样的思路,就可以完成其它各种工具的实现了。
在我已经发布的markdown-editor-reactjs (opens new window)[8]中,已经完成了其它工具的实现,想要看代码的可以去源码里看
七、补充
为了保证包的体积足够小,我将「第三方依赖库」、「markdown主题」、「代码高亮主题」都通过外链的形式导入了
八、最后
一个简易版的markdown编辑器就实现了,大家可以手动尝试实现一下。后续我也会继续发一些教程,对这个编辑器的功能进行扩展
我将代码都上传到了 Github仓库 (opens new window)[9](希望大家点个⭐️ 「star」),后续扩展一下功能,并作为一个完整的组件发布到npm给大家使用,希望大家多多支持~(其实我已经悄悄发布,但因功能还不是太完善,就不先拿出来给大家使用了,这里简单放个npm包的地址 (opens new window)[10])