简书 Markdown 预览同步滚动脚本


2019.1.22 简书官方已经加入了同步滚动的功能。
但是官方实现的时候,优先的是顶端对齐,也就是编辑区和预览区在顶部一定是同步的。本文的实现是优先底部对齐,也就是如果滑动到最后,预览和编辑是一定会对齐的。我觉得优先底部对齐更实用,因为写作的过程中在文末添加内容的场景更多,这个时候需要对齐。


如你所见,右侧的预览并不能实时地与左侧的输入进行同步。

这是很气的。
哎呦这是最气的。

所以我写了一个脚本,点击此处安装(需要先行安装浏览器用户脚本扩展)。

使用效果

经过评论区小伙伴的反馈,这里还真遇到一个坑:
一开始的版本做的比较简单,当输入框接收到滚动事件后,就直接向预览框也发送这个滚动事件。
但是当浏览器开启平滑滚动时,一次鼠标滚轮的滚动会被浏览器分解成多段来处理,以达到平滑的效果。
平滑滚动的情况下,被分解的滚动事件的头一段的像素移动非常小,我们的脚本却会把这个小滚动事件也照旧作用在预览框上。问题来了,此时预览框接收到滚动事件后,又通过我们这个脚本向输入框上发了一个小滚动事件,相当于“反弹”了一下。也就是说,如果没有这个“反弹”回来的滚动事件影响,预览框本应该继续进行接下来的平滑滚动分解动作,但是却收到了来自我们脚本的小滚动事件,就忽视了接下来的平滑滚动分解动作,所以每次滚动只滚动了很小的距离,看起来像是被卡住了。
所以问题在于考虑的太简单,事件发生了来回相互触发。我们的脚本无法区别哪些是人产生的滚动事件,哪些是脚本发出的滚动事件,导致事件来回反弹,影响正常工作。

解决办法是手动的截断由我们的脚本所产生的滚动事件。步骤如下:
假如发生了编辑框的滚动事件计 mainFlag 为 true,发生了预览框的滚动事件计 preFlag 为 true。

  1. 用户滚动编辑框,触发滚动事件, mainFlag 计为 true,触发脚本改变预览框位置。
  2. 上一步骤预览框位置改变又触发了预览框滚动事件, preFlag 计为 true,此时检测发现 mainFlag 也为 true,证明这次预览框滚动事件是由脚本产生的事件,应该被截断。(如果不截断,这次滚动事件又会触发更改编辑框的动作,引起 bug。)
  3. 把两个标志位都恢复为 false

形象的说就是,由两个标志位来做“配对”,“配对”抵消下一个事件。


1.3 版代码如下:

// ==UserScript==
// @name            简书 Markdown 预览同步滚动
// @name:en         Jianshu MD AUTO Scroll
// @namespace       https://github.com/BlindingDark/JianshuMDAutoScroll
// @include         *://www.jianshu.com/writer*
// @version         1.3
// @description:en  jianshu Markdown preview AUTO scroll
// @description     给简书的在线 Markdown 编辑器增加输入预览同步滚动的功能
// @author          BlindingDark
// @grant           none
// @require      https://cdn.bootcss.com/jquery/3.2.1/jquery.js
// ==/UserScript==

(function() {
  'use strict';
  var spSwitchMain; // 切换的那个按钮所在的窗体
  var txtMain;      // 输入框
  var spPreview;    // 预览框

  const SWITCH_FEATURE   = 'a.fa.fa-columns';
  const EXPAND_FEATURE   = 'a.fa.fa-expand';
  const COMPRESS_FEATURE = 'a.fa.fa-compress';

  function getInput() {
    return $('#arthur-editor');
  }

  function getPreview() {
    return getInput().closest("div").parent().next();
  }

  function scrollEvent(){
    txtMain = getInput()[0];
    spPreview = getPreview()[0];

    if(txtMain == undefined) {
      return;
    }
    if(spPreview == undefined) {
      return;
    }

    let mainFlag = false; // 抵消两个滚动事件之间互相触发
    let preFlag = false; // 如果两个 flag 都为 true,证明是反弹过来的事件引起的

    function scrolling(who){
      if(who == 'pre'){
        preFlag = true;
        if (mainFlag === true){ // 抵消两个滚动事件之间互相触发
          mainFlag = false;
          preFlag = false;
          return;
        }
        txtMain.scrollTop = Math.round((spPreview.scrollTop + spPreview.clientHeight) * txtMain.scrollHeight  / spPreview.scrollHeight - txtMain.clientHeight);
        return;
      }
      if(who == 'main'){
        mainFlag = true;
        if (preFlag === true){ // 抵消两个滚动事件之间互相触发
          mainFlag = false;
          preFlag = false;
          return;
        }
        spPreview.scrollTop = Math.round((txtMain.scrollTop + txtMain.clientHeight) * spPreview.scrollHeight / txtMain.scrollHeight - spPreview.clientHeight);
        return;
      }
    }

    function mainOnscroll(){
      scrolling('main');
    }

    function preOnscroll(){
      scrolling('pre');
    }

    getInput().on('scroll', () => mainOnscroll());
    getPreview().on('scroll', () => preOnscroll());
  }

  function cycle() {
    scrollEvent();
    $(EXPAND_FEATURE).on('click', scrollEvent);
    $(COMPRESS_FEATURE).on('click', scrollEvent);
    $(SWITCH_FEATURE).on("click", scrollEvent);

    window.setTimeout(cycle, 1000);
  }

  cycle();
})();

有兴趣的同学可以直接前往 Github 改进此脚本。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,802评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,109评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,683评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,458评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,452评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,505评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,901评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,550评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,763评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,556评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,629评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,330评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,898评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,897评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,140评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,807评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,339评论 2 342

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,370评论 25 707
  • 阳光公寓门口有一条斑马线,它没有红绿灯,却链接着阳光胡同和国泰商厦。 每天上班高峰期,都必须经过这一条路。路上车水...
    花落风吹雪阅读 326评论 0 0