從原理到實現動態簡歷

Serendipity96發表於2019-03-02

先睹為快

1. 原理

var number = `1234567890`
var n = 0
var timerId = setInterval(()=>{
  n++;
  document.body.innerText = number.slice(0,n)
  // 或者 document.body.innerText = number.substring(0,n)
  if(n>=10){
    clearInterval(timerId)
  }
},500)
複製程式碼

假設有 10 個數字,初始時 n = 0,螢幕上一個數字都沒有,每隔 500 ms多出現一個數,用 setInterval 來做定時,用 slice 或 substring 控制多出現的數,當 n ≥ 10 時 ,說明數字已經全部出現,clearInterval。

從原理到實現動態簡歷

思考 1

如果我想寫每隔 500ms 列印的是 CSS 語句怎麼辦?

var content = `body{
  background:red
}`

var n = 0
var timerId = setInterval(()=>{
  n++;
  document.body.innerText = content.slice(0,n)
  if(n>=content.length){
    clearInterval(timerId)
  }
},200)
複製程式碼
從原理到實現動態簡歷

列印出來怎麼是這個效果?background 前面的的縮排怎麼消失了?因為 HTML 會把大於等於兩個的空格或者縮排都變成一個空格。

解決

在 CSS 中有一個 <pre> </pre>標籤,pre 是 preview 預覽的意思。在頁面中加一個 <pre> </pre>標籤。

HTML

<body>
  <pre id="code"></pre>
</body>
複製程式碼

CSS

var content = `body{
  background:red;
}`
var n = 0
var timerId = setInterval(()=>{
  n++;
  code.innerText = content.slice(0,n)
  if(n>=content.length){
    clearInterval(timerId)
  }
},100)
複製程式碼

效果

從原理到實現動態簡歷

思考2

寫進去的程式碼怎麼生效呢?我的背景並沒有變成紅色。

解決

當頁面中動態的 CSS 程式碼寫完的時候,同時在 <style> 標籤中加同樣一句話,這樣就好像是寫的程式碼生效了。

HTML

<head>
  <meta charset="utf-8">
  <title>JS Bin</title>
  <style id="styleTag"></style>
</head>
<body>
  <pre id="code"></pre>
</body>
複製程式碼

在 head 裡給 style 一個 id。

CSS

var content = `body{
  background:red;
}`

var n = 0
var timerId = setInterval(()=>{
  n++;
  code.innerText = content.slice(0,n)
  if(n>=content.length){
    styleTag.innerText = content
    clearInterval(timerId)
  }
},1000)
複製程式碼
從原理到實現動態簡歷

2. 寫簡歷啦

1. 準備一些文字介紹

var content = `
 面試官你好,我是XXX
 只用文字作做我介紹太單調了
 我就用程式碼來介紹吧
 首先準備一些樣式

 body{
  background:red;
 }`

var n = 0
var timerId = setInterval(()=>{
  n++;
  code.innerText = content.slice(0,n)
  if(n>=content.length){
    styleTag.innerText = content
    clearInterval(timerId)
  }
},100)
複製程式碼
從原理到實現動態簡歷

問題

誒???CSS 樣式怎麼不生效了?

原因

因為<style>的標籤裡有文字,CSS 沒有辦法解析程式碼,顯示樣式。

解決

以註釋的形式新增文字。

var content = `
/* 
 * 面試官你好,我是XXX
 * 只用文字作做我介紹太單調了
 * 我就用程式碼來介紹吧
 * 首先準備一些樣式
 */
body{
  background:red;
}`

var n = 0
var timerId = setInterval(()=>{
  n++;
  code.innerText = content.slice(0,n)
  if(n>=content.length){
    styleTag.innerText = content
    clearInterval(timerId)
  }
},100)
複製程式碼
從原理到實現動態簡歷

2. 給動畫加一點過渡

CSS

*{
  transition: all 1s;
}
複製程式碼

3. 加個邊框

#code{
  border:1px solid red;
  padding:16px;
}
複製程式碼

4. 給標籤加高亮

原理

偷樑換柱

var timerId = setInterval(() => {
  n++;
  code.innerText = content.slice(0, n)
  code.innerText = code.innerText.replace(`body`,`<span style="color:red">body</span>`)
  if (n >= content.length) {
    styleTag.innerText = content
    clearInterval(timerId)
  }
}, 100)
複製程式碼

利用 span 標籤樣式,替換沒有樣式的 CSS。

注意:如果想要識別替換的標籤生效,需要把 innerText 改成 innerHTML,對比結果如下。

從原理到實現動態簡歷
var n = 0
var timerId = setInterval(() => {
  n++;
  code.innerHTML = content.slice(0, n)
  code.innerHTML = code.innerHTML.replace(`body`,`<span style="color:red">body</span>`)
  if (n >= content.length) {
    styleTag.innerText = content
    clearInterval(timerId)
  }
}, 100)
複製程式碼
從原理到實現動態簡歷

思考

難道我要全部手寫一遍高亮語法替換?

解決

呼叫別人的庫 Prism ,引入 CSS 和 JavaScript。

code.innerHTML = 
code.innerHTML.replace(`body`,`<span style="color:red">body</span>`)
複製程式碼

呼叫庫函式來實現高亮,這個庫的原理就是加 span 標籤來實現高亮

code.innerHTML = 
Prism.highlight(code.innerHTML, Prism.languages.css, `css`);
複製程式碼
從原理到實現動態簡歷

再次偷樑換柱

讓別人以為這個高亮是因為我寫了高亮這部分的程式碼而實現的

解決

從原理到實現動態簡歷

庫裡的高亮選擇器如上圖,這就提示我們可以通過覆蓋再復原來實現。

  1. 引入一個 CSS 檔案,和庫裡高亮選擇器命名相同,樣式用黑色先覆蓋。
  2. 在 JavaScript 的檔案裡寫選擇器樣式,把剛剛的黑色替換掉。

注意

CSS 引入的順序,用自己寫的樣式去覆蓋庫裡的樣式。

<link rel="stylesheet" href="vendor/prism/prism.css">
<link rel="stylesheet" href="css/default.css">
複製程式碼

default.css

.token.selector{
    color: black;
}
.token.property{
    color: black;
}
複製程式碼

JavaScript

/* 給程式碼加個高亮吧 */
.token.selector{ 
    color: #690; 
}
.token.property{ 
    color: #905; 
}
複製程式碼
從原理到實現動態簡歷

這樣就讓別人感覺是因為高亮部分的程式碼使螢幕中顯示的程式碼高亮了。

5. 加個 3D 效果

/* 加點 3D 效果 */
#code{
    transform: rotate(360deg);
}
複製程式碼

6. 再來一張白紙

function fn2() {
    var paper = document.createElement(`div`)
    paper.id = `paper`
    document.body.appendChild(paper)
}

function fn3(preContent) {
    var contentPaper = `
    #paper{
        width:100px;
        height:100px;
        background:red;
    }
    `
    var n = 0
    var timerId = setInterval(() => {
        n++;
        code.innerHTML = preContent + contentPaper.substring(0, n)
        code.innerHTML = 
            Prism.highlight(code.innerHTML, Prism.languages.css, `css`);
        styleTag.innerText = preContent + contentPaper.substring(0, n)
        if (n >= contentPaper.length) {
            clearInterval(timerId)
        }
    }, 10)
}
複製程式碼

在第一個 clearInterval 後執行 fn2 和 fn3。

var content = `...註釋暫時省略...`

var n = 0
var timerId = setInterval(() => {
    n++;
    code.innerHTML = content.slice(0, n)
    code.innerHTML = 
        Prism.highlight(code.innerHTML, Prism.languages.css, `css`);
    styleTag.innerText = content.slice(0, n)
    if (n >= content.length) {
        clearInterval(timerId)
        fn2()
        fn3(content)
    }
}, 10)
複製程式碼

注意

要把 content 的內容傳給 fn3 ,否則 fn3 內容會覆蓋前面的效果,而不是追加在後。

糾正一個 bug

styleTag.innerText = content.slice(0, n) 替換 styleTag.innerText = content

前一句表示一邊寫程式碼一遍展示效果,後一句表示當所有的樣式程式碼寫完之後才新增進來,這樣展示的效果有延遲。

效果

從原理到實現動態簡歷

程式碼優化

var content = `
/* 
 * 面試官你好,我是XXX
 * 只用文字作做我介紹太單調了
 * 我就用程式碼來介紹吧
 * 首先準備一些樣式
 */

*{
    transition: all 1s;
}
body{
    background:#eee;
}
#code{
    border:1px solid red;
    padding:16px;
}
/* 給程式碼加個高亮吧 */
.token.selector{ 
    color: #690; 
}
.token.property{ 
    color: #905; 
}
/* 加點 3D 效果 */
#code{
    transform: rotate(360deg);
}
/* 接下來我需要一張白紙 */
`

var contentPaper = `
    #paper{
        width:100px;
        height:100px;
        background:red;
    }`

writeCode(``, content, () => {
    createPaper(() => {
        writeCode(content, contentPaper)
    })
})

function writeCode(prefix, code, fn) {
    let domCode = document.querySelector(`#code`)
    domCode.innerHTML = prefix || ``
    let n = 0
    let timerId = setInterval(() => {
        n++;
        domCode.innerHTML =
            Prism.highlight(prefix + code.substring(0, n), 
            Prism.languages.css, `css`);
        styleTag.innerHTML = prefix + code.substring(0, n)
        if (n >= code.length) {
            clearInterval(timerId)
            fn().call()
        }
    },50)
}

function createPaper(fn) {
    var paper = document.createElement(`div`)
    paper.id = `paper`
    document.body.appendChild(paper)
    fn.call()
}

複製程式碼

1. 為什麼要使用回撥?

如果不使用,執行順序:

  1. 執行 writeCode ,setInterval 定時,然後返回
  2. 執行 fn2
  3. 時間到,執行 setInterval

這是非同步啊,emmmm,什麼是非同步?

生活中的例子:

同步:讓黃牛去買票,然後我站著等。

非同步:讓黃牛去買票(告訴黃牛買到票就打電話給我),然後我去做別的事。

非同步不等結果,直接進行下一步。

這時可以採用回撥來解決,回撥是拿到非同步結果的一種方式。回撥也可以拿同步結果。

2. prefix 是幹什麼的?

最開始 domCode 裡面存的是空字串,一邊寫樣式一邊存第一次樣式程式碼,接下來建立 paper 的 div,建立完成後繼續寫樣式,是在第一次的樣式上追加,否則第二次樣式覆蓋第一次樣式程式碼。

7. 樣式程式碼自動往下滾

default.css

#code{
    height: 100vh;
    overflow: hidden;
}
複製程式碼

在 writeCode( ) 的styleTag.innerHTML 下面加 domCode.scrollTop = domCode.scrollTop = domCode.scrollHeight,只要高度變高了,能拉多長拉多長。

styleTag.innerHTML = prefix + code.substring(0, n)
domCode.scrollTop = domCode.scrollTop = domCode.scrollHeight
複製程式碼
從原理到實現動態簡歷

8. 調整紙張樣式

JavaScript

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()
}

/* 接下來我需要一張白紙 */
#code{
    position: fixed;
    left: 0;
    width: 50%;
    height: 100%;
}
#paper{
    padding: 16px;
    position: fixed;
    right: 0;
    width: 50%;
    height: 100%;
    background: #ddd;
    display: flex;
    justify-content: center;
    align-items: center;
}
#paper > content {
    background: white;
    width: 100%;
    height: 100%;
}
複製程式碼

未完待續…

相關文章