JavaScript 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)。

到此这篇关于JavaScript markdown 编辑器实现双屏同步滚动的文章就介绍到这了,更多相关JS markdown 双屏同步滚动内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • JavaScript实现页面无缝滚动效果

    目前我只使用两种方式,如果还有其他方式,希望推荐一下. 1.js+transform使用定时器动态增加大小,再把值赋给 transform,实现位置偏移,来实现无缝滚动. html一定要循环两遍数据,这样的话,会出现两个一样的数据,在一个数据消失后,不会使页面空白,而这时transform归0,有从头开始,因为两个数据相同,归0后视觉上就像无缝滚动. <div style="height: 100%" @mouseenter="moveStar()" @mou

  • js实现文字滚动的效果

    本文实例为大家分享了js实现文字滚动的效果的具体代码,供大家参考,具体内容如下 在之前小编已经和大家介绍了一些常用的js动画效果,在此,和大家介绍一种可能不太常用的动画效果.该动画效果与文字相关,且,虽然不常用,但几乎每个人都见过它.相信大家都使用酷狗音乐或是网易云音乐进行音乐的搜索,听鉴,那么久一定会看到歌词页面中,歌词滚动的效果.此动画效果与其相同,但相对于这些音乐歌词效果而言又有些不同,增加了文字竖直排列并且滚动的效果. 在介绍之前,先和大家一起了解一些文字垂直排列的方式:writing-

  • JavaScript滚动轮播图制作原理详解

    本文实例为大家分享了JavaScript滚动轮播图制作原理,供大家参考,具体内容如下 滚动轮播图 滚动轮播图布局关键:是所有图片并排在一起,所以unit的宽要足够宽.克隆第一张图片追加到最后一张右按钮拉动策略:先拉动,再瞬移左按钮拉动策略:先瞬移,再拉动与呼吸轮播图一样,需要做防流氓策略 复制代码:将我代码中的图片更换为你的图片,注意图片路径,还需要引入js库,我引入的是jquery-1.12.3.min.js的版本,引入的时候注意路径 举例: <!DOCTYPE html> <html

  • js实现列表自动滚动循环播放

    本文实例为大家分享了js实现列表自动滚动循环播放的具体代码,供大家参考,具体内容如下 1.实现效果图 鼠标移入,暂停滚动: 鼠标移出,继续滚动: 2.原理 要实现无缝衔接,在原有ul后面还要有一个一样内容的ul: 最外层div为可视区域,设overflow:hidden: 2个ul的高度 > 外层可视div高度,才能滚动: 3.实现代码 html: <!-- vue --> <div id="review_box" @mouseover="rollSt

  • JavaScript+Node.js写一款markdown解析器

    目录 1. 准备工作 2. 处理图片&超链接 3. 处理blockquote 4. 处理标题 5. 处理字体 6. 处理代码块 7. 处理列表 8. 处理表格 9. 调用方法 1. 准备工作 首先编写getHtml函数,传入markdown文本字符串,这里使用fs读取markdown文件内容,返回值是转换过后的字符串. const fs = require('fs'); const source = fs.readFileSync('./test.md', 'utf-8'); const get

  • js正则匹配markdown里的图片标签的实现

    其实前端后端需要将markdown文本转换为html文本都有相应的库,几句代码就ok,但有时我们又必须获取到markdown里的某个标签来进行相应的转换,有几种办法,可以从已经转换好的html文本里获取,还有的就是直接从markdown文本里获取,这里说的是第二种. 1. 一个markdown里只有一个图片的情况 const str = "asddsad![标题](url)asdasddasd"; //一段markdown文本,包含一个图片"![标题](url)"

  • JS加载解析Markdown文档过程详解

    网上有很多网站会通过.md文档来做页面内容,很好奇,这是怎么做的? 出于好奇,建了一个test.md文件: # Hello World! asdfa asd *斜体文本* **粗体文本** ***斜粗体文本*** 分隔线(如下) *** * * * **** - - - ----------- GOOGLE.COM ~~删除线~~ <u>下划线</u> 用浏览器打开之后,结果就被原封不动的输出来了,浏览器根本不会解析这玩意... 跟阮老师的blog相差好大啊~呵呵~还是太天真了!

  • js实现列表循环滚动

    本文实例为大家分享了js实现文字无缝轮播的具体代码,供大家参考,具体内容如下 先介绍几个属性 clientHeight 元素的高度 clientTop 元素顶部边框的宽度 scrollTop 滚动条遮挡的部分的高度(包含border) scrollHeight 整个内容的高度(包含border) offsetTop 距离上一个 position 不为 static(默认) 的元素的顶部内边框的距离 <!DOCTYPE html> <html lang="en">

  • Vuejs中使用markdown服务器端渲染的示例

    啊哈,又是来推荐一个 vuejs 的 package,miaolz123/vue-markdown. 对应的应用场景是:你想使用一个编辑器或者是在评论系统中支持 markdown.这个 package 的有点还是挺多了,比如默认就支持 emoji,这个就很完美啦!laravist 的新版就使用了 vue-markdown 来渲染评论. 安装 直接使用 npm 来安装: npm install --save vue-markdown 使用 也是很简单的,可以直接这样: import VueMark

  • JavaScript markdown 编辑器实现双屏同步滚动

    目录 前言 百分比滚动 双屏同时渲染占用面积大的元素 赋上一个索引 踩坑 前言 由于一直在使用 markdown 编辑器写技术文章,所以对于编写体验很敏感.我发现各大社区的 markdown 编辑器基本都有同步滚动功能.只不过有些做得好,有些做得马马虎虎.出于好奇,我就打算自己亲自实现一下这个功能. 思考了一段时间,最后想出来了三种方案: 百分比滚动 双屏同时渲染占用面积大的元素 每一行的元素都赋上一个索引,根据索引来精确同步每一行的滚动高度 百分比滚动 假设现在正在滚动 a 屏,那 a 屏的滚

  • vue中利用simplemde实现markdown编辑器(增加图片上传功能)

    前言 最近在搭个人博客网站,需要一个 markdown 编辑器,来进行博客的编写 看了网上的教程,决定使用 simplemde 以为可以直接能拿来用的 不过实际运用的时候发现还是有要完善的地方 比如令人头疼的图片上传 最终效果 安装及初始化 npm install simplemde --save 在html中加入一个textarea <textarea id="simplemde"></textarea> 在vue的生命周期函数 mounted 中,添加 si

  • 利用Electron简单撸一个Markdown编辑器的方法

    Markdown 是我们每一位开发者的必备技能,在写 Markdown 过程中,总是寻找了各种各样的编辑器,但每种编辑器都只能满足某一方面的需要,却不能都满足于日常写作的各种需求. 所以萌生出自己动手试试,利用 Electron 折腾一个 Markdown 编辑器出来. 下面罗列出我所理想的 Markdown 编辑器的痛点需求: 必须要有图床功能,而且还可以直接上传到自己的图片后台,如七牛: 样式必须是可以自定义的: 导出的 HTML 内容可以直接粘贴到公众号编辑器里,直接发布,而不会出现格式的

  • Thinkphp5框架中引入Markdown编辑器操作示例

    本文实例讲述了Thinkphp5框架中引入Markdown编辑器操作.分享给大家供大家参考,具体如下: 编辑器下载地址以及演示:https://pandao.github.io/editor.md/ 1.把下载的项目放在public目录下 2.页面中引入jquery.js,editormd.js,editormd.css demo <!DOCTYPE html> <html lang="en"> <head> <meta charset=&qu

  • 分享一个基于Ace的Markdown编辑器

    我认为的编辑器分成两类,一种是分为左右两边实现即时渲染:一种是先写语法,然后通过按钮实现渲染. 其实即时渲染也不难,共同需要考虑的问题就是xss,因为渲染库能自定义第三方的xss过滤(之前是通过设置来实现,也就是本身自带,不过在某个版本后被取消了),所以xss就用官方推荐的dompurify.即时渲染可以通过编辑器本身api实现文本变动监听来实现,还有一个需要考虑的问题就是代码与渲染区域的对应.但因为这与我的需求相悖,在这里就不介绍了,相信小老板们都能轻松实现 统一惯例,我们来看看效果图 上面的

  • Android实现两个ScrollView互相联动的同步滚动效果代码

    本文实例讲述了Android实现两个ScrollView互相联动的同步滚动效果代码.分享给大家供大家参考,具体如下: 最近在做一个项目,用到了两个ScrollView互相联动的效果,简单来说联动效果意思就是滑动其中的一个ScrollView另一个ScrollView也一同跟着滑动,要做到一起同步滑动.感觉在以后的项目开发中大家可能也会用到,绝对做个Demo分享出来,供大家一起学习,以便大家以后好用,觉的不错,有用的可以先收藏起来哦! 其实对于ScrollView,Android官方并没有提供相关

  • C#实现两个richtextbox控件滚动条同步滚动的简单方法

    前言 有时候我们需要实现对照文章等,往往将文本放到两个richtextbox控件中,但是,如果我们需要同步滚动查看,来达到更好的观看效果. 当然,传统的方法重载控件或者自定义控件都可以达到目的,但是对于新手或者想仅仅只用一次这个控件的人来说,是非常麻烦的.所以,接下来我来提供一种简单快捷的方法来实现:richtextbox滚动条同步的功能. 方法如下: 首先,我们在winform窗体创建两个richtextbox控件 下面介绍两个方法,我经常用到 第一个方法,获得当前鼠标所在richtextbo

  • 利用Vue实现一个markdown编辑器实例代码

    前言 前段时间做项目的时候,需要一个Markdown编辑器,在网上找了一些开源的实现,但是都不满足需求 说实话,这些开源项目也很难满足需求公司项目的需求,与其实现一个大而全的项目,倒不如实现一个简单的,易于在源码上修改的项目,核心功能都有的,以供修改使用 本文的源码地址如下:https://github.com/jiulu313/HelloMarkDown(本地下载) 喜欢的朋友可以帮忙star一下,欢迎交流学习 先看一下本项目的效果图(图片经过压缩) 本文的目的就是实现一个有核心功能的,简单,

  • javascript实现蒙版与禁止页面滚动

    本文实例为大家分享了javascript实现蒙版与禁止页面滚动的具体代码,供大家参考,具体内容如下 项目需求:页面很长,要求加个蒙版,点击特定位置蒙版消失可以滑动页面,否则蒙版存在页面不可以滑动:要同时兼容移动端和pc端. <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name=viewport content="

  • javascript单张多张图无缝滚动实例代码

    我们会看到很多的网站上会使用多张图片无缝滚动的效果. 下面我就介绍几种纯JS实现多张图片的无缝滚动,并实现鼠标移到图片上运动停止的效果,可以控制图片左右滚动. 1.效果展示: <!DOCTYPE html> <html> <head> <title>无缝滚动</title> </head> <style type="text/css"> *{margin: 0;padding: 0;} #div1{po

随机推荐