在网上看到一个这样的网站,STRML它的效果看着十分有趣,如下图所示:
这个网站是用`react.js`来写的,于是,我就想着用`vue.js`也来写一版,开始撸代码。
首先要分析打字的原理实现,假设我们定义一个字符串`str`,它等于一长串注释加`CSS`代码,并且我们看到,当`css`代码写完一个分号的时候,它写的样式就会生效。我们知道要想让一段`CSS`代码在页面生效,只需要将其放在一对`<style>`标签对中即可。比如:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
红色字体
<style>
body{ color:#f00; } </style> </body> </html>
你可以狠狠点击此处具体示例查看效果。
当看到打字效果的时候,我们不难想到,这是要使用`间歇调用(定时函数:setInterval())`或`超时调用(延迟函数:setTimeout())`加`递归`去模拟实现`间歇调用`。一个包含一长串代码的字符串,它是一个个截取出来,然后分别写入页面中,在这里,我们需要用到字符串的截取方法,如`slice(),substr(),substring()`等,选择用哪个截取看个人,不过需要注意它们之间的区别。好了,让我们来实现一个简单的这样打字的效果,如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<div id="result"></div>
<script>
var r = document.getElementById('result'); var c = 0; var code = 'body{background-color:#f00;color:#fff};' var timer = setInterval(function(){ c++; r.innerHTML = code.substr(0,c); if(c >= code.length){ clearTimeout(timer); } },50) </script> </body> </html>
你可以狠狠点击此处具体示例查看效果。好的,让我们来分析一下以上代码的原理,首先放一个用于包含代码显示的标签,然后定义一个包含代码的字符串,接着定义一个初始值为`0`的变量,为什么要定义这样一个变量呢?我们从实际效果中看到,它是一个字一个字的写入到页面中的。初始值是没有一个字符的,所以,我们就从第`0`个开始写入,`c`一个字一个字的加,然后不停的截取字符串,最后渲染到标签的内容当中去,当`c`的值大于等于了字符串的长度之后,我们需要清除定时器。定时函数看着有些不太好,让我们用超时调用结合递归来实现。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<div id="result"></div>
<script>
var r = document.getElementById('result'); var c = 0; var code = 'body{background-color:#f00;color:#fff};'; var timer; function write() { c++; r.innerHTML = code.substr(0, c); if (c >= code.length && timer) { clearTimeout(timer) } else { setTimeout(write, 50); } } write(); </script> </body> </html>
你可以狠狠点击此处具体示例查看效果。
好了,到此为止,算是实现了第一步,让我们继续,接下来,我们要让代码保持空白和缩进,这可以使用`<pre>`标签来实现,但其实我们还可以使用css代码的`white-space`属性来让一个普通的`div`标签保持这样的效果,为什么要这样做呢,因为我们还要实现一个功能,就是编辑它里面的代码,可以让它生效。更改一下代码,如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
<style>
#result {
white-space: pre-wrap; overflow: auto; } </style> </head> <body> <div id="result"></div> <script> var r = document.getElementById('result'); var c = 0; var code = ` body{ background-color:#f00; color:#fff; }`; var timer; function write() { c++; r.innerHTML = code.substr(0, c); if (c >= code.length && timer) { clearTimeout(timer) } else { setTimeout(write, 50); } } write(); </script> </body> </html>
你可以狠狠点击此处具体示例查看效果。
接下来,我们还要让样式生效,这很简单,将代码在`style`标签中写一次即可,请看:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
<style>
#result {
white-space: pre-wrap; overflow: auto; } </style> </head> <body> <div id="result"></div> <style id="myStyle"></style> <script> var r = document.getElementById('result'), t = document.getElementById('myStyle'); var c = 0; var code = ` body{ background-color:#f00; color:#fff; }`; var timer; function write() { c++; r.innerHTML = code.substr(0, c); t.innerHTML = code.substr(0, c); if (c >= code.length) { clearTimeout(timer); } else { setTimeout(write, 50); } } write(); </script> </body> </html>
你可以狠狠点击此处具体示例查看效果。
我们看到代码还会有高亮效果,这可以用正则表达式来实现,比如以下一个`demo`:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>代码编辑器</title>
<style>
* {
margin: 0; padding: 0; } .ew-code { tab-size: 4; -moz-tab-size: 4; -o-tab-size: 4; margin-left: .6em; background-color: #345; white-space: pre-wrap; color: #f2f2f2; text-indent: 0; margin-right: 1em; display: block; overflow: auto; font-size: 20px; border-radius: 5px; font-style: normal; font-weight: 400; line-height: 1.4; font-family: Consolas, Monaco, "宋体"; margin-top: 1em; } .ew-code span { font-weight: bold; } </style> </head> <body> <code class="ew-code"> <div id="app"> <p>{{ greeting }} world!</p> </div> </code> <code class="ew-code"> //定义一个javascript对象 var obj = { greeting: "Hello," }; //创建一个实例 var vm = new Vue({ data: obj }); /*将实例挂载到根元素上*/ vm.$mount(document.getElementById('app')); </code> <script> var lightColorCode = { importantObj: ['JSON', 'window', 'document', 'function', 'navigator', 'console', 'screen', 'location'], keywords: ['if', 'else if', 'var', 'this', 'alert', 'return', 'typeof', 'default', 'with', 'class', 'export', 'import', 'new' ], method: ['Vue', 'React', 'html', 'css', 'js', 'webpack', 'babel', 'angular', 'bootstap', 'jquery', 'gulp', 'dom' ], // special: ["*", ".", "?", "+", "$", "^", "[", "]", "{", "}", "|", "\\", "(", ")", "/", "%", ":", "=", ';'] } function setHighLight(el) { var htmlStr = el.innerHTML; //匹配单行和多行注释 var regxSpace = /(\/\/\s?[^\s]+\s?)|(\/\*(.|\s)*?\*\/)/gm, matchStrSpace = htmlStr.match(regxSpace), spaceLen; //匹配特殊字符 var regxSpecial = /[`~!@#$%^&.{}()_\-+?|]/gim, matchStrSpecial = htmlStr.match(regxSpecial), specialLen; var flag = false; if (!!matchStrSpecial) { specialLen = matchStrSpecial.length; } else { specialLen = 0; return; } for (var k = 0; k < specialLen; k++) { htmlStr = htmlStr.replace(matchStrSpecial[k], '<span style="color:#b9ff01;">' + matchStrSpecial[k] + '</span>'); } for (var key in lightColorCode) { if (key === 'keywords'