博客中几个组件的实现
在开发博客时做的几个小组件,记录一下他们的实现过程。
数字时钟
数字时钟是博客里第一个实现的组件。网上指针时钟教程很多,但大多是钟表样式,接近我想要效果的是翻牌式数字时钟。干脆自己搞一个电子表式的。
具体实现
先拆基本单位
观察电子表的结构可以看出,所有的数字都是由七个条形拼凑出来的。七个条形中除了中间的横条不同外,其余六个都属于同一类“带尖角的段码”,因此基本单位其实只需要实现三种形状:
- 普通段码
- 中间横条
- 分隔用的冒号
没找现成字体,自己写了个 BaseStick 组件当每根发光条。现在这版内部已经不靠 clip-path 裁形状了,直接用 SVG path 画段码本体和两端尖角,边缘更可控,缩放比纯 CSS 裁切稳定。画好后 rotate 改朝向,复用成上下左右四种位置。
再处理数字映射
接着再写一层 DigitalNumber 组件,将七个长条先按照 8 的结构摆好,并给它们从上到下编号。
真正控制不同数字显示的部分则交给一个映射表来完成,例如 0 需要亮起哪几段,1 需要关闭哪几段,都在 stickActiveMap 里写死。这样渲染时只需要根据当前字符去读取对应的配置,再把 active 状态传给每根数码管即可。
两个小细节
关闭状态的长条没有直接消失,而是返回同尺寸的空白占位,数字切换时布局不会抖。段码换成了 SVG,但这个思路没变。
冒号单独拆了个分支,直接渲染两个小圆角方块,实现反而比七段数码管省事。
最外层时钟逻辑
最外层的 DigitalClock 组件就比较直接了,每秒通过 setInterval 取一次当前时间,然后把 toTimeString() 截取成 HH:MM:SS 这样的字符数组逐个渲染。
不直接用电子表字体,主要还是想要切换时一段一段亮起和熄灭的感觉,手搓出来才对味。
backToTop
具体实现
文章页滚深了还是想要个回顶按钮,鼠标滚轮往回翻太累了。
没做太复杂,固定在右下角,点击 scrollTo(0, 0) 就行。图标用 @ant-design/icons 的 CaretUpOutlined,沿用站点主题色和暗黑配色,放进侧边栏不突兀。
按钮里还留了个 data-id=”--scroll-position” 的节点,本来想后面显示滚动进度百分比,顺便兼顾阅读进度提示。现在这版还是先把结构留着,功能上比较朴素。
点击放礼花
纯粹觉得好玩才加的。配色参考了 canvas-confetti,但没引第三方库,自己用原生 Canvas 手写了个轻量粒子系统。点一下页面任意位置,一小簇彩色粒子从点击处炸开,带重力下落和旋转,慢慢消失,不抢戏。
具体实现
整体思路
全屏 canvas 上画粒子,每个粒子有位置、速度、颜色、形状、生命周期。点击时在鼠标位置生成一批,requestAnimationFrame 逐帧更新和重绘。
canvas 用 pointer-events-none 覆盖页面,只管渲染,不拦截点击事件。
粒子设计
每个粒子的数据结构:
x、y:位置vx、vy:速度life/maxLife:控制渐隐color:15 种预设色随机size:2 到 4 像素,偏小巧gravity:重力,抛物线下落rotation/rotationSpeed:旋转,方形和三角形转起来有动感shape:圆、方、三角随机
速度方向 0 到 2π 随机,大小 1 到 4,爆炸范围比较克制。垂直速度额外减 2,粒子先微微向上飞再下落,更像真实礼花。
动画循环
每一帧里,先清空画布,然后遍历所有存活的粒子,更新它们的位置、速度和生命值。生命值按 1 / maxLife 递减,减到 0 以下就从数组里移除。绘制时根据剩余生命值设置 globalAlpha,实现淡出效果。
速度上除了重力之外,还乘了一个 0.98 的空气阻力系数,让粒子不会一直飞太远。
一个细节:只响应单纯的点击
一开始监听 click,结果选中文本也触发礼花,体验不好。最后在 handleClick 里检查 window.getSelection()?.toString() 是否为空,不为空就 return,复制段落时不会被礼花打扰。
文章标题导航
具体实现
长文阅读没目录,来回跳不方便,技术文章二三级标题又多。
给 article 加固定 id,组件挂载后 querySelectorAll 取所有带 id 的 h1 到 h6,把标题文本、层级、距顶部偏移量存进状态,目录遍历这个数组渲染。
缩进直接按标题级别算左边距,h2 不缩进,h3、h4 逐级错开。点击就是锚点跳转,标题本身有 id,拼 #${id} 就行。
高亮是核心。监听 scroll 和 resize,window.scrollY + 96 和每个标题的 offsetTop 比较,找当前滚动到最后一个标题作为激活项。96 是给顶部导航留的缓冲,不然切换偏晚。命中项换成主题蓝色,读到哪一节一目了然。