<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta 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> <div> <p>{{ greeting }} world!</p> </div> </code> <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: ["*", ".", "?", "+", "$", "^", "[", "]", "{", "}", "|", "\\", "(", ")", "https://www.jb51.net/", "%", ":", "=", ';'] } 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>' + matchStrSpecial[k] + '</span>'); } for (var key in lightColorCode) { if (key === 'keywords') { lightColorCode[key].forEach(function (imp) { htmlStr = htmlStr.replace(new RegExp(imp, 'gim'), '<span>' + imp + '</span>') }) flag = true; } else if (key === 'importantObj') { lightColorCode[key].forEach(function (kw) { htmlStr = htmlStr.replace(new RegExp(kw, 'gim'), '<span>' + kw + '</span>') }) flag = true; } else if (key === 'method') { lightColorCode[key].forEach(function (mt) { htmlStr = htmlStr.replace(new RegExp(mt, 'gim'), '<span>' + mt + '</span>') }) flag = true; } } if (flag) { if (!!matchStrSpace) { spaceLen = matchStrSpace.length; } else { spaceLen = 0; return; } for(var i = 0;i < spaceLen;i++){ var curFont; if(window.innerWidth <= 1200){ curFont = '12px'; }else{ curFont = '14px'; } htmlStr = htmlStr.replace(matchStrSpace[i],'<span>' + matchStrSpace[i] + '</span>'); } el.innerHTML = htmlStr; } } var codes = document.querySelectorAll('.ew-code'); for (var i = 0, len = codes.length; i < len; i++) { setHighLight(codes[i]) } </script> </body> </html>
你可以狠狠点击此处 查看效果。
不过这里为了方便,我还是使用插件 Prism.js ,另外在这里,我们还要用到将一个普通文本打造成 HTML 网页的插件 marked.js 。
接下来分析如何暂停动画和继续动画,很简单,就是清除定时器,然后重新调用即可。如何让编辑的代码生效呢,这就需要用到自定义事件 .sync 事件修饰符,自行查看官网 vue.js 。
虽然这里用原生 js 也可以实现,但我们用 vue-cli 结合组件的方式来实现,这样更简单一些。好了,让我们开始吧:
新建一个 vue-cli 工程(步骤自行百度):
新建一个 styleEditor.vue 组件,代码如下:
<template> <div> <div v-html="codeInstyleTag"></div> <div ref="container" contenteditable="true" @input="updateCode($event)" v-html="highlightedCode"></div> </div> </template> <script> import Prism from 'prismjs' export default { name:'Editor', props:['code'], computed:{ highlightedCode:function(){ //代码高亮 return Prism.highlight(this.code,Prism.languages.css); }, // 让代码生效 codeInstyleTag:function(){ return `<style>${this.code}</style>` } }, methods:{ //每次打字到最底部,就要滚动 goBottom(){ this.$refs.container.scrollTop = 10000; }, //代码修改之后,可以重新生效 updateCode(e){ this.$emit('update:code',e.target.textContent); } } } </script> <style scoped> .code{ display:none; } </style>
新建一个 resumeEditor.vue 组件,代码如下: