markdown 编辑器实现双屏同步滚动
由于一直在使用 markdown 编辑器写技术文章,所以对于编写体验很敏感。我发现各大社区的 markdown 编辑器基本都有同步滚动功能。只不过有些做得好,有些做得马马虎虎。出于好奇,我就打算自己亲自实现一下这个功能。
思考了一段时间,最后想出来了三种方案:
- 百分比滚动
- 双屏同时渲染占用面积大的元素
- 每一行的元素都赋上一个索引,根据索引来精确同步每一行的滚动高度
百分比滚动
假设现在正在滚动 a 屏,那 a 屏的滚动百分比计算方式为:a 屏的滚动高度 / a 屏的内容总高度
,用代码表示 a.scrollTop / a.scrollHeight
。当滚动 a 屏时,需要手动同步 b 屏的滚动高度,也就是根据 a 屏的滚动百分比算出 b 屏的滚动高度:
a.onscroll = () => {b.scrollTo({ top: a.scrollTop / a.scrollHeight * b.scrollHeight })
}
原理就是这么简单,可惜实现效果不太好。
从上面的动图可以看出,当我在第二个大标题处停留的时候,左右双屏的内容是同步的。但当我滚动到第三个大标题时,左右双屏的内容高度已经差了将近 300 像素了。所以说这个方案勉勉强强能用吧,聊胜于无。
双屏同时渲染占用面积大的元素
双屏内容高度不一致,是因为 markdown 同一个元素渲染后的高度和渲染前会有差别。例如一个图片,用 markdown 写就一行代码的事,但渲染出来的图片有大有小,高度几十、几百像素的都有。如果 markdown 的图片代码双屏同时渲染,倒是能解决这个问题。
但是除了图片仍然有不少元素渲染前后的高度是有差距的,虽然没有图片这么夸张。譬如 h1 h2 这种,当文章内容越长,这种小差异带来的问题会越来越大,导致双屏内容高度的差距也会越来越大。所以说这种方案也不是很靠谱。
每一行的元素都赋上一个索引,根据索引来精确精确同步每一行的滚动高度
之前两个方案都属于勉强能用,不够好。现在这个第三方案就比前面两个强多了,几乎能做到精确同步每一行的内容。具体怎么做呢?
第一步,监听 markdown 编辑框的内容变化,为每一个元素赋上一个索引,空行空文本除外。
当把编辑框的 HTML 传给右边的框渲染时,需要把 data-index
赋值给渲染后的元素。这样就能通过 data-index
精确定位渲染前后的同一元素了。
第二步,根据 a 屏的元素滚动高度计算 b 屏上同一索引的元素滚动高度
在 a 屏进行滚动时,需要从上到下遍历 a 屏的所有元素,并且找到第一个在屏幕内的元素。找到第一个在屏幕内的元素
这句话的意思是因为在滚动过程中,有些元素会因为滚动跑到屏幕外面(原来在屏幕内,滚动到屏幕外),这些元素我们是不需要计算的。
判断一个元素是否在屏幕内:
// dom 是否在屏幕内
function isInScreen(dom) {const { top, bottom } = dom.getBoundingClientRect()return bottom >= 0 && top < window.innerHeight
}
除了判断元素是否在屏幕内,还需要判断这个元素在屏幕内的部分占整个元素高度的百分比。譬如说一个图片的 markdown 字符串,由于滚动的原因,导致一半在屏幕内,一半在屏幕外。为了精确同步,那么渲染后的图片也必须有一半在屏幕内一半在屏幕外。
计算元素在屏幕内的百分比代码:
// dom 在当前屏幕展示内容的百分比
function percentOfdomInScreen(dom) {// 已经通过另一个函数 isInScreen() 确定了这个 dom 在屏幕内,所以只需要计算它在屏幕内的百分比,而不需要考虑它是否在屏幕外const { height, bottom } = dom.getBoundingClientRect()if (bottom <= 0) return 0 // 不在屏幕内if (bottom >= height) return 1 // 完全在屏幕内return bottom / height // 部分在屏幕内
}
现在我们就可以从上到下遍历 a 屏的所有元素,找到第一个在屏幕内的元素了:
// scrollContainer 即上面说的 a 屏,ShowContainer 是 b 屏
const nodes = Array.from(scrollContainer.children)
for (const node of nodes) {// 从上往下遍历,找到第一个在屏幕内的元素if (isInScreen(node) && percentOfdomInScreen(node) >= 0) {const index = node.dataset.index// 根据滚动元素的索引,找到它在渲染框中对应的元素const dom = ShowContainer.querySelector(`[data-index="${index}"]`)// 获取滚动元素在 a 屏中展示的内容百分比const percent = percentOfdomInScreen(node)// 计算这个对等元素在 b 屏中距离容器顶部的高度const heightToTop = getHeightToTop(dom)// 根据 percent 算出对等元素在 b 屏中需要隐藏的高度const domNeedHideHeight = dom.offsetHeight * (1 - percent)// scrollTo({ top: heightToTop }) 会把对等元素滚动到在 b 屏中恰好完全展示整个元素的位置// 然后再滚动它需要隐藏的高度 domNeedHideHeight,组合起来就是 scrollTo({ top: heightToTop + domNeedHideHeight })ShowContainer.scrollTo({ top: heightToTop + domNeedHideHeight })break}
}
从动图来看,目前已经做到行内容的精确同步了。
踩坑
有一些元素渲染后会变成嵌套元素,例如表格 table,渲染后的内容层级为:
<table><tbody><tr><td></td></tr></tbody>
</table>
按照目前的渲染逻辑,假如我写了个表格:
|1|b|
...
那么 |1|b|
上的 data-index
会对应到 table
上。
那这就会有个 bug,当 |1|b|
滚动到 50% 的时候,整个 table
也会滚动到 50%。这个现象如下图所示:
这和我们相要的效果不一样。a 屏连一行的内容都没滚完,b 屏整个内容已经滚动到一半了。
所以像这种嵌套的元素,在打 data-index
标记时,要把它打到真正的内容上。用表格 table 来做示例,就得把 data-index
的标记打在 tr
上。
这样一来,同步滚动就正常了。同理,其他的嵌套元素也一样(譬如 ul ol)。
总结
完整的代码我已经放在 github 上了:
- markdown-editor-sync-scroll-demo
还有在线 DEMO:
- demo1
- demo2
- demo3
- demo4
- demo5
- demo6
如果在线 DEMO 比较慢,可以克隆项目后直接打开 html 文件访问。
markdown 编辑器实现双屏同步滚动相关推荐
- Markdown编辑器的使用方法
本文目录 Markdown介绍 快捷键 1.标题 2.粗体/斜体/删除线/字体底色 3.上标/下标 4.无序排列 5.有序排列 6.待办事项 7.引用 8.脚注 9.自动注释 10.链接 a) 地址 ...
- Atom-无懈可击的Markdown编辑器
备战美赛期间,向岳神学习,搞了Atom玩协作开发,第一次没有自动补全的手撸了遗传算法.今天发现Atom还有写Markdown的妙用,遂拿来练手. 1. 安装Atom 下载安装Atom:https:// ...
- Typora markdown公式换行等号对齐_下了31个markdown编辑器,我就不信选不出一个好用的...
markdown编辑器测评 标准 总体标准 渲染领域 编辑领域 数据管理 其他 Typora Vnote Mweb Joplin Zettlr macdown ulysses Marktext gho ...
- 使用Atom快速打造好用的Markdown编辑器
使用Atom快速打造好用的Markdown编辑器 Atom当前主流的跨平台的三大编辑器(Atom,sublime,vscode)之一 今天尝试了使用Atom来打造Markdown编辑器,快速上手且易用 ...
- 在线编辑_水墨-在线 Markdown 编辑器
水墨-在线 Markdown 编辑器 基于 Spring-boot.FreeMarker.layui.Vditor 构建的一款在线 所见即所得的 Markdown 编辑器.水墨-在线 Markdown ...
- markdown 编辑器_推荐一款公众号 Markdown 编辑器
公众号 Markdown 编辑器 简介 这款编辑器可以将 Markdown 转换成微信公众号编辑器的样式,只需将 MD 文档复制到左侧栏,再在右侧栏顶部"点击复制",右侧预览内容就 ...
- mac markdown_适用于Mac的最佳Markdown编辑器
mac markdown Markdown provides a convenient way to add formatting to a plain text document, while le ...
- Gitbook详解(七)-Markdown编辑器推荐
文章目录 1. 编辑器类型 2. MarkdownPad 3. Typora 4. Mou 5. Atom 6. Haroopad 7. Cmd Markdown 8. 最后 1. 编辑器类型 Git ...
- Typora收费了?推荐两款Markdown编辑器
大家好,这里是扫地工. 这是一篇迟了两个月的推文,Typora 在11月底时推出正式版本,并正式转为收费版本. 这个话题当时直接冲上了知乎热榜第一,不过我很少使用Markdown,而且一直觉得Typo ...
最新文章
- summary_1.正念自控法
- 他在 B 站有 140 万粉丝,今天来免费带你学 Linux 了!
- python 打包发布网站_Python代码的打包与发布
- python dry原则_关于Python 的这几个技巧,你应该知道
- Hive 之 常用函数
- hector与gmapping总结
- C语言-字符数组和字符串
- win10最常用dos命令以及win+R即可运行的命令
- 在excel中数字比对_Excel数据比对,多种方法总有一个适合你
- OSN 3500 SDH智能光传输系统整机与单板技术分享
- win10热点手机显示IP配置错误连不上和电脑连上网线没网络
- Crazy Kids
- AHRS姿态解算说明(加速度+陀螺仪+磁力计原理及原始数据分析)
- 如何实现页面广告随时上下线、过期自动下线及到时自动上线
- 新一代智能视频云发展现状分析:五大要素成关键
- 用phpcms切换中英文网页的方法(不用解析二级域名)、phpcms完成pc和手机端切换(同一域名)...
- [FAQ12112]在电池低电压时,如何关闭camera的闪光功能
- 手机服务器艰辛之路(一)~手机服务器环境部署
- iOS 面试题集合~[有答案]
- CSDN《原力计划—打卡挑战》为你而来,新升级, 多流量,抓住春季的小尾巴,冲冲冲!