简书 Markdown 预览同步滚动脚本

作者: BlindingDark | 来源:发表于2016-11-15 17:49 被阅读731次

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

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

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

使用效果

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

解决办法是手动的截断由我们的脚本所产生的滚动事件。步骤如下:
假如发生了编辑框的滚动事件计 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 改进此脚本。

相关文章

网友评论

  • 乄恰似一种蜕变:多谢作者大大,刚好是我需要的,拿走了。
  • 李傲龍:哇很棒了已经,就是有一点左右内容不太对齐,不知道为什么做这个插件的人不多。
    BlindingDark:@李傲龍 目前能查到的好像只有我这一个,别的我是没查到。可能用这个都是小众,而且大部分情况还是用本地编辑器。
    左右内容不齐这个很难解决,因为 markdown 一行可能是最终预览的一大段,比如插入图片,表格等等。
  • ouhaya:你好,我想问下,预览窗口比编辑窗口的滚动慢5,6行,是故意这么做的吗?感觉有点别扭呢。
    BlindingDark:这个算是 bug,我有点偷懒。这个滚动比例不好把握,因为 markdown 内容的长度和预览内容长度是差很大的,比如你插入了一张图片,markdown 只用了一行,预览却会占据很大一块空间。
    现在的版本只是保证当你 markdown 编辑框滚动条拖动到底部的时候,预览框也一定在底部。
    这么做是为了保证在写文章的时候,往下创建新行的时候,预览能跟着滚动。
    如何做到完美的一一对应?我还没有很好的解决方案。但是源代码是开放的,原理也很简单,有能力的朋友可以帮忙优化一下。
  • 董八七:鼠标滚轮不能使用了
    BlindingDark:@董八七 不清楚。代码上只是监听滚动事件。难道是Chrome版本问题?你更新一下?
    董八七:@BlindingDark 就是上下鼠标滚轮,而页面没有响应,只能拖进度条
    BlindingDark:我这是正常工作的。“鼠标滚轮不能使用”指的是什么呢?
  • 董八七:简书不支持预览同步感觉特不能理解,尤其是文章比较长时。谢谢分享,尝试一下。
  • 鱼f:收藏

本文标题:简书 Markdown 预览同步滚动脚本

本文链接:https://www.haomeiwen.com/subject/iegkpttx.html