当青训营遇上码上掘金 | 会动的名片
作为一个前端工程师🦁️,这次选择的是 主题 1:我的名片
,用代码来介绍自己,这太酷了。
话不多说,先看 👀 成品:
小屏不大好看,可以在 code.juejin.cn 或者 self-introduction.icodeq.com 查看全屏幕版本
项目思路
看到题目之后,原本是想要仿写一个闪光 📸️ 卡片的:poke-holo.simey.me
但是因为技术栈不同,其实是不知道怎么抄。
只能退而求其次,用我之前写过一个 会动的代码:哆啦A梦
作为模版
详情看这里,也是上次的掘金入围作者啦(只是过了初审而已hh):
🤔,大概就是这样
其实 js
部分很简单
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);
// textDom.scrollTop = textDom.scrollHeight;
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. 自己的博客,更新了230+篇博客,总PV 12W+;
2. Github,90+ followers,250+ stars;
3. 运维了一个由多服务杂糅到一起的资源站,累计UV50W+,累计PV 300W+;
4. 会Python,用Python写过很多没什么技术但是很好玩的东西,包括每日早报、每日Bing壁纸、上传接口、交作业网站、文件分享站、十几个小小API,用到了云函数;
5. 前端,今年6月才开始在饥人谷好好学,用Vue2.7+Element做出过好几个单页,没用框架的时候写过LearnOnly导航站、每日早报、追梦计划、自建不蒜子等。
然后就是找了一堆图,再把简单的框架搭起来。
<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 滚动视窗:
document.querySelector(".section-*").scrollIntoView(true);
来实现联动。
听起来很简单,但是我实现起来却是这样的()():
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,在掘金的嵌入代码时,最外层的滚动条也会被滚动,影响用户体验
解决方案:
document.querySelector(".section-*").scrollIntoView({block: "nearest", inline: "nearest"});
// https://stackoverflow.com/questions/56688002/javascript-scrollintoview-only-in-immediate-parent
接着就是没什么意思的了,写了一堆适配代码,然后加上了一个好看的按钮样式:
.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);
}
补充:增加了全屏检测以及切换功能:
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
);
}
// 来源:https://www.jianshu.com/p/6170f1ba7467
记录一个 scrollIntoView 的bug
- 起初我用的是
document.querySelector(".section-0").scrollIntoView(true);
- 但是我发现这在掘金的嵌入代码预览时,父级页面:也就是浏览器窗口
- 居然也会跟着滚动,这不是期望发生的行为。
- 经过查阅很多资料后,使用了一个还算可以的解。
// 也就是上文提到的
document.querySelector(".section-*").scrollIntoView({block: "nearest", inline: "nearest"});
参考资料
- javascript - scrollIntoView block vs inline - Stack Overflow
- html - Javascript scrollIntoView only in immediate parent - Stack Overflow
让我感到很有意思的是,一个5年前就说应该被修复的 bug
居然留存到了现在( 2023 年)