TL;DR
本文首发于稀土掘金:https://juejin.cn/post/7188749864279212087
当青训营遇上码上掘金 | 会动的名片
作为一个前端工程师🦁️,这次选择的是 主题 1:我的名片
,用代码来介绍自己,这太酷了。
话不多说,先看 👀 成品:
小屏不大好看,可以在 code.juejin.cn 或者 self-introduction.icodeq.com 查看全屏幕版本
代码仓库:https://github.com/zkeq/Doraemon/tree/Self-introduction
项目思路
看到题目之后,原本是想要仿写一个闪光 📸️ 卡片的:poke-holo.simey.me

但是因为技术栈不同,其实是不知道怎么抄。
只能退而求其次,用我之前写过一个 会动的代码:哆啦A梦
作为模版
详情看这里,也是上次的掘金入围作者啦(只是过了初审而已hh):
🤔,大概就是这样

其实 js
部分很简单
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| const cssString = `...这部分省略...`;
let codeString = '';
const textDom = document.querySelector('.text'); const styleDom = document.querySelector('.doraemon-style'); const playButton = document.querySelector('.play'); const stopButton = document.querySelector('.stop'); const progressBar = document.querySelector('.progress-bar');
let textStartIndex = 0; let range = 3; let timer = null;
const { width: finalWidth } = document.querySelector('body').getBoundingClientRect(); const updateProgress = (progress) => { progressBar.style.width = `${finalWidth * progress}px`; }; let init = 0; const len = cssString.length; const play = () => { if (textStartIndex >= len) { textStartIndex = 0; } if (timer) return;
timer = setInterval(() => { textStartIndex += range; if (textStartIndex > len) { playButton.innerHTML = '重放' textDom.innerHTML = cssString.substring(0, len); styleDom.innerHTML = cssString.substring(0, len); updateProgress(1); hljs.highlightAll(); window.clearInterval(timer); timer = null; } else { playButton.innerHTML = '播放' textDom.innerHTML = cssString.substring(0, textStartIndex); styleDom.innerHTML = cssString.substring(0, textStartIndex); updateProgress(textStartIndex / len); textDom.scrollTop = textDom.scrollHeight; hljs.highlightAll(); } }, 0); };
const stop = () => { window.clearInterval(timer); auio.pause(); timer = null; };
playButton.onclick = play; stopButton.onclick = stop;
|
一些技术细节?
Q1. 首先就是第一个点:如何实现左边代码右边实时效果一起显示呢?
A1: style
属性其实也可以有自己的 class
,当我们选中它,并且使用 js
动态的向内添加内容时,页面的样式也会随之变化,那么我们只需要同时向两个元素中添加 css
字符即可,一个用于可视化,一个用于页面的真实渲染。
Q2. 代码块的高亮怎么实现的?
A2: 用 <pre>
标签包起来,然后: highlightjs.org
开始写吧!
那么有了大概的思路,我们就可以开始创作啦。
首先理了一下要放点什么东西来展示,大概就是以下这些:
1 2 3 4 5
| 1. 自己的博客,更新了230+篇博客,总PV 12W+; 2. Github,90+ followers,250+ stars; 3. 运维了一个由多服务杂糅到一起的资源站,累计UV50W+,累计PV 300W+; 4. 会Python,用Python写过很多没什么技术但是很好玩的东西,包括每日早报、每日Bing壁纸、上传接口、交作业网站、文件分享站、十几个小小API,用到了云函数; 5. 前端,今年6月才开始在饥人谷好好学,用Vue2.7+Element做出过好几个单页,没用框架的时候写过LearnOnly导航站、每日早报、追梦计划、自建不蒜子等。
|
然后就是找了一堆图,再把简单的框架搭起来。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
| <div class="card_wrap"> <section class="section-0"> <h3>Hello, I am Zkeq。 👋</h3> </section> <section class="section-1"> <h3>#000 一个大一学生,学过 Python,目前在学前端。</h3>
<h4>作为程序员,GitHub 的数据应该可以代表一些事情:</h4>
<a href="https://github.com/zkeq" target="_blank"> <img src="https://stats.readme.icodeq.com/api?username=zkeq&show_icons=true&theme=radical&hide_border=true"> </a> <button class="btn-next" onclick="play()">继续播放</button> </section>
<section class="section-2"> <h3>#00F 那么,我都做过什么呢?</h3>
<h5>我想,最令我满意的,应该是:<a target="_blank" href="https://cdn.onmicrosoft.cn"> 渺软 CDN</a></h5>
<a href="https://cdn.onmicrosoft.cn" target="_blank"> <img src="https://bu.dusays.com/2023/01/15/63c2d4143b8d7.png"> </a> <ul> <li>同时 这也是最近的项目 ( 12.31 上线 1.7 完成重构) </li> <li>主要是为了解决前端在开发简单 demo 时遇到的静态资源引入的问题。</li> <li>详情请移步评论区最后一条查看 更新日志</li> <li>页面重构其实是 @xcsoft 大佬帮忙做的,我也在努力学习做出漂亮页面啦。</li> </ul> <button class="btn-next" onclick="play()">继续播放</button> </section>
<section class="section-3"> <h3>#0FF 再者重要的,应该就是我的博客啦。</h3>
<a href="https://icodeq.com" target="_blank"> <img src="https://img.onmicrosoft.cn/2022-09-08/image-20220909153929835.png"> </a> <h5>到目前的话,应该是在很用心的运营这个博客的!</h5>
<ul> <li>博文更新 240+ 篇,累计字数 146k+ 。</li> <li>虽然大部分都是学习的笔记吧,但是在这个过程中养成了整理知识点的好习惯!</li> <li>因为访问数据被清过一次,所以访问人数应该在 10w uv +</li> <li>因为这个博客折腾出了很多周边产品(x) </li> <li>感觉有这样一个可以自己掌控的小天地很舒服</li> </ul>
<h5>这里是最近的博文哦:</h5>
<a href="https://icodeq.com" target="_blank"> <img src="https://zkeq.xyz/Profile/article.svg"> </a> <ul> <li>因为是学习委员,再加上我们班的专业就是前端</li> <li>所以暑假带着他们一起学习啦。</li> </ul> <button class="btn-next" onclick="play()">继续播放</button> </section>
<section class="section-4"> <h3>#0F0 优雅至极!追梦计划!</h3>
<a href="https://dream-plan.cn" target="_blank"> <img src="https://user-images.onmicrosoft.cn/155082301-d777c58f-d495-42d7-8dba-59ca844379e7.jpg"> </a> <ul> <li>这个其实是从高中带过来的执念 hhh</li> <li>因为那时候很执着于励志视频,总感觉自己的生活没有斗志(</li> <li>于是趁着闲下来并且用自己的爬虫知识,搞来了 1000 多个 video </li> <li>就做出来了这个(</li> </ul> <button class="btn-next" onclick="play()">继续播放</button> </section>
<section class="section-5"> <h3>#FF0 流量之最!图欧学习资源库</h3>
<a href="https://tuostudy.com" target="_blank"> <img src="https://githubusercontent.onmicrosoft.cn/zkeq/Python-WebSite-Screenshot/master/save/tuostudy.com/2023-01-14_06-08-37.png"> </a> <ul> <li>这个站的日 uv 在 1w 左右() 应该算是我运维的最大的流量体了。</li> <li>虽然是公益运维,但是它带动了我的一系列周边()</li> </ul>
<h5>这里是统计日志:</h5>
<a href="https://site.icodeq.com/tuostudy.com" target="_blank"> <img src="https://bu.dusays.com/2023/01/15/63c2de4536cac.png"> </a> <button class="btn-next" onclick="play()">继续播放</button> </section>
<section class="section-6"> <h3>#F0F 其他的我感觉倒是没什么好炫耀的啦</h3> <blockquote><h5>都是一些自己练手或者自己玩的小项目</h5></blockquote> <button class="btn-next" onclick="play()">继续播放</button> <section class="section-6_1"> <h4>#00000F 每日早报</h4>
<a href="https://news.icodeq.com" target="_blank"> <img src="https://bu.dusays.com/2023/01/15/63c2d48578f96.png"> </a> <ul> <li>初学 js 的时候写的,到现在好像每天 1k 访问量的样子</li> <li>后端采用 Python 云函数 bs4 + fastapi 开发</li> <li>这个 js 写的惨不忍睹,但是勉强能用只能算是</li> </ul> <button class="btn-next" onclick="play()">继续播放</button> </section>
<section class="section-6_2"> <h4>#0000F0 LearnOnly 导航重构</h4>
<a href="https://learnonly.xyz" target="_blank"> <img src="https://bu.dusays.com/2023/01/15/63c2d43e74542.png"> </a> <ul> <li>这个主要是模仿 万花筒 导航站,做的自己的一个起始页。</li> <li>里面用到了 js 瀑布流,算是练习了一下基本功。</li> <li>支持导入自己的书签(如果你也有很好的文件夹分类习惯的话)</li> </ul> <button class="btn-next" onclick="play()">继续播放</button> </section>
<section class="section-6_3"> <h4>#000F00 Vue2 + element 做的几个单页</h4> <a href="https://share.onmicrosoft.cn/5fa9z3zqt" target="_blank"> <img src="https://img.onmicrosoft.cn/2022/12/17/84ffc1d1-1562-4a59-b901-4206427272b2.png"> <img src="https://img.onmicrosoft.cn/2022/12/17/7e32a7e6-040a-49a5-af7b-6eb4f99fc477.png"> <img src="https://img.onmicrosoft.cn/2022/12/17/0b05a7b1-ea5f-45dc-b349-2d7c536fddb5.png"> <img src="https://bu.dusays.com/2023/01/15/63c2d4b523a60.png"> </a> <ul> <li>这些主要是写给自己用的,所以就随便写啦。</li> <li>其实自己没有学过框架,只是模糊知道一点概念。但是双向绑定真的很爽。</li> <li>安利官网的互动教程:https://cn.vuejs.org/tutorial/#step-1</li> </ul> <button class="btn-next" onclick="play()">继续播放</button> </section>
<section class="section-6_4"> <h4>#00F000 自建不蒜子</h4> <a href="https://busuanzi.icodeq.com" target="_blank"> <img src="https://bu.dusays.com/2023/01/15/63c2d60e41f6a.png"> </a> <ul> <li>因为我的博客是纯静态博客,没有后台</li> <li>所以想要实现访问量统计的话,就要借助其他工具。</li> <li>不蒜子 就是一个轻量的,开箱即用的统计工具。</li> <li>但是因为其经常 502 所以我自己仿写了一个。/</li> </ul> <button class="btn-next" onclick="play()">继续播放</button> </section>
<section class="section-6_5"> <h4>#0F0000 Bing-WallPage-Actions Bing 壁纸爬虫</h4> <a href="https://bing-wallpaper.apifox.cn" target="_blank"> <img src="https://img.onmicrosoft.cn/2022-09-08/image-20220909010202981.png"> </a> <ul> <li>目前已经上架 Apifox APIhub 啦(就在B站旁边)</li> <li>里面会吐出 单个 国家/地区 所有的每日图片</li> <li>目前大概 270*9 大约 2000 张图片了</li> <li>详情点击图片体验。</li> </ul> <button class="btn-next" onclick="play()">继续播放</button> </section>
<section class="section-6_6"> <h4>#F00000 一些平台的部署记录留念</h4>
<a href="https://github.com/zkeq" target="_blank"> <img src="https://img.onmicrosoft.cn/2022-09-08/github.com_zkeq%E7%9A%84%E5%89%AF%E6%9C%AC.png">
<img src="https://img.onmicrosoft.cn/2022-09-08/vercel.com_dashboard.png">
<img src="https://bu.dusays.com/2023/01/14/63c2ab62a9a04.png">
<img src="https://bu.dusays.com/2023/01/15/63c301f60bcaf.png"> </a> </section>
<button class="btn-next" onclick="play()">重新播放</button> </section>
</div>
|
然后就面临了一个难题,我这是动态添加的 CSS
,如何与右边的 HTML 效果
联动呢?
想了很久,最后想到的方法是首先把元素都隐藏起来,然后 CSS 挨个显示
过程中使用 js 滚动视窗:
1
| document.querySelector(".section-*").scrollIntoView(true);
|
来实现联动。
听起来很简单,但是我实现起来却是这样的()():
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| if (textStartIndex > 673 && !section_6_5){ document.querySelector(".section-6_5").scrollIntoView(true); section_6_5 = true; stop(); }else if (textStartIndex > 633 && !section_6_4){ document.querySelector(".section-6_4").scrollIntoView(true); section_6_4 = true; stop(); }else if (textStartIndex > 593 && !section_6_3){ document.querySelector(".section-6_3").scrollIntoView(true); section_6_3 = true; stop(); }else if (textStartIndex > 553 && !section_6_2){ document.querySelector(".section-6_2").scrollIntoView(true); section_6_2 = true; stop(); }else if (textStartIndex > 513 && !section_6_1){ document.querySelector(".section-6_1").scrollIntoView(true); section_6_1 = true; stop(); }else if (textStartIndex > 473 && !section_6){ document.querySelector(".section-6").scrollIntoView(true); section_6 = true; stop(); }else if (textStartIndex > 435 && !section_5){ document.querySelector(".section-5").scrollIntoView(true); section_5 = true; stop(); }else if (textStartIndex > 396 && !section_4){ document.querySelector(".section-4").scrollIntoView(true); section_4 = true; stop(); }else if (textStartIndex > 359 && !section_3){ document.querySelector(".section-3").scrollIntoView(true); section_3 = true; stop(); }else if (textStartIndex > 321 && !section_2){ document.querySelector(".section-2").scrollIntoView(true); section_2 = true; stop(); }else if (textStartIndex > 283 && !section_1){ document.querySelector(".section-1").scrollIntoView(true); section_1 = true; stop(); }else if (textStartIndex > 245 && !section_1){ document.querySelector(".section-0").scrollIntoView(true); section_0 = true; }
|
只能是算出来每个代码块的 CSS 是第几个,然后判断一下,很笨,但是勉强能用吧。
后来发现这个方案有个 bug,在掘金的嵌入代码时,最外层的滚动条也会被滚动,影响用户体验
解决方案:
1 2
| document.querySelector(".section-*").scrollIntoView({block: "nearest", inline: "nearest"});
|
接着就是没什么意思的了,写了一堆适配代码,然后加上了一个好看的按钮样式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| .btn-next { --clr-font-main: hsla(0 0% 20% / 100); --btn-bg-1: hsla(194 100% 69% / 1); --btn-bg-2: hsla(217 100% 56% / 1); --btn-bg-color: hsla(360 100% 100% / 1); --radii: 0.5em; cursor: pointer; padding: 0.3em 1.3em; margin-bottom: 10px; zoom: 0.8; min-width: 60px; min-height: 44px; font-size: var(--size, 1rem); font-family: "Segoe UI", system-ui, sans-serif; font-weight: 500; transition: 0.8s; background-size: 280% auto; background-image: linear-gradient(325deg, var(--btn-bg-2) 0%, var(--btn-bg-1) 55%, var(--btn-bg-2) 90%); border: none; border-radius: var(--radii); color: var(--btn-bg-color); box-shadow: 0px 0px 20px rgba(71, 184, 255, 0.5), 0px 5px 5px -1px rgba(58, 125, 233, 0.25), inset 4px 4px 8px rgba(175, 230, 255, 0.5), inset -4px -4px 8px rgba(19, 95, 216, 0.35); }
.btn-next:hover { background-position: right top; }
.btn-next:is(:focus, :focus-within,:active) { outline: none; box-shadow: 0 0 0 3px var(--btn-bg-color), 0 0 0 6px var(--btn-bg-2); }
|
补充:增加了全屏检测以及切换功能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| function launchFullScreen(element) { if(isFullScreen()){ exitScreen(); return; }
if (element.requestFullscreen) { element.requestFullscreen(); } else if (element.mozRequestFullScreen) { element.mozRequestFullScreen(); } else if (element.webkitRequestFullscreen) { element.webkitRequestFullscreen(); } else if (element.msRequestFullscreen) { element.msRequestFullscreen(); } }
function exitScreen() { if (document.exitFullscreen) { document.exitFullscreen(); } else if (document.mozCancelFullScreen) { document.mozCancelFullScreen(); } else if (document.webkitCancelFullScreen) { document.webkitCancelFullScreen(); } else if (document.msExitFullscreen) { document.msExitFullscreen(); } if (typeof cfs != "undefined" && cfs) { cfs.call(el); } }
function isFullScreen() { return !! ( document.fullscreen || document.mozFullScreen || document.webkitIsFullScreen || document.webkitFullScreen || document.msFullScreen ); }
|
- 起初我用的是
document.querySelector(".section-0").scrollIntoView(true);
- 但是我发现这在掘金的嵌入代码预览时,父级页面:也就是浏览器窗口
- 居然也会跟着滚动,这不是期望发生的行为。
- 经过查阅很多资料后,使用了一个还算可以的解。
1 2
| document.querySelector(".section-*").scrollIntoView({block: "nearest", inline: "nearest"});
|
参考资料
让我感到很有意思的是,一个5年前就说应该被修复的 bug
居然留存到了现在( 2023 年)
