今天跟着方老师作了一个会动的简历,思路就是经过 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 方面耗费了点时间...
行文不顺畅,这个..这个..这个我也在慢慢改进,我以前还在想着,是把作这个东西的整个流程都记录下来,仍是只挑其中比较重要的知识点,写着写着,仍是写下了含糊不清的这篇东西...不过也算是有所收获,多总结总结仍是不亏的~