一个用原生 HTML、CSS 和 JavaScript 实现的暗色字符背景与鼠标反色交互学习项目。
这个项目通过一页纯静态首屏,练习 canvas 字符墙绘制、设备像素比适配、鼠标平滑跟随、mix-blend-mode: difference 局部反色效果,以及中心标题的打字进入动画。页面默认展示文案为「快乐的大葱鸭」,不依赖框架、构建工具或第三方运行时,适合直接打开源码观察视觉层如何拆分与协作。
- 理解用
canvas固定网格绘制随机字符背景的基础方式 - 练习通过
requestAnimationFrame组织持续动画与缓动更新 - 掌握
devicePixelRatio适配高分屏画布清晰度的处理思路 - 熟悉
mix-blend-mode: difference在局部反色交互中的使用方式 - 通过 HTML 结构、CSS 视觉层和 JavaScript 动画逻辑的拆分,练习纯静态页面的组织方式
# 本项目无额外依赖,无需安装 npm 包或启动构建工具# 直接在浏览器中打开 index.html也可以将 InvertField 目录放到任意静态服务器中访问。项目入口文件是 index.html,页面加载后会自动启动字符墙动画、鼠标反色圆和标题打字效果。
- 打开页面后可看到暗色全屏字符墙背景
- 背景字符会按固定节奏随机替换,并带有轻微颜色过渡
- 桌面端移动鼠标时,白色圆形会平滑跟随并对覆盖区域产生反色效果
- 按下鼠标时反色圆会缩小,松开后恢复尺寸
- 中心标题会以打字效果逐字显示「快乐的大葱鸭」
- 浏览器窗口尺寸变化后,画布会重新适配视口与高分屏比例
InvertField/
├── index.html # 页面入口,组织 canvas、暗角图层、遮罩层和中心标题
├── css/
│ └── style.css # 全屏布局、背景压暗、标题渐变、鼠标反色圆和移动端适配
└── js/
└── app.js # 字符墙绘制、随机刷新、颜色过渡、鼠标跟随和标题打字效果
| 能力 | 说明 | 代码位置 |
|---|---|---|
| 全屏首屏布局 | 页面使用 100vh 首屏容器承载背景层、遮罩层和中心标题 |
index.html、css/style.css |
| Canvas 字符墙 | 按固定网格铺满屏幕,每个格子绘制一个随机 ASCII 字符 | js/app.js |
| 随机字符刷新 | 每隔约 50ms 随机替换约 5% 的字符,形成暗色电子噪声感 |
refreshCells() |
| 颜色过渡 | 被刷新的字符会从旧颜色逐帧过渡到新颜色,避免整屏硬闪 | tweenCellColors()、mixColor() |
| DPR 适配 | 根据 devicePixelRatio 设置真实画布尺寸,提升高分屏清晰度 |
resizeCanvas() |
| 鼠标平滑跟随 | 使用目标坐标与当前坐标插值,让反色圆跟随鼠标移动 | renderCursor() |
| 局部反色交互 | 通过 .cursor-orb 的 mix-blend-mode: difference 实现覆盖区域反色 |
css/style.css |
| 按压反馈 | 鼠标按下时反色圆缩小,提供轻量交互反馈 | bindCursor()、css/style.css |
| 标题打字效果 | 页面中心文案按固定时间间隔逐字显示 | typeTitle() |
| 移动端适配 | 窄屏隐藏自定义光标并恢复系统默认光标行为 | css/style.css |
glyphs定义可随机显示的字符池,当前使用稳定的 ASCII 字符集合cellWidth和cellHeight决定网格密度,fontSize决定单个字符大小resetCells()会根据当前视口重新计算行列数量,并为每个格子生成字符与颜色状态drawMatrix()每次按照cells数据重绘整张画布
animateMatrix()使用requestAnimationFrame驱动画布动画主循环tickMs控制随机刷新字符的节奏,当前约每50ms刷新一小批格子refreshCells()只负责挑选要变化的格子,tweenCellColors()负责把颜色变化做成逐帧过渡- 字符替换和颜色补间分离后,动画更容易单独调节节奏与观感
resizeCanvas()将 CSS 视口尺寸与真实画布像素尺寸分开处理- 真实画布尺寸乘以
dpr,展示尺寸仍保持 CSS 像素 ctx.setTransform(dpr, 0, 0, dpr, 0, 0)让绘制坐标继续按 CSS 像素书写- 窗口变化时重新生成字符格子,避免拉伸导致字符位置错乱或画面发糊
.cursor-orb是一个固定定位的白色圆形层mix-blend-mode: difference会让它覆盖到的区域发生像素级反色pointer对象同时保存当前坐标和目标坐标,renderCursor()每帧做插值移动- JS 只负责坐标和状态 class,具体尺寸、混合模式和显隐过渡由 CSS 控制
matrix-canvas负责字符背景hero-vignette.outer负责外圈暗角,让画面边缘更沉hero-vignette.center负责压暗标题背后的背景,保证主文案可读hero-overlay负责整体压暗,避免字符墙抢走视觉主体hero-content放在最高层,只承载页面中心标题
index.html负责组织页面层级,包括背景画布、暗角、遮罩、标题文本和脚本入口。css/style.css负责视觉表现,包括全屏布局、暗色背景、渐变标题、反色圆和移动端光标处理。js/app.js负责所有运行时交互与动画,包括字符墙绘制、窗口 resize、鼠标跟随和标题打字。
- 修改中心文案:调整
js/app.js中typeTitle()里的const text - 修改背景字符:调整
glyphs - 修改字符刷新频率:调整
tickMs - 修改字符密度:调整
fontSize、cellWidth、cellHeight - 修改背景配色:调整
palette或css/style.css中的全局色值 - 修改反色圆大小:调整
.cursor-orb的width和height
- 增加主题配置,让字符颜色、标题文案和光标尺寸可集中切换
- 为字符墙增加鼠标附近扰动、局部亮度提升或点击扩散效果
- 给标题添加副标题、链接或更多入场动画
- 将字符墙逻辑封装成可复用模块,方便在其他静态页面中挂载
- 增加暂停动画或降低动效模式,照顾低性能设备与无障碍偏好
- 用纯静态文件实现一个完整的首屏视觉交互
- 通过
canvas网格数据理解「状态更新 → 重绘」的基础动画流程 - 掌握高分屏画布适配、窗口 resize 重建和字符密度控制
- 体会 CSS 混合模式与 JavaScript 坐标缓动配合后的交互表现力
- 为后续制作更复杂的视觉背景、创意首屏或互动海报打基础
欢迎通过 Issue 或 Pull Request 交流这个学习项目的视觉实现思路、交互细节和可扩展方向。继续扩展时,建议保持背景绘制、视觉样式和交互逻辑的职责边界清晰,避免把画布状态和 DOM 样式更新混在同一段逻辑里。
当前项目位于仓库 learning-projects 中,仓库根目录已提供 MIT License。如果后续需要将该项目单独拆分发布,建议再为子项目补充独立说明。
感谢浏览器原生提供的 Canvas、CSS 混合模式、指针事件和动画帧调度能力,让这个小型视觉练习可以在不依赖框架的前提下完成较完整的交互效果。
注意:这是一个面向学习与练习的静态视觉页面,重点在于理解 Canvas 字符墙、反色光标和页面视觉分层的实现方式,而不是直接作为生产级落地页模板使用。