本身動手開發更好用的markdown編輯器-04(實時預覽)

這裏文章都是從我的的github博客直接複製過來的,排版可能有點亂. 原始地址  http://benq.im/2015/04/25/hexomd-04/
 
程序打包
 
文章目錄
  1. 1. 打開新窗口
  2. 2. 預覽功能
  3. 3. 優化體驗
    1. 3.1. 滾動條隨動
    2. 3.2. 樣式美化
    3. 3.3. 代碼塊高亮
    4. 3.4. 關閉主程序前先自動關閉預覽窗口
  4. 4. 總結
  5. 5. 附件

上一篇咱們實現了系統模塊的一些功能,對angular的使用更深刻了一點.javascript

今天這篇咱們要實現實時預覽的功能,將學習到如何使用nw.js打開額外新窗口,窗口之間如何通訊,並將引入新的開源框架marked,用於markdown的解析.css

打開新窗口

預覽的功能我將在編輯器以外的新窗口裏實現,由於我日常都習慣使用雙顯示器,這樣能把預覽放在另外一個顯示器.html

先在studio/views裏新增preview.html,做爲預覽的窗口頁面.java

1
2
3
4
5
6
7
8
9
10
11
12
13
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>預覽</title>
</head>
<body>
<article class="markdown-body" id="content">
</article>
<script src="../../../lib/jquery-2.1.3.js"></script>
<script src="../preview.js"></script>
</body>
</html>

 

studio/directives.js裏增長打開預覽窗口的directivenode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//預覽
studio.directive('studioPreview',function(){
return function($scope,elem){
$(elem[0]).on('click',function(){
var previewWinUrl = ('file:///' + require('path').dirname(process.execPath) + '/app/modules/studio/views/preview.html').replace(/\\/g,'/');
if (!hmd.previewWin) {
//開發時爲了方便調試,設置toolbar:true,發佈時設爲false.
hmd.previewWin = require('nw.gui').Window.open(previewWinUrl, {
position: 'center',
"toolbar": true,
"frame": true,
"width": 800,
"height": 600,
"min_width": 600,
"min_height": 400,
"icon": "app/img/logo.png"
});
//關閉的時候置空preivewWin變量
hmd.previewWin.on('close', function () {
hmd.previewWin = null;
this.close(true);
});
}
});
};
});

 

預覽窗口每次只能打開一個,因此打開以前會先判斷hmd.previewWin是否已存在,而且窗口關閉事件裏將hmd.previewWin置空.jquery

studio/views/studio.html裏綁定預覽按鈕git

1
2
3
...
<a studio-preview href="javascript://" class="btn btn-primary" title="預覽"><i class="glyphicon glyphicon-eye-open"></i></a>
...

 

這樣就實現了點擊預覽按鈕打開預覽窗口
github

預覽功能

markdown的解析我使用開源的marked.npm

安裝marked
打開命令行,進入app目錄,輸入安裝命令:markdown

1
npm install marked --save

 

editor.js增長markdown解析的方法,輸出當前編輯器內容解析後的結果.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
init: function (options,filepath) {
...
this.initMarked();
this.cm = CodeMirror.fromTextArea(el, options);
...
},
//初始化解析模塊
initMarked:function(){
this.marked = require('../app/node_modules/marked');
this.marked.setOptions({
renderer: new this.marked.Renderer(),
gfm: true,
tables: true,
breaks: false,
pedantic: false,
sanitize: true,
smartLists: true,
smartypants: false
});
},
//解析markdown
parse:function(){
return this.marked(this.cm.getValue());
},

 

這裏要注意的是this.marked = require('../app/node_modules/marked');,而不是直接require('marked'),這是由於nw.js的這個問題

修改directive

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//預覽
studio.directive('studioPreview',function(){
return function($scope,elem){

//修改文本時更新預覽,change事件觸發很是頻繁,因此這裏使用setTimeout防止無心義的頻繁解析.
var changeTimer;
hmd.editor.on('change',function(){
clearTimeout(changeTimer);
changeTimer = setTimeout(function(){
hmd.previewWin && hmd.previewWin.emit('change', hmd.editor.parse());
},200);
});
//打開文件時更新預覽
hmd.editor.on('setFiled',function(filepath){
hmd.previewWin && hmd.previewWin.emit('change', hmd.editor.parse());
});

$(elem[0]).on('click',function(){
//省略...
hmd.previewWin.on('loaded',function(){
hmd.previewWin && hmd.previewWin.emit('change', hmd.editor.parse());
});
hmd.previewWin.on('close', function () {
hmd.previewWin = null;
this.close(true);
});
}
});
};
});

 

咱們經過自定義事件emit('change', hmd.editor.parse())來與previewWin窗口通信. 在初始化窗口,打開文件,修改文件時都觸發窗口的change事件,將解析後的內容做爲事件參數傳遞.
新建腳本文件studio/preview.js,並在preview.html裏引用

1
2
3
4
var gui = require('nw.gui'), win = gui.Window.get();
win.on('change', function (mdHtml) {
$('#content').html(mdHtml);
});

 

preview.js裏監聽change事件,而後將解析後的內容直接顯示到頁面上.

優化體驗

如今已經能夠實時的預覽了,但功能仍是過於簡單,使用起來很不方便,這一節將優化預覽窗口的使用體驗.

滾動條隨動

若是文本太多致使出現滾動條,預覽窗口仍是會一直顯示在第一屏,並不會跟隨咱們在編輯器中的查看位置來實時的更新預覽的位置.咱們要看預覽還要手動去調整預覽窗口的滾動條高度,這樣的體驗徹底等於無法使用,所以如今來實現預覽窗口隨着編輯器的滾動條高度等比隨動.

codemirror已經實現了scroll事件,節省了咱們大量的工做量,這個框架的做者考慮的真是周到,不得不讚一下.
咱們在editor.jsscroll事件進行封裝.

1
2
3
4
//滾動事件
this.cm.on('scroll',function(cm){
me.fire('scroll',cm.getScrollInfo());
});

 

directive裏將編輯器滾動事件傳遞給預覽窗口

1
2
3
4
5
6
7
8
9
10
11
12
studio.directive('studioPreview',function(){
...
//編輯器滾動
var scrollTimer;
hmd.editor.on('scroll',function(scrollInfo){
clearTimeout(scrollTimer);
scrollTimer = setTimeout(function(){
hmd.previewWin && hmd.previewWin.emit('editorScroll',scrollInfo);
},200);
});
...
}

 

一樣的道理,咱們應該防止太頻繁的觸發

最後在preview.js裏響應editorScroll事件,並更新預覽頁面的滾動條高度

1
2
3
4
win.on('editorScroll',function(scrollInfo){
var scrollTop = $(document.body).height()*scrollInfo.top/scrollInfo.height;
$(document.body).scrollTop(scrollTop);
});

 

樣式美化

默認的無樣式界面看起來太不舒服了,如今來實現跟編輯器同樣的能夠選擇或者自定義的樣式.

咱們將預覽的樣式放在/app/css/previewtheme目錄下,先在裏面增長兩個測試用的樣式文件

增長預覽樣式設置
這個跟上一篇的編輯器樣式設置相似.

system/model.js增長默認配置

1
2
3
4
5
6
7
8
9
//默認設置
var defaultSystemData = {
//最後一次打開的文件
lastFile: null,
//編輯器樣式
theme:'ambiance',
//預覽窗口樣式
preViewTheme:'default'
};

 

system/views/system.html增長表單字段

1
2
3
4
5
6
7
8
9
10
11
<div class="content studio-wrap">
<form class="system-form" name="systemForm">
...
<div class="form-group">
<label>預覽樣式</label>
<select name="preViewTheme" ng-model="systemSetting.preViewTheme" ng-options="k as v for (k, v) in preViewThemes">
</select>
</div>
...
</form>
</div>

 

system/controllers.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var system = hmd.system,
fs = require('fs');
//讀取theme目錄,生成樣式列表
var readCssList = function(path){
var files = fs.readdirSync(path),themes={};
files.forEach(function (file) {
if(~file.indexOf('.css')){
file = file.replace('.css','');
themes[file] = file;
}
});
return themes;
};
system.controller('system', function ($scope) {
$scope.themes = readCssList('./app/lib/codemirror/theme');
$scope.preViewThemes = readCssList('./app/css/previewtheme');
$scope.systemSetting = system.get();
$scope.save = function (systemSetting) {
system.save(systemSetting);
};
});

 

將讀取目錄全部樣式文件生成鍵值對的代碼封裝成方法readCssList,而後增長$scope.preViewThemes綁定便可..

再一次感覺angular的方便.

應用樣式

預覽頁面加載成功後,經過事件setTheme將系統設置傳遞給預覽窗口

1
2
3
4
5
6
7
8
studio.directive('studioPreview',function(){
...
hmd.previewWin.on('loaded',function(){
hmd.previewWin.emit('setTheme',hmd.system.get());
hmd.previewWin && hmd.previewWin.emit('change', hmd.editor.parse());
});
...
});

 

preview.js

1
2
3
win.on('setTheme',function(setting){
$('head').append('<link href="../../../css/previewtheme/'+setting.preViewTheme+'.css" rel="stylesheet" />');
});

 

從網上找幾個經常使用的marddown樣式文件來看看效果,你能夠本身找或寫更多樣式.


代碼塊高亮

做爲一個碼農,寫的markdown文件裏都有好多代碼塊,確定要把代碼塊弄好看點.

安裝highlight.js

1
npm install highlight.js

 

安裝完成後,代碼高亮的樣式文件在目錄node_modules/highlight.js/styles/

在系統設置裏增長預覽代碼樣式設置,跟以前的預覽樣式相似,這裏直接上代碼,再也不重複描述了.

model.js

1
2
3
4
5
6
7
8
9
10
11
//默認設置
var defaultSystemData = {
//最後一次打開的文件
lastFile: null,
//編輯器樣式
theme:'ambiance',
//預覽窗口樣式
preViewTheme:'github',
//預覽代碼塊樣式
preViewHighLightTheme:'default'
};

 

system.html

1
2
3
4
5
6
7
...
<div class="form-group">
<label>代碼預覽樣式</label>
<select name="preViewHighLightTheme" ng-model="systemSetting.preViewHighLightTheme" ng-options="k as v for (k, v) in preViewHighLightThemes">
</select>
</div>
...

 

controllers.js

1
2
3
4
5
6
7
8
9
system.controller('system', function ($scope) {
$scope.themes = readCssList('./app/lib/codemirror/theme');
$scope.preViewThemes = readCssList('./app/css/previewtheme');
$scope.preViewHighLightThemes = readCssList('./app/node_modules/highlight.js/styles');
$scope.systemSetting = system.get();
$scope.save = function (systemSetting) {
system.save(systemSetting);
};
});

 

系統設置截圖

preview.js

1
2
3
4
win.on('setTheme',function(setting){
$('head').append('<link href="../../../node_modules/highlight.js/styles/' + setting.preViewHighLightTheme +'.css" rel="stylesheet" />');
$('head').append('<link href="../../../css/previewtheme/'+setting.preViewTheme+'.css" rel="stylesheet" />');
});

 

這樣就完成了,很簡單,沒幾行代碼.

關閉主程序前先自動關閉預覽窗口

如今還有個小問題,主程序關掉後,預覽窗口還在.

modules/directives.js

1
2
3
4
5
6
7
...
win.on('close', function () {
var me = this;
hmd.previewWin && hmd.previewWin.close();
me.close(true);
});
...

 

監聽主窗口的關閉事件,若是有預覽窗口,就先關閉預覽窗口再關閉本身

總結

如今咱們的markdown編輯器應該是挺好用的了,至少比一些在線的方便些,能夠很靈活的定製各類樣式.
作預覽這個功能的時候個人想法是:一個重要的功能,不只要實現基本功能,更重要的是完善體驗,太差的體驗跟沒有這個功能沒區別,所以咱們把時間都花在優化預覽的體驗上.與其增長10個不經常使用的功能,不如把最經常使用的一個功能作好.

最終效果截圖

主窗口


預覽窗口

附件

本篇程序打包
項目地址

相關文章
相關標籤/搜索