[JavaScript]編寫一份會動的簡歷


編寫會動的簡歷

今天跟着方老師做了一個會動的簡歷,思路就是通過 JavaScript 代碼,利用定時器每次同時在 HTML 和 CSS 中輸入固定的字符達到實時代碼預覽的效果,其中用到了 prism.js 庫給代碼添加高亮,用了 marked.js 庫把 markdown 轉換成 HTML ,並在頁面中展示出來。

效果預覽:Git Pages

代碼鏈接:GitHub

是不是看上去覺得效果挺炫的~下面我就來整理一下這個項目的流程,思路以及踩過的坑。感覺好長好多涉及的點要記...應該分幾篇寫還是合成一篇?...算了先寫着先吧。


分別設置 HTML 和 CSS

先寫個 demo,可以讓代碼出現在頁面上:

輸出

可以看到,我們指定的字符串已經出現在頁面上了,但是,出現了兩個問題:

1.頁面背景色並沒有發生變化

2.輸出字符串中的空格被壓縮了

解決方法:

1.當然沒有啊,我們只是把字符串寫進 HTML 中,CSS 中還是沒有這些設置

2.我們使用 pre 包裹住字符串,讓他的空格得以保留

修改一下:把字符串寫入 HTML 中的同時,把字符串也寫進 CSS 中;使用 pre 標簽。

輸出正確


使用 setInterval 和 substring 進行逐個字符輸出

思路:使用 substring 獲取字符串的某個部分,使用 setInterval 每次輸入固定字符,並做判斷,當獲取的字符串下標超過字符串長度時,setInterval 停止。

輸出成功


使用 prism.js 設置 CSS 高亮

由於 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)

more style

給代碼設置更多的樣式,包括通過使用 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

因為所要輸入的地方不同,所以另寫一個函數,輸入 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)
    })
})

最后把 Markdown 轉換成 HTML

我們選用了比較討巧的方法:新建一個 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 方面耗費了點時間...

行文不暢,這個..這個..這個我也在慢慢改進,我之前還在想着,是把做這個東西的整個流程都記錄下來,還是只挑其中比較重要的知識點,寫着寫着,還是寫下了含糊不清的這篇東西...不過也算是有所收獲,多總結總結還是不虧的~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM