在網上看到一個這樣的網站,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'