先睹为快

如下图,代码在自己一行一行写程序,逐渐画出一个喜气灯笼的模样(PC移动端都支持噢),想不想知道是它怎么实现的呢?和胖头鱼一起来探究一番吧O(∩_∩)O~

你也可以直接点击 用程序自动画了一个灯笼(https://qianlongo.github.io/juejin-activities/dist/index.html#/new-year-2022) 体验一番,胖头鱼的掘金活动仓库查看源码(https://github.com/qianlongo/juejin-activities)

111111.gif

原理探究

这个效果就好像一个打字员在不断地录入文字,页面呈现动态效果。又好像一个早已经录制好影片,而我们只是坐在放映机前观看。

原理本身也非常简单,只要你会一点点前端知识,就可以马上亲手做一个出来。

1. 滚动的代码

定时器字符累加: 相信聪明的你早已经猜到屏幕中滚动的htmlcss代码就是通过启动一个定时器,然后将预先准备好的字符,不断累加到一个pre标签中。

2. 灯笼的布局

动态添加html片段css片段一张静态网页由htmlcss组成,灯笼能不断地发生变化,背后自然是组成灯笼的htmlcss不断变化的结果。

3. 例子解释

想象一下你要往一张网页每间隔0.1秒增加一个字,是不是开个定时器,间断地往body里面塞,就可以啊!没错,做到这一步就完成了原理的第一部分

再想象一下,在往页面里面塞的时候,我还想改变啊字的字体颜色以及网页背景颜色,那应该怎么做呢,是不是执行下面的代码就可以呢?

.xxx{color: blue;background: red;
}

没错,只不过更改字体和背景色不是突然改变的,而是开个定时器,间断地往style标签中塞入以下代码,这样就完成了原理的第二步,是不是好简单 , 接下来让我们一步步完成它。

简要解析

1.编辑器布局

工欲善其事,必先利其器。在实现代码自己画画的前提是有个类似编辑器地方给他show,所以会有编辑htmlcss和预览三个区域。

移动端布局

上下结构布局,上面是htmlcss的编辑区域,下面的灯笼的展示区域

1111-1.png

PC端布局

左右结构布局,左边是htmlcss的编辑区域,右边是灯笼的展示区域

1111-2.png

模板

<template><div :class="containerClasses"><div class="edit"><div class="html-edit" ref="htmlEditRef"><!-- 这是html代码编辑区域 --><pre v-html="htmlEditPre" ref="htmlEditPreRef"></pre></div><div class="css-edit" ref="cssEditRef"><!-- 这是css代码编辑区域 --><pre v-html="styleEditPre"></pre></div></div><div class="preview"><!-- 这是预览区域,灯笼最终会被画到这里噢 --><div class="preview-html" v-html="previewHtmls"></div><!-- 这里是样式真正起作用的地方,密码就隐藏... --><div v-html="previewStyles"></div></div></div>
</template>

端控制

简单的做一下移动端和PC端的适配,然后通过样式去控制布局即可

computed: {
containerClasses () {// 做一个简单的适配return ['container',isMobile() ? 'container-mobile' : '']
}
}

2.代码高亮

示例中的代码高亮是借助prismjspre进行转化处理的,只需要填充你想要高亮的代码,以及选择高亮的语言就可以实现上述效果。

// 核心代码,只有一行
this.styleEditPre = Prism.highlight(previewStylesSource, Prism.languages.css)

3. 灯笼布局实现

要实现灯笼不断变化的布局,需要两个东西,一个是灯笼本身的html元素还有就是控制html样式的css

通过preview-html``承载html片段,通过previewStyles承载由style标签包裹的css样式

// 容器
<div class="preview"><!-- 这是预览区域,灯笼最终会被画到这里噢 --><div class="preview-html" v-html="previewHtmls"></div><!-- 这里是样式真正起作用的地方 --><div v-html="previewStyles"></div>
</div>

逻辑代码

// 样式控制核心代码
this.previewStyles = `<style>${previewStylesSource}</style>
`
// html控制核心代码
this.previewHtmls = previewHtmls

4. 代码配置预览

我们通过一个个步骤将代码按阶段去执行,而代码本身是通过两个文件进行配置的,一个是控制html的文件,一个是控制css的文件。每一个步骤都是数组的一项

4.1 html配置

注意下面的代码格式是故意弄成这种格式的,并非是没有对齐

export default [// 开头寒暄`<!-- XDM好,我是前端胖头鱼~~~听说掘金又在搞活动了,奖品还很丰厚...我能要那个美腻的小姐姐吗?-->`,// 说明主旨`<!-- 以前都是用“手”写代码,今天想尝试一下“代码写代码”,自动画一个喜庆的灯笼-->  `,// 创建编辑器`<!-- 第①步,先创建一个编辑器-->  `,// 创建编辑器html结构` <div class="container"><div class="edit"><div class="html-edit"><!-- 这是html代码编辑区域 --><pre v-html="htmlEditPre"><!-- htmlStep0 --></pre></div><div class="css-edit"><!-- 这是css代码编辑区域 --><pre v-html="cssEditPre"></pre></div></div><div class="preview"><!-- 这是预览区域,灯笼最终会被画到这里噢 --><div class="preview-html"></div><!-- 这里是样式真正起作用的地方,密码就隐藏... --><div v-html="cssEditPre"></div></div></div>`,// 开始画样式`<!-- 第②步,给编辑器来点样式,我要开始画了喔~~-->  `,// 画灯笼的大肚子`<!-- 第③步,先画灯笼的大肚子结构 --><div class="lantern-container"><!-- htmlStep1 --><!-- 大红灯笼区域 --><div class="lantern-light"><!-- htmlStep2 --></div></div>`,// 提着灯笼的线`<!-- 第④步,灯笼顶部是有根线的 --><div class="lantern-top-line"></div>`,`<!-- 第⑤步,给灯笼加两个盖子 --><div class="lantern-hat-top"></div><div class="lantern-hat-bottom"></div><!-- htmlStep3 -->`,`<!-- 第⑥步,感觉灯笼快要成了,再给他加上四根线吧 --><div class="lantern-line-out"><div class="lantern-line-innner"><!-- htmlStep5 --></div></div><!-- htmlStep4 -->`,`<!-- 第⑦步,灯笼是不是还有底部的小尾巴呀 --><div class="lantern-rope-top"><div class="lantern-rope-middle"></div><div class="lantern-rope-bottom"></div></div>`,`<!-- 第⑧步,最后当然少不了送给大家的福啦 --><div class="lantern-fu">福</div>`]

4.2 css配置

export default [// 0. 添加基本样式`/* 首先给所有元素加上过渡效果 */* {transition: all .3s;-webkit-transition: all .3s;}/* 白色背景太单调了,我们来点背景 */html {color: rgb(222,222,222); background: rgb(0,43,54); }/* 代码高亮 */.token.selector{ color: rgb(133,153,0); }.token.property{ color: rgb(187,137,0); }.token.punctuation{ color: yellow; }.token.function{ color: rgb(42,161,152); }`,// 1.创建编辑器本身的样式`/* 我们需要做一个铺满全屏的容器 */.container{width: 100%;height: 100vh;display: flex;justify-content: space-between;align-items: center;}/* 代码编辑区域50%宽度,留一些空间给预览区域 */.edit{width: 50%;height: 100%;background-color: #1d1f20;display: flex;flex-direction: column;justify-content: space-between;}.html-edit, .css-edit{flex: 1;overflow: scroll;padding: 10px;}.html-edit{border-bottom: 5px solid #2b2e2f;}/* 预览区域有50%的空间 */.preview{flex: 1;height: 100%;background-color: #2f1f47;}.preview-html{display: flex;align-items: center;justify-content: center;height: 100%;}/* 好啦~ 你应该看到一个编辑器的基本感觉了,我们要开始画灯笼咯 */`,// 2`/* 给灯笼的大肚子整样式 */.lantern-container {position: relative;}.lantern-light {position: relative;width: 120px;height: 90px;background-color: #ff0844;border-radius: 50%;box-shadow: -5px 5px 100px 4px #fa6c00;animation: wobble 2.5s infinite ease-in-out;transform-style: preserve-3d;}/* 让他动起来吧 */@keyframes wobble {0% {transform: rotate(-6deg);}50% {transform: rotate(6deg);}100% {transform: rotate(-6deg);}}`,// 3`/* 顶部的灯笼线 */.lantern-top-line {width: 4px;height: 50px;background-color: #d1bb73;position: absolute;left: 50%;transform: translateX(-50%);top: -20px;border-radius: 2px 2px 0 0;}`,// 4`/* 灯笼顶部、底部盖子样式 */.lantern-hat-top,.lantern-hat-bottom {content: "";position: absolute;width: 60px;height: 12px;background-color: #ffa500;left: 50%;transform: translateX(-50%);}/* 顶部位置 */.lantern-hat-top {top: -8px;border-radius: 6px 6px 0 0;}/* 底部位置 */.lantern-hat-bottom {bottom: -8px;border-radius: 0 0 6px 6px;}`,// 5`/* 灯笼中间的线条 */.lantern-line-out,.lantern-line-innner {height: 90px;border-radius: 50%;border: 2px solid #ffa500;background-color: rgba(216, 0, 15, 0.1);}/* 线条外层 */.lantern-line-out {width: 100px;margin: 12px 8px 8px 10px;}/* 线条内层 */.lantern-line-innner {margin: -2px 8px 8px 26px;width: 45px;display: flex;align-items: center;justify-content: center;}`,// 6`/* 灯笼底部线条 */.lantern-rope-top {width: 6px;height: 18px;background-color: #ffa500;border-radius: 0 0 5px 5px;position: relative;margin: -5px 0 0 60px;/* 让灯穗也有一个动画效果 */animation: wobble 2.5s infinite ease-in-out;}.lantern-rope-middle,.lantern-rope-bottom {position: absolute;width: 10px;left: -2px;}.lantern-rope-middle {border-radius: 50%;top: 14px;height: 10px;background-color: #dc8f03;z-index: 2;}.lantern-rope-bottom {background-color: #ffa500;border-bottom-left-radius: 5px;height: 35px;top: 18px;z-index: 1;}`,// 7`/* 福样式 */.lantern-fu {font-size: 30px;font-weight: bold;color: #ffa500;}`
]

整体流程

实现原理和整个过程所需的知识点,通过简要解析相信你已经明白了,接下来我们要做的事情就是把这些知识点组合在一起,完成自动画画。

import Prism from 'prismjs'
import htmls from './config/htmls'
import styles from './config/styles'
import { isMobile, delay } from '../../common/utils'export default {name: 'newYear2022',data () {return {// html代码展示片段htmlEditPre: '',htmlEditPreSource: '',// css代码展示片段styleEditPre: '',// 实际起作用的csspreviewStylesSource: '',previewStyles: '',// 预览的htmlpreviewHtmls: '',}},computed: {containerClasses () {// 做一个简单的适配return ['container',isMobile() ? 'container-mobile' : '']}},async mounted () {// 1. 打招呼await this.doHtmlStep(0)// 2. 说明主旨await this.doHtmlStep(1)await delay(500)// 3. 第一步声明await this.doHtmlStep(2)await delay(500)// 4. 创建写代码的编辑器await this.doHtmlStep(3)await delay(500)// 5. 准备写编辑器的样式await this.doHtmlStep(4)await delay(500)// 6. 基本样式await this.doStyleStep(0)await delay(500)// 7. 编辑器的样式await this.doStyleStep(1)await delay(500)// 8. 画灯笼的大肚子htmlawait Promise.all([ this.doHtmlStep(5, 0), this.doEffectHtmlsStep(5, 0),])await delay(500)// 8. 画灯笼的大肚子cssawait this.doStyleStep(2)await delay(500)// 9. 提着灯笼的线htmlawait Promise.all([ this.doHtmlStep(6, 1), this.doEffectHtmlsStep(6, 1),])await delay(500)// 10. 提着灯笼的线cssawait this.doStyleStep(3)await delay(500)// 11. 给灯笼加两个盖子htmlawait Promise.all([ this.doHtmlStep(7, 2), this.doEffectHtmlsStep(7, 2),])await delay(500)// 12. 给灯笼加两个盖子cssawait this.doStyleStep(4)await delay(500)// 13. 感觉灯笼快要成了,再给他加上四根线吧htmlawait Promise.all([ this.doHtmlStep(8, 3), this.doEffectHtmlsStep(8, 3),])await delay(500)// 14. 感觉灯笼快要成了,再给他加上四根线吧cssawait this.doStyleStep(5)await delay(500)// 15. 灯笼是不是还有底部的小尾巴呀htmlawait Promise.all([ this.doHtmlStep(9, 4), this.doEffectHtmlsStep(9, 4),])await delay(500)// 16. 灯笼是不是还有底部的小尾巴呀cssawait this.doStyleStep(6)await delay(500)// 17. 最后当然少不了送给大家的福啦htmlawait Promise.all([ this.doHtmlStep(10, 5), this.doEffectHtmlsStep(10, 5),])await delay(500)// 18. 最后当然少不了送给大家的福啦cssawait this.doStyleStep(7)await delay(500)},methods: {// 渲染cssdoStyleStep (step) {const cssEditRef = this.$refs.cssEditRefreturn new Promise((resolve) => {// 从css配置文件中取出第n步的样式const styleStepConfig = styles[ step ]if (!styleStepConfig) {return}let previewStylesSource = this.previewStylesSourcelet start = 0let timter = setInterval(() => {// 挨个累加let char = styleStepConfig.substring(start, start + 1)previewStylesSource += charif (start >= styleStepConfig.length) {console.log('css结束')clearInterval(timter)resolve(start)} else {this.previewStylesSource = previewStylesSource// 左边编辑器展示给用户看的this.styleEditPre = Prism.highlight(previewStylesSource, Prism.languages.css)// 右边预览区域实际起作用的cssthis.previewStyles = `<style>${previewStylesSource}</style>`start += 1// 因为要不断滚动到底部,简单粗暴处理一下document.documentElement.scrollTo({top: 10000,left: 0,})// 因为要不断滚动到底部,简单粗暴处理一下cssEditRef && cssEditRef.scrollTo({top: 100000,left: 0,})}}, 0)})},// 渲染htmldoEffectHtmlsStep (step, insertStepIndex = -1) {// 注意html部分和css部分最大的不同在于后面的步骤是有可能插入到之前的代码中间的,并不是一味地添加到尾部// 所以需要先找到标识,然后插入const insertStep = insertStepIndex !== -1 ? `<!-- htmlStep${insertStepIndex} -->` : -1return new Promise((resolve) => {const htmlStepConfig = htmls[ step ]let previewHtmls = this.previewHtmlsconst index = previewHtmls.indexOf(insertStep)const stepInHtmls = index !== -1let frontHtml = stepInHtmls ? previewHtmls.slice(0, index + insertStep.length) : previewHtmlslet endHtml = stepInHtmls ? previewHtmls.slice(index + insertStep.length) : ''let start = 0let chars = ''let timter = setInterval(() => {let char = htmlStepConfig.substring(start, start + 1)// 累加字段chars += charpreviewHtmls = frontHtml + chars + endHtmlif (start >= htmlStepConfig.length) {console.log('html结束')clearInterval(timter)resolve(start)} else {// 赋值html片段this.previewHtmls = previewHtmlsstart += 1}}, 0)})},// 编辑区域html高亮代码doHtmlStep (step, insertStepIndex = -1) {const htmlEditRef = this.$refs.htmlEditRefconst htmlEditPreRef = this.$refs.htmlEditPreRef// 同上需要找到插入标志const insertStep = insertStepIndex !== -1 ? `<!-- htmlStep${insertStepIndex} -->` : -1return new Promise((resolve) => {const htmlStepConfig = htmls[ step ]let htmlEditPreSource = this.htmlEditPreSourceconst index = htmlEditPreSource.indexOf(insertStep)const stepInHtmls = index !== -1// 按照条件拼接代码let frontHtml = stepInHtmls ? htmlEditPreSource.slice(0, index + insertStep.length) : htmlEditPreSourcelet endHtml = stepInHtmls ? htmlEditPreSource.slice(index + insertStep.length) : ''let start = 0let chars = ''let timter = setInterval(() => {let char = htmlStepConfig.substring(start, start + 1)chars += charhtmlEditPreSource = frontHtml + chars + endHtmlif (start >= htmlStepConfig.length) {console.log('html结束')clearInterval(timter)resolve(start)} else {this.htmlEditPreSource = htmlEditPreSource// 代码高亮处理this.htmlEditPre = Prism.highlight(htmlEditPreSource, Prism.languages.html)start += 1if (insertStep !== -1) {// 当要插入到中间时,滚动条滚动到中间,方便看代码htmlEditRef && htmlEditRef.scrollTo({top: (htmlEditPreRef.offsetHeight - htmlEditRef.offsetHeight) / 2,left: 1000,})} else {// 否则直接滚动到底部htmlEditRef && htmlEditRef.scrollTo({top: 100000,left: 0,})}}}, 0)})},}
}

结尾

马上就要新年啦!愿大家新年快乐,“码”到成功。

参考

  1. 过年了~我用CSS画了个灯笼,看着真喜庆

  2. 用原生 js 写一个 "多动症" 的简历

往期干货:26个经典微信小程序+35套微信小程序源码+微信小程序合集源码下载(免费) 干货~~~2021最新前端学习视频~~速度领取前端书籍-前端290本高清pdf电子书打包下载
点赞和在看就是最大的支持❤️

几个骚操作,让代码自动学会画画,太好玩啦!相关推荐

  1. 骚操作!代码写情诗 | 程序员有话说

    作者 | 素年清时 责编 | 伍杏玲 2019年人工智能系统学: https://edu.csdn.net/topic/ai30?utm_source=cxrs_bw 随着<中国诗词大会> ...

  2. Python骚操作—自动刷抖音

    python骚操作之电脑自动刷抖音 上篇文章发完之后,有朋友在后台留言给我,说手机自动化控制很炫酷,但是appium的安装和使用很复杂,想让我写一篇详细文章介绍一下,下面我就以自动刷抖音为例简单介绍一 ...

  3. :) 程序员的骚语句、骚操作

    请下载国家反诈中心app,谨防杀猪盘.电信诈骗! 文章目录 前言 段子 骚操作 故事 前言 主要记录一些和程序员有趣的事等等,也希望大家有好东西能够分享出来,独乐乐不如众乐乐. 段子 程序员18个有趣 ...

  4. python多线程抢红包代码_Python有哪些神一般的骚操作? 远远不止抢车票、抢红包《附代码》...

    ​ 有人说,"Python除了不会生孩子,Python从撩妹到装x,无所不能!什么都会!" 下载视频?我用Python: 玩跳一跳?我用Python跳到4999分: 撩妹子?依然用 ...

  5. Python骚操作,实现驾考自动答题,直接满分

    Python骚操作来了~ 用Python来实现科目一/四自动答题,100分不要太简单! 最初是表弟最近想买车,但是驾照都没有,买什么车,只能先考驾照~ 看他在网页上练习题目慢吞吞的,我就看不下去了,直 ...

  6. 骚操作,IDEA防止写代码沉迷插件 !

    当初年少懵懂,那年夏天填志愿选专业,父母听其他长辈说选择计算机专业好.从那以后,我的身上就有了计院深深的烙印.从寝室到机房,从机房到图书馆,C.C++.Java.只要是想写点自己感兴趣的东西,一坐就是 ...

  7. 【C语言】那些优秀代码里的骚操作(持续更新…)

    [C语言]那些优秀代码里的骚操作(持续更新-) 1.联合体`union`的妙用 2.`#include`的本质是什么? 3.脱裤子放屁的`do{ }while(0)` 4.一个成熟的代码要学会自己写函 ...

  8. python程序怎么保存到u盘_Python 骚操作,自动拷贝U盘

    阅读文本大概需要 6 分钟. Python 这门语言有非常多有趣的内容,比如给微信好友自动发消息.查看微信好友撤回的消息.通过微信控制电脑等等.在我公众号上也分享过很多有趣的程序,近期我会去搜集一些骚 ...

  9. 五分钟没有操作自动退出_这又是什么骚操作??5只蚂蚁战略配售基金拟增设B类份额,自动赎回退出!!...

    他来了,他来了,这又是什么骚操作??昨天,五只创新未来18个月封闭运作混合型证券投资基金发布联合声明,会为这个战略配售基金安排一个月的退出选择期. 5只创新未来18个月封闭运作混合型证券投资基金发布联 ...

最新文章

  1. QT 中使用 OpenCv 的 CascadeClassifier 报错
  2. regexpal 正则表达式实时调试工具
  3. VS C++调用python进行画图matplotlib
  4. 安装 archlinux 之使用 EFI/GPT
  5. timertask run函数未执行_图执行模式下的 TensorFlow 2
  6. JS闭包—你不知道的JavaScript上卷读书笔记(二)
  7. java读mysql增量_在Java中检索MySQL自动增量
  8. 60-130-336-源码-source-kafka相关-Flink读取kafka
  9. excel判断两列中同一行的数据是否一致
  10. python 课堂笔记 420_一位初学Python同学的课堂笔记,仿佛看到当年的自己
  11. 开源数据屏蔽 数据加密_数据屏蔽或更改行为信息
  12. 重装Windows10系统(Win10系统)
  13. I2C双向电平转换电路
  14. wincc安装信息服务器,WinCC 7.4软件不会安装?怎么授权?一文教会你
  15. linux常用运行库,软件常用运行库-软件常用运行库scku下载 v3.1.0.0官方版--pc6下载站...
  16. Win7开发的VC2012程序在XP上无法运行
  17. “Improving Adversarial Robustness Requires Revisiting Misclassified Examples“ 论文笔记
  18. 专业录音:Audio Hijack for Mac
  19. 下载xcode 6 beta.dmg
  20. 服务器虚拟化的技术和原理,虚拟化一、虚拟化技术基础原理

热门文章

  1. 浙大计算机学硕调剂专硕,浙大计算机学硕复试线399分,专硕375,不愧被称为“炸大”...
  2. Kotlin Contracts
  3. C#通过模板导出Word(文字,表格,图片)
  4. 性能优化--全文搜索引擎(Elasticsearch)
  5. 迈向HTTPS(七)反向代理服务器代理HTTPS
  6. SetTimer如何修改间隔时钟uElapse
  7. 第四届工业危废处置与资源化利用战略合作峰会暨危废产业技术创新联盟年会
  8. 电脑对人体的伤害:分析、保护
  9. 笔记本触控板玻璃检测设备
  10. 【JZOJ 省选模拟】人生(life)