├── .gitignore
├── src
├── index.js
├── lib
│ ├── fragment.js
│ ├── configOptions.js
│ ├── syncScroll.js
│ ├── scrollControl.js
│ └── area.js
├── styles.css
└── demo.js
├── .idea
├── misc.xml
├── vcs.xml
├── encodings.xml
├── modules.xml
├── sync-scroll.iml
└── workspace.xml
├── babel.config.js
├── index.html
├── LICENSE
├── webpack.prod.config.js
├── webpack.config.js
├── package.json
├── README.md
├── dist
├── index.html
├── md-sync-scroll.js
└── styles.css
└── template.html
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | .idea/
3 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | export * from './lib/syncScroll';
2 | export * from './lib/configOptions';
3 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
${hljs.highlight(lang, str, true).value}`;
20 | } catch (e) {
21 | }
22 | }
23 |
24 | return `${md.utils.escapeHtml(str)}`;
25 | }
26 | });
27 |
28 | const editArea = document.getElementById('edit');
29 | const previewArea = document.getElementById('preview');
30 | const editArea2 = document.getElementById('edit2');
31 | const previewArea2 = document.getElementById('preview2');
32 |
33 | const options = ConfigOptions.instance({
34 | syncWithClick: true,
35 | offsetScroll: 100
36 | });
37 | const syncScroll = new SyncScroll(options);
38 | previewArea.innerHTML = md.render(editArea.innerText);
39 | previewArea2.innerHTML = md.render(editArea2.innerText);
40 |
41 | syncScroll.addAreas([
42 | {
43 | area: editArea,
44 | queryCriteria: '.h1,.h2,.h3,.h4,.h5,.h6'
45 | },
46 | {
47 | area: previewArea,
48 | queryCriteria: 'h1,h2,h3,h4,h5,h6'
49 | },
50 | {
51 | area: editArea2,
52 | queryCriteria: '.h1,.h2,.h3,.h4,.h5,.h6'
53 | },
54 | {
55 | area: previewArea2,
56 | queryCriteria: 'h1,h2,h3,h4,h5,h6'
57 | }
58 | ]);
59 |
--------------------------------------------------------------------------------
/src/lib/scrollControl.js:
--------------------------------------------------------------------------------
1 | import {Area} from "./area";
2 |
3 | /**
4 | * control类,用于内部对`Area`统一管理
5 | */
6 | export class ScrollControl {
7 |
8 | /**
9 | * 信号量,避免滚动事件死循环
10 | * @type {number}
11 | * @private
12 | */
13 | _sign = 1;
14 | /**
15 | * 当前对齐的`Fragment`
16 | */
17 | _curFrag;
18 | /**
19 | * `Area`集合
20 | */
21 | _areas;
22 | /**
23 | * 全局配置
24 | */
25 | _options;
26 |
27 | constructor(options) {
28 | this._options = options;
29 |
30 | this._areas = [];
31 | }
32 |
33 | /**
34 | * 添加`Area`
35 | * @param el - 对应的DOM元素
36 | * @param queryCriteria - 查询字符串
37 | * @param options
38 | */
39 | addArea(el, queryCriteria, options) {
40 | const area = new Area(this, el, queryCriteria, options || this._options);
41 | this._areas.push(area);
42 | }
43 |
44 | /**
45 | * 以`syncArea`为参照,进行同步滚动。
46 | * @param syncArea - 参照的`Area`
47 | * @param offsetOptions
48 | */
49 | syncScroll(syncArea, offsetOptions) {
50 | this._curFrag = syncArea.currentFragment();
51 |
52 | const scrollTop = syncArea.scrollTop, scrollLeft = syncArea.scrollLeft;
53 | for (let i = 0, length = this._areas.length; i < length; i++) {
54 | if (syncArea === this._areas[i]) { continue; }
55 |
56 | this._areas[i].syncWith(this._curFrag, scrollTop ,scrollLeft, offsetOptions);
57 | }
58 | }
59 |
60 | updateAreas() {
61 | let area;
62 | for (let i = 0, length = this._areas.length; i < length; i++) {
63 | area = this._areas[i];
64 |
65 | area.updateFragments();
66 | }
67 | }
68 |
69 | destroyAreas() {
70 | this._areas.forEach(function (area) {
71 | area.destory();
72 | })
73 | }
74 |
75 | /**
76 | * 判断互斥锁状态
77 | * @returns {boolean}
78 | */
79 | isLocked() {
80 | return !(this._sign === 1);
81 | }
82 |
83 | /**
84 | * 加锁
85 | */
86 | lock() {
87 | this._sign = this._sign - this._areas.length + 1;
88 | }
89 |
90 | /**
91 | * 解锁
92 | */
93 | unlock() {
94 | this._sign = this._sign + 1;
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Sync Scroll
2 | 
3 | 
4 | 
5 | 
6 | 
7 | 
8 | 
9 |
10 | 这是一个基于**片段**的同步滚动插件。
11 |
12 | 请查看Github上的[Demo页][https://abc1310054026.github.io/sync-scroll/],上面有具体的效果。
13 | 如果你不关心实现的方式,请直接看**Usage**
14 | ## 片断
15 | ```markdown
16 | // 片段start
17 | # 这表示一个片段的开始
18 | 这些是片段内容
19 | 这些也是片段内容
20 | // 片段end
21 | ```
22 | 如果你想要使用它让你的内容能进行同步滚动,你需要让你的内容符合以上的布局。这种布局很多Markdown格式的文本都是天然符合的,不过就是标题-内容-标题-内容...
23 |
24 | 示例:
25 |
26 | ```markdown
27 | # 文章标题
28 | 标题下的内容...
29 | # 文章标题2
30 | 标题下的内容2...
31 | ...
32 | ```
33 | 其实这个页面就是一个很好的例子,当你滚动滚轮或滚动条的时候,注意一下以`#`号开头的段落,应该能看到左右两边的滚动距离是不同的。
34 |
35 | # Usage
36 | 如果想让SyncScroll正常运行,需要你用一个块级元素包裹你的内容,你可以按`F12`查看此页的代码。
37 |
38 | 我用`
10 |
11 |
12 |
32 | ```
33 | // sync-scroll被占用了,没得办法.jpg
34 | npm install md-sync-scroll --save
35 | ```
36 |
37 |
39 | ```markdown
40 | // 片段start
41 | # 这表示一个片段的开始
42 | 这些是片段内容
43 | 这些也是片段内容
44 | // 片段end
45 | ```
46 |
47 |
51 | ```markdown
52 | # 文章标题
53 | 标题下的内容...
54 | # 文章标题2
55 | 标题下的内容2...
56 | ...
57 | ```
58 |
59 |
79 | ```js
80 | import {SyncScroll, ConfigOptions} from "md-sync-scroll";
81 | ```
82 |
83 |
88 | ```js
89 | import {SyncScroll, ConfigOptions} from "md-sync-scroll";
90 |
91 | const editArea = document.getElementById('edit');
92 | const previewArea = document.getElementById('preview');
93 | // 通过ConfigOptions可以配置参数,详细信息见下文`API`
94 | const options = ConfigOptions.instance({
95 | syncWithClick: true,
96 | offsetScroll: 100
97 | });
98 | const syncScroll = new SyncScroll(options);
99 |
100 | // 对于本页面来说,我用`h1-6`指示片段的开始,那么我就要查询被我指定为`h1-h6`的元素
101 | // 在左边我用class='h1-6'标记,在右边用<h1>-<h6>表示
102 | // syncScroll.addArea(editArea, '.h1,.h2,.h3,.h4,.h5,.h6');
103 | // syncScroll.addArea(previewArea, 'h1,h2,h3,h4,h5,h6');
104 | syncScroll.addAreas([
105 | {
106 | area: editArea,
107 | queryCriteria: '.h1,.h2,.h3,.h4,.h5,.h6'
108 | },
109 | {
110 | area: previewArea,
111 | queryCriteria: 'h1,h2,h3,h4,h5,h6'
112 | }
113 | ]);
114 | // 可以调用`addArea`单个添加,在`addArea`调用后,需要手动调用`update`更新数据
115 | // syncScroll.addArea({
116 | // area: editArea,
117 | // queryCriteria: '.h1,.h2,.h3,.h4,.h5,.h6'
118 | // });
119 | // syncScroll.addArea({
120 | // area: previewArea,
121 | // queryCriteria: 'h1,h2,h3,h4,h5,h6'
122 | // });
123 | // syncScroll.update();
124 |
125 | ```
126 |
127 |
10 |
11 |
12 |
32 | ```
33 | // sync-scroll被占用了,没得办法.jpg
34 | npm install md-sync-scroll --save
35 | ```
36 |
37 |
39 | ```markdown
40 | // 片段start
41 | # 这表示一个片段的开始
42 | 这些是片段内容
43 | 这些也是片段内容
44 | // 片段end
45 | ```
46 |
47 |
51 | ```markdown
52 | # 文章标题
53 | 标题下的内容...
54 | # 文章标题2
55 | 标题下的内容2...
56 | ...
57 | ```
58 |
59 |
79 | ```js
80 | import {SyncScroll, ConfigOptions} from "md-sync-scroll";
81 | ```
82 |
83 |
88 | ```js
89 | import {SyncScroll, ConfigOptions} from "md-sync-scroll";
90 |
91 | const editArea = document.getElementById('edit');
92 | const previewArea = document.getElementById('preview');
93 | // 通过ConfigOptions可以配置参数,详细信息见下文`API`
94 | const options = ConfigOptions.instance({
95 | syncWithClick: true,
96 | offsetScroll: 100
97 | });
98 | const syncScroll = new SyncScroll(options);
99 |
100 | // 对于本页面来说,我用`h1-6`指示片段的开始,那么我就要查询被我指定为`h1-h6`的元素
101 | // 在左边我用class='h1-6'标记,在右边用<h1>-<h6>表示
102 | // syncScroll.addArea(editArea, '.h1,.h2,.h3,.h4,.h5,.h6');
103 | // syncScroll.addArea(previewArea, 'h1,h2,h3,h4,h5,h6');
104 | syncScroll.addAreas([
105 | {
106 | area: editArea,
107 | queryCriteria: '.h1,.h2,.h3,.h4,.h5,.h6'
108 | },
109 | {
110 | area: previewArea,
111 | queryCriteria: 'h1,h2,h3,h4,h5,h6'
112 | }
113 | ]);
114 | // 可以调用`addArea`单个添加,在`addArea`调用后,需要手动调用`update`更新数据
115 | // syncScroll.addArea({
116 | // area: editArea,
117 | // queryCriteria: '.h1,.h2,.h3,.h4,.h5,.h6'
118 | // });
119 | // syncScroll.addArea({
120 | // area: previewArea,
121 | // queryCriteria: 'h1,h2,h3,h4,h5,h6'
122 | // });
123 | // syncScroll.update();
124 |
125 | ```
126 |
127 |
380 | ```
381 | // sync-scroll被占用了,没得办法.jpg
382 | npm install md-sync-scroll --save
383 | ```
384 |
385 |
387 | ```markdown
388 | // 片段start
389 | # 这表示一个片段的开始
390 | 这些是片段内容
391 | 这些也是片段内容
392 | // 片段end
393 | ```
394 |
395 |
399 | ```markdown
400 | # 文章标题
401 | 标题下的内容...
402 | # 文章标题2
403 | 标题下的内容2...
404 | ...
405 | ```
406 |
407 |
427 | ```js
428 | import {SyncScroll, ConfigOptions} from "md-sync-scroll";
429 | ```
430 |
431 |
436 | ```js
437 | import {SyncScroll, ConfigOptions} from "md-sync-scroll";
438 |
439 | const editArea = document.getElementById('edit');
440 | const previewArea = document.getElementById('preview');
441 | // 通过ConfigOptions可以配置参数,详细信息见下文`API`
442 | const options = ConfigOptions.instance({
443 | syncWithClick: true,
444 | offsetScroll: 100
445 | });
446 | const syncScroll = new SyncScroll(options);
447 |
448 | // 对于本页面来说,我用`h1-6`指示片段的开始,那么我就要查询被我指定为`h1-h6`的元素
449 | // 在左边我用class='h1-6'标记,在右边用<h1>-<h6>表示
450 | // syncScroll.addArea(editArea, '.h1,.h2,.h3,.h4,.h5,.h6');
451 | // syncScroll.addArea(previewArea, 'h1,h2,h3,h4,h5,h6');
452 | syncScroll.addAreas([
453 | {
454 | area: editArea,
455 | queryCriteria: '.h1,.h2,.h3,.h4,.h5,.h6'
456 | },
457 | {
458 | area: previewArea,
459 | queryCriteria: 'h1,h2,h3,h4,h5,h6'
460 | }
461 | ]);
462 | // 可以调用`addArea`单个添加,在`addArea`调用后,需要手动调用`update`更新数据
463 | // syncScroll.addArea({
464 | // area: editArea,
465 | // queryCriteria: '.h1,.h2,.h3,.h4,.h5,.h6'
466 | // });
467 | // syncScroll.addArea({
468 | // area: previewArea,
469 | // queryCriteria: 'h1,h2,h3,h4,h5,h6'
470 | // });
471 | // syncScroll.update();
472 |
473 | ```
474 |
475 |