今天看啥  ›  专栏  ›  Serendipity96

从原理到实现动态简历

Serendipity96  · 掘金  ·  · 2018-04-03 02:53

从原理到实现动态简历

先睹为快

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%;
}

未完待续...




原文地址:访问原文地址
快照地址: 访问文章快照