<template> <div class = "resumeEditor" :class="{htmlMode:enableHtml}" ref = "container"> <div v-if="enableHtml" v-html="result"></div> <pre v-else>{{result}}</pre> </div> </template> <script> import marked from 'marked' export default { props:['markdown','enableHtml'], name:'ResumeEditor', computed:{ result:function(){ return this.enableHtml ? marked(this.markdown) : this.markdown } }, methods:{ goBottom:function(){ this.$refs.container.scrollTop = 10000 } } } </script> <style scoped> .htmlMode{ anmation:flip 3s; } @keyframes flip{ 0%{ opactiy:0; } 100%{ opactiy:1; } } </style>
新建一个底部导航菜单组件 bottomNav.vue ,代码如下:
<template> <div> <a @click="pauseFun">{{ !paused ? '暂停动画' : '继续动画 ||' }}</a> <a @click="skipAnimationFun">跳过动画</a> <p> <span v-for="(url,index) in demourl" :key="index"> <a :href="url.url" >{{ url.title }}</a> </span> </p> <div @click="musicPause" :class="playing ? 'rotate' : ''" ref="music"></div> </div> </template> <script> export default{ name:'bottom', data(){ return{ demourl:[ {url:'http://eveningwater.com/',title:'个人网站'}, {url:'https://github.com/eveningwater',title:'github'} ], paused:false,//暂停 playing:false,//播放图标动画 autoPlaying:false,//播放音频 audio:'' } }, mounted(){ }, methods:{ // 播放音乐 playMusic(){ this.playing = true; this.autoPlaying = true; // 创建audio标签 this.audio = new Audio(); this.audio.src = "http://eveningwater.com/project/newReact-music-player/audio/%E9%BB%84%E5%9B%BD%E4%BF%8A%20-%20%E7%9C%9F%E7%88%B1%E4%BD%A0%E7%9A%84%E4%BA%91.mp3"; this.audio.loop = 'loop'; this.audio.autoplay = 'autoplay'; this.$refs.music.appendChild(this.audio); }, // 跳过动画 skipAnimationFun(e){ e.preventDefault(); this.$emit('on-skip'); }, // 暂停动画 pauseFun(e){ e.preventDefault(); this.paused = !this.paused; this.$emit('on-pause',this.paused); }, // 暂停音乐 musicPause(){ this.playing = !this.playing; if(!this.playing){ this.audio.pause(); }else{ this.audio.play(); } } } } </script> <style scoped> #bottom{ position:fixed; bottom:5px; left:0; right:0; } #bottom p{ float:right; } #bottom a{ text-decoration: none; color: #999; cursor:pointer; margin-left:5px; } #bottom a:hover,#bottom a:active{ color: #010a11; } </style>
接下来是核心 APP.vue 组件代码:
<template> <div> <div> <StyleEditor ref="styleEditor" v-bind.sync="currentStyle"></StyleEditor> <ResumeEditor ref="resumeEditor" :markdown = "currentMarkdown" :enableHtml="enableHtml"></ResumeEditor> </div> <BottomNav ref ="bottomNav" @on-pause="pauseAnimation" @on-skip="skipAnimation"></BottomNav> </div> </template> <script> import ResumeEditor from './components/resumeEditor' import StyleEditor from './components/styleEditor' import BottomNav from './components/bottomNav' import './assets/common.css' import fullStyle from './style.js' import my from './my.js' export default { name: 'app', components: { ResumeEditor, StyleEditor, BottomNav }, data() { return { interval: 40,//写入字的速度 currentStyle: { code: '' }, enableHtml: false,//是否打造成HTML网页 fullStyle: fullStyle, currentMarkdown: '', fullMarkdown: my, timer: null } }, created() { this.makeResume(); }, methods: { // 暂停动画 pauseAnimation(bool) { if(bool && this.timer){ clearTimeout(this.timer); }else{ this.makeResume(); } }, // 快速跳过动画 skipAnimation(){ if(this.timer){ clearTimeout(this.timer); } let str = ''; this.fullStyle.map((f) => { str += f; }) setTimeout(() => { this.$set(this.currentStyle,'code',str); },100) this.currentMarkdown = my; this.enableHtml = true; this.$refs.bottomNav.playMusic(); }, // 加载动画 makeResume: async function() { await this.writeShowStyle(0) await this.writeShowResume() await this.writeShowStyle(1) await this.writeShowHtml() await this.writeShowStyle(2) await this.$nextTick(() => {this.$refs.bottomNav.playMusic()}); }, // 打造成HTML网页 writeShowHtml: function() { return new Promise((resolve, reject) => { this.enableHtml = true; resolve(); }) }, // 写入css代码 writeShowStyle(n) { return new Promise((resolve, reject) => { let showStyle = (async function() { let style = this.fullStyle[n]; if (!style) return; //计算出数组每一项的长度 let length = this.fullStyle.filter((f, i) => i <= n).map((it) => it.length).reduce((t, c) => t + c, 0); //当前要写入的长度等于数组每一项的长度减去当前正在写的字符串的长度 let prefixLength = length - style.length; if (this.currentStyle.code.length < length) { let l = this.currentStyle.code.length - prefixLength; let char = style.substring(l, l + 1) || ' '; this.currentStyle.code += char; if (style.substring(l - 1, l) === '\n' && this.$refs.styleEditor) { this.$nextTick(() => { this.$refs.styleEditor.goBottom(); }) } this.timer = setTimeout(showStyle, this.interval); } else { resolve(); } }).bind(this) showStyle(); }) }, // 写入简历 writeShowResume() { return new Promise((resolve, reject) => { let length = this.fullMarkdown.length; let showResume = () => { if (this.currentMarkdown.length < length) { this.currentMarkdown = this.fullMarkdown.substring(0, this.currentMarkdown.length + 1); let lastChar = this.currentMarkdown[this.currentMarkdown.length - 1]; let prevChar = this.currentMarkdown[this.currentMarkdown.length - 2]; if (prevChar === '\n' && this.$refs.resumeEditor) { this.$nextTick(() => { this.$refs.resumeEditor.goBottom() }); } this.timer = setTimeout(showResume, this.interval); } else { resolve() } } showResume(); }) } } } </script> <style scoped> #app { font-family: 'Avenir', Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } .main { position: relative; } html { min-height: 100vh; } * { transition: all 1.3s; } </style>