今天跟着方老師作了一個會動的簡歷,思路就是經過 JavaScript 代碼,利用定時器每次同時在 HTML 和 CSS 中輸入固定的字符達到實時代碼預覽的效果,其中用到了 prism.js 庫給代碼添加高亮,用了 marked.js 庫把 markdown 轉換成 HTML ,並在頁面中展現出來。javascript
效果預覽:Git Pagescss
代碼連接:GitHubhtml
是否是看上去以爲效果挺炫的~下面我就來整理一下這個項目的流程,思路以及踩過的坑。感受好長好多涉及的點要記...應該分幾篇寫仍是合成一篇?...算了先寫着先吧。java
先寫個 demo,可讓代碼出如今頁面上:git
能夠看到,咱們指定的字符串已經出如今頁面上了,可是,出現了兩個問題:github
1.頁面背景色並無發生變化面試
2.輸出字符串中的空格被壓縮了npm
解決方法:markdown
1.固然沒有啊,咱們只是把字符串寫進 HTML 中,CSS 中仍是沒有這些設置app
2.咱們使用 pre 包裹住字符串,讓他的空格得以保留
修改一下:把字符串寫入 HTML 中的同時,把字符串也寫進 CSS 中;使用 pre 標籤。
思路:使用 substring 獲取字符串的某個部分,使用 setInterval 每次輸入固定字符,並作判斷,當獲取的字符串下標超過字符串長度時,setInterval 中止。
因爲 JSbin 不能上傳文件,因此這裏往下就不用 JSbin 演示了。
使用 prism.js 庫給字體作高亮(沒錯,仍是使用 CRM 大法~):
1.下載 prism.js 的 CSS 和 JS 文件
2.引入文件到項目中
3.copy.run.modify
var result = ` body{ background-color: red; } ` var n = 0 var timer = setInterval(()=>{ n+=1 code.innerHTML = Prism.highlight(result.substring(0,n), Prism.languages.css) styleTag.innerHTML = result.substring(0,n) if(n>=result.length){ window.clearInterval(timer) } },100)
給代碼設置更多的樣式,包括經過使用 animation 營造呼吸效果,調整代碼框大小等等:
var result = ` /* * 面試官你好,我是XXX * 只用文字做作我介紹太單調了 * 我就用代碼來介紹吧 * 首先準備一些樣式 * */ * { transition: all 1s; } html { font-size: 16px; } .code-wrapper { width: 50%; left: 0; position: fixed; height: 100%; } /* 調整一下代碼框大小 */ #code { border:1px solid transparent; padding: 16px; overflow: hidden; } #code { left: 0; width: 100%; height:100%; } /* 讓代碼呼吸起來 */ #code{ animation: breathe 1s infinite alternate-reverse; } /* 給代碼加上一點點高亮 */ .token.comment { color: slategray; } .token.property { color: #f92672; } .token.selector { color: #a6e22e; } `
另外,設置代碼高亮有一個小竅門,即咱們先設置一個 default.css,把高亮的 CSS 隱藏起來(放到 prism.css 後面),而後再經過 setInterval 把高亮代碼設置回來,起到更好的視覺效果。
左邊是負責代碼輸出展現,右邊是咱們最後要寫入 Markdown 的地方,因此咱們要建立一個函數,建立一塊白板出來:
function createPaper(fn){ var paper = document.createElement('div') paper.id = "paper" var content = document.createElement('pre') content.className = "content" paper.appendChild(content) document.body.appendChild(paper) fn.call() }
在建立白板的時候,輸入字符串的任務要停下來,以後再繼續輸入字符串的任務。
如今封裝一下以前的函數:
function writeCode(prefix,code){ let domCode = document.querySelector('#code') domCode.innerHTML = prefix || '' let n = 0 let timer = setInterval(()=>{ n = n+1 domCode.innerHTML = Prism.highlight(prefix + code.substring(0,n), Prism.languages.css) //這句話的做用是:當代碼在被輸入到HTML中時,代碼框的滾動條能一直保持在最下方 domCode.scrollTop = domCode.scrollHeight styleTag.innerHTML = prefix + code.substring(0,n) if(n >= code.length){ window.clearInterval(timer) } },20) }
prefix 和 code 分別對應第一次須要輸入的字符串和本次須要輸入的字符串,若是沒有 prefix,則以前的 innerHTML 則會被新的字符串取代,而不是連起來。
因此咱們這裏的流程應該是:writeCode('',result) -> 添加白板 createPaper() -> 設置白板樣式 -> 在白板添加 Markdown
那咱們這樣寫行不行呢:
writeCode('',result) createPaper() writeMarkdown()
很遺憾,這樣是不行的,由於 writeCode() 中有 setInterval 計時器,因此他是一個異步函數,實際上流程就變成了這樣:
添加白板 createPaper() ? writeCode('',result) ? 設置白板樣式 ? 在白板添加 Markdown
下面先插播一下我對於 異步與回調 的理解。
如:
function setTime(fn){ setTimeout(()=>{ console.log(2) fn.call() },1000) } function cb(){ console.log(3) } setTime(cb) console.log(1)
按照代碼順序原本是先執行 setTime(cb),再執行 console.log(1),但因爲 setTime(cb) 是異步事件,不用等待他執行完成以後才執行後續代碼,因此 console.log(1) 就先執行了,1S 後才執行setTime(cb) 的內容。
function setTime(fn){ setTimeout(()=>{ console.log(2) fn.call() },1000) } function cb(){ console.log(‘異步任務執行完成,回調結束’) } setTime(cb)
如這段代碼中,函數 cb() 就被當作 回調函數 傳入 setTime() 中,當 setTime() 中的內容執行完以後,就執行函數 cb() 。
注意
同步事件/函數 也可使用回調
因此咱們應該使用回調函數,在writeCode() 結束的時候執行 createPaper()
function writeCode(prefix,code,fn){ let domCode = document.querySelector('#code') domCode.innerHTML = prefix || '' let n = 0 let timer = setInterval(()=>{ n = n+1 domCode.innerHTML = Prism.highlight(prefix + code.substring(0,n), Prism.languages.css) domCode.scrollTop = domCode.scrollHeight styleTag.innerHTML = prefix + code.substring(0,n) if(n >= code.length){ window.clearInterval(timer) fn.call() } },20) } writeCode('',result,()=>{ createPaper() })
由於所要輸入的地方不一樣,因此另寫一個函數,輸入 markdown:
function writeMarkdown(markdown,fn){ let domMarkdown = document.querySelector('#paper .content') let n = 0 let timer = setInterval(()=>{ n = n+1 domMarkdown.innerHTML = markdown.substring(0,n) domMarkdown.scrollTop = domMarkdown.scrollHeight if(n >= markdown.length){ window.clearInterval(timer) fn.call() } },20) } var result4 = ` /* 還差一點點 */ .markdown-body { padding: 16px; background-color: white; overflow: auto; } /* Done~ 簡歷完成啦~ */ ` var md = ` # 簡歷 我的簡歷 ... ` // 實際上就變成了: writeCode('',result,()=>{ createPaper(()=>{ writeMarkdown(md) }) })
咱們選用了比較討巧的方法:新建一個 div,而後把 div 的樣式設置好(主要用了github-markdown-css),div 內容設置成通過 marked.js 庫(怎麼使用在此不贅述了)處理後的 HTML,最後用這個 div 替換掉以前的白板。
function convertMarkdownToHtml(fn){ var div = document.createElement('div') div.className = 'html markdown-body' div.innerHTML = marked(md) let markdownContainer = document.querySelector('#paper > .content') markdownContainer.style = 'background-color:white' markdownContainer.replaceWith(div) fn && fn.call() } // 實際上最後的流程就變成了: writeCode('',result,()=>{ createPaper(()=>{ writeCode(result,result2,()=>{ writeMarkdown(md,()=>{ writeCode(result + result2,result3,()=>{ convertMarkdownToHtml(()=>{ writeCode(result + result2 + result3,result4,()=>{ console.log('Done') }) }) }) }) }) }) }) // 哈哈,恐怖吧?傳說中的回調地獄
縱觀整個項目下來,感受異步和回調不難,反而在 CSS 方面耗費了點時間...
行文不順暢,這個..這個..這個我也在慢慢改進,我以前還在想着,是把作這個東西的整個流程都記錄下來,仍是隻挑其中比較重要的知識點,寫着寫着,仍是寫下了含糊不清的這篇東西...不過也算是有所收穫,多總結總結仍是不虧的~