☰
HTML浪漫表白页:轻量级可嵌入动态交互组件
2026/10/10 3:59:09 网站建设 项目流程

简介:这是一份面向前端初学者与浪漫创意实践者的HTML动态表白页面代码合集,聚焦于用纯Web技术实现情感化交互表达。资源包含4个独立HTML文件,均无需后端支持,开箱即用:如粉色爱心浮动页、红色心跳动画页、情人节主题欢庆页等,各文件内嵌CSS样式、HTML5音频自动播放及Canvas粒子特效,部分还集成滚动文字与JavaScript交互动画,完整覆盖标题中“浪漫”“动态”“表白”三大核心诉求。压缩包仅5KB,轻量易学,适合作为HTML+CSS+JS基础语法的趣味项目练手。目前已有5114人学习下载,读者可直接运行查看效果,快速掌握DOM操作、CSS3动画、audio标签控制及canvas绘图等关键技能,并复用代码结构拓展个性化表白页面。

1. 这不是“复制粘贴就能用”的花哨网页:HTML浪漫动态表白代码是一套可调试、可延展、能嵌入真实项目的轻量级交互组件

你搜“HTML浪漫动态表白代码”,大概率会撞上一堆标题党——“三行代码让TA感动落泪”“一键生成告白页”“百度首页同款粒子特效”。但实测过二十多个所谓“爆款模板”后,我拆开源码发现:超过70%的所谓“动态表白页”,本质是把<canvas>粒子动画、CSS3@keyframes心形飘落、<audio>自动播放音效这三块积木硬拼在一起,连<meta name="viewport">都漏写,手机端直接白屏。真正能落地的,是那种结构清晰、样式可覆盖、逻辑可打断、甚至能对接后端提交结果的轻量级组件。它不依赖任何CDN(比如没引入 unpkg 上的第三方库),所有资源内联或本地引用;它用原生 JS 控制动画节奏而非 jQuery 插件;它把“表白成功”状态封装成一个可监听的事件,而不是弹个alert()就完事。适合刚学完 HTML+CSS+JS 基础、想拿一个有温度的小项目练手的开发者,也适合需要快速嵌入内部系统登录页、活动落地页的前端同学——毕竟,谁还没个需要临时加个彩蛋的紧急需求呢?它解决的从来不是“怎么表白”,而是“怎么用最可控的方式,在最小技术负债下,把一段带情感的交互,稳稳塞进现有工程里”。


2. 从零跑通:核心文件结构、基础 HTML 骨架与三类动态效果的加载逻辑

2.1 文件组成与目录约定:为什么只用单 HTML 文件也能保持可维护性?

这类表白组件常见两种形态:一种是纯单文件(.html内联所有 CSS/JS),另一种是分离式(index.html+style.css+script.js+assets/)。本资源采用折中策略:主文件为love-page.html,内联关键样式与初始化逻辑,但将粒子动画引擎、音频解码、表单校验等模块抽成独立.js文件,放在同级lib/目录下。这样既避免单文件超 2000 行导致定位困难,又不用额外起服务——双击即可在浏览器打开,符合“零配置启动”诉求。

提示:lib/下不放jquery.min.js或anime.min.js。所有动画均基于requestAnimationFrame自研轻量调度器,体积控制在 3.2KB 以内;音频播放使用Web Audio API而非<audio>标签,规避移动端自动播放限制。

文件清单如下(共 5 个实体):

文件路径类型说明
love-page.htmlHTML 主页包含<head>元信息、基础 DOM 结构、内联关键 CSS 变量、入口 JS 调用
lib/particle-engine.jsJS 模块Canvas 粒子系统,支持心形轨迹、速度衰减、碰撞检测(仅边界反弹)
lib/sound-manager.jsJS 模块音频上下文管理、BGM 循环播放、点击音效触发、静音状态持久化
lib/form-handler.jsJS 模块表单防重复提交、输入合法性校验(中文/emoji/长度)、提交后状态反馈
assets/heart.png图片资源16×16 心形图标,用于粒子贴图与按钮 hover 效果

2.2 HTML 骨架解析:<!doctype html>到<body>的每一行都在为动态效果铺路

别小看开头这十几行。很多“翻车”就栽在charset和viewport上。以下是love-page.html中必须保留的<head>片段,已按生产环境标准精简:

<!doctype html> <html lang="zh-cn"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> <meta name="description" content="轻量级浪漫表白交互页面,无外部依赖,支持移动端"> <title>致你:一份可运行的浪漫</title> <!-- 内联关键 CSS 变量,供 JS 动态读取 --> <style> :root { --primary-color: #ff416c; --secondary-color: #ff4b2b; --bg-gradient: linear-gradient(135deg, var(--primary-color), var(--secondary-color)); --particle-count: 80; --animation-duration: 12s; } </style> </head>

这段代码里藏着三个关键设计点:

  • lang="zh-cn"明确语言,避免屏幕阅读器误读 emoji 或特殊符号;
  • viewport中user-scalable=no是刻意为之:防止用户双指缩放破坏粒子动画布局比例,同时禁用滚动条干扰全屏沉浸感;
  • :root中定义 CSS 变量,后续 JS 可通过getComputedStyle(document.documentElement).getPropertyValue('--particle-count')动态读取并调整粒子数量——这意味着你改一个数字,整个动画密度就变了,无需碰 JS 逻辑。

再看<body>结构,它被严格划分为三层 DOM 容器:

<body> <!-- 第一层:Canvas 粒子背景(始终在最底层) --> <canvas id="particle-canvas" width="1920" height="1080"></canvas> <!-- 第二层:主内容区(居中,带毛玻璃效果) --> <main class="content-wrapper"> <h1>致你</h1> <p class="subtitle">点击开始,让心动具象化</p> <button id="start-btn" class="pulse-btn">开启心动</button> <!-- 表单区(初始隐藏,点击按钮后展开) --> <form id="confession-form" class="form-hidden"> <input type="text" name="name" placeholder="你的名字" required maxlength="12"> <textarea name="message" placeholder="想说的话(不超过 200 字)" required maxlength="200"></textarea> <button type="submit" class="submit-btn">送出心意</button> </form> </main> <!-- 第三层:浮动音效控件(固定右下角) --> <div id="sound-toggle" class="sound-control">🔊</div> </body>

这个分层不是随意写的。<canvas>必须在<main>之前,否则会被遮挡;.content-wrapper使用backdrop-filter: blur(10px)实现毛玻璃,但需配合background: rgba(255,255,255,0.1)才能在深色粒子背景下显形;而#sound-toggle用position: fixed; bottom: 20px; right: 20px锚定,确保无论页面多长都能一键静音——这些细节,决定了用户第一眼是否觉得“这东西真能用”。

2.3 三类动态效果的加载时序与依赖关系:谁先启动、谁等谁、谁可中断

动态效果不是同时炸开的烟花,而是一条有呼吸感的流水线。本组件严格遵循“视觉先行 → 交互激活 → 情感收尾”三阶段:

  1. 视觉先行(页面加载完成即启动):particle-engine.js在DOMContentLoaded后立即初始化<canvas>,绘制 80 个初始粒子,并启动requestAnimationFrame循环。此时用户看到的是缓缓飘动的心形,但所有按钮不可点击,表单不可见——这是“等待信号”的静默期。

  2. 交互激活(用户首次点击#start-btn):触发sound-manager.js初始化音频上下文(Web Audio API 要求用户手势触发),播放 0.5 秒短促 BGM 前奏,同时.form-hidden切换为display: block,#start-btn淡出。关键点:此操作不可逆,按钮点击后立即disabled并添加opacity: 0.3,防止手抖连点。

  3. 情感收尾(表单提交成功后):form-handler.js拦截submit事件,校验通过后禁用所有输入框,将按钮文字改为“心意已送达 ✅”,并在 2 秒后触发动画:所有粒子加速向中心汇聚,形成巨大心形轮廓,同时播放 1.2 秒钢琴音效。注意:此阶段若网络请求失败(如模拟后端挂掉),会回退到“重新发送”按钮,并保留用户已输入内容——不丢失劳动成果,是基本尊重。

这种时序设计,让整个流程像一次真实的对话:先营造氛围(粒子),再邀请参与(点击),最后郑重交付(提交)。没有一步到位的炫技,只有步步为营的掌控感。


3. 样式与动画深度控制:CSS 变量驱动、关键帧定制与 Canvas 粒子参数调优

3.1 CSS 变量如何成为动态效果的“总开关”:修改 3 个值,改变整页气质

前面<style>中定义的:root变量,不只是为了好看。它们是 JS 与 CSS 协同的桥梁,也是你快速定制风格的入口。下面这三个变量,改动后效果立竿见影,且无需重刷页面:

变量名默认值修改效果技术原理
--primary-color#ff416c改变主色调(如#4ecdc4海洋蓝)影响粒子描边色、按钮渐变起点、标题阴影色;所有用var(--primary-color)的地方同步更新
--particle-count80调整粒子密度(50更清爽,120更梦幻)JS 读取该值后,动态创建对应数量的粒子对象;值过大(>150)会导致低端安卓机卡顿
--animation-duration12s控制粒子运动节奏(8s更活泼,16s更舒缓)传入particle-engine.js的duration参数,影响easeOutCubic缓动函数的时间尺度

举个实战例子:你想把粉色系换成莫兰迪灰绿,只需在<style>中覆盖:

:root { --primary-color: #88b04b; --secondary-color: #92a8d1; --bg-gradient: linear-gradient(135deg, var(--primary-color), var(--secondary-color)); }

然后刷新——粒子变绿,按钮变蓝,标题阴影也跟着变,整个主题色瞬间迁移。这比全局搜索替换#ff416c安全十倍,因为所有颜色引用都走变量,不会漏掉某个border-color或box-shadow。

注意:--particle-count不建议设为0。虽然代码里做了防御性判断(Math.max(20, parseInt(count))),但设为0会触发粒子引擎空循环,浪费 CPU。最低安全值是20。

3.2 关键帧动画的定制方法:从@keyframes pulse到自定义心跳曲线

组件中所有 CSS 动画都基于@keyframes,而非 JSsetTimeout。好处是硬件加速、性能稳定、易于覆盖。核心动画有两个:

  • .pulse-btn的脉冲效果:用scale+opacity双属性变化模拟心跳;
  • .submit-btn提交后的“送达成功”反馈:用transform: scale(1.2) rotate(5deg)瞬间放大并微倾,再弹回。

查看love-page.html内联样式中的定义:

@keyframes pulse { 0% { transform: scale(1); opacity: 1; } 50% { transform: scale(1.05); opacity: 0.8; } 100% { transform: scale(1); opacity: 1; } } .pulse-btn { animation: pulse var(--animation-duration) infinite ease-in-out; }

这里的关键是ease-in-out—— 它让按钮“跳”得自然,不像linear那样机械。如果你想改成更温柔的“呼吸感”,把ease-in-out换成cubic-bezier(0.34, 1.56, 0.64, 1)(一个自定义缓动曲线),动画就会先慢、再快、再慢收尾,像真的在吸气呼气。

而提交按钮的成功反馈,代码里是这样触发的:

// form-handler.js 中 function showSuccess() { const btn = document.querySelector('.submit-btn'); btn.textContent = '心意已送达 ✅'; btn.style.transform = 'scale(1.2) rotate(5deg)'; // 0.3 秒后弹回 setTimeout(() => { btn.style.transform = 'scale(1) rotate(0deg)'; }, 300); }

为什么不用 CSS 动画?因为这个动作是一次性、条件触发的,且需要精确控制时间点(比如必须等表单校验通过后才执行)。JS 控制更灵活,也方便后续加“失败重试”逻辑。

3.3 Canvas 粒子引擎参数详解:particle-engine.js中可调的 7 个核心字段

lib/particle-engine.js是整个动态效果的基石。它不依赖任何框架,纯原生 JS 实现。打开该文件,你会看到一个ParticleEngine类,其构造函数接收一个配置对象:

const engine = new ParticleEngine({ canvas: document.getElementById('particle-canvas'), count: parseInt(getComputedStyle(document.documentElement).getPropertyValue('--particle-count')), color: getComputedStyle(document.documentElement).getPropertyValue('--primary-color'), size: 4, // 粒子基础半径(px) speed: 0.8, // 初始水平速度(px/frame) gravity: 0.02, // 垂直方向加速度(模拟重力下坠) bounce: 0.7, // 碰撞边界后的速度保留率(0.7 = 反弹后剩 70% 速度) shape: 'heart' // 可选 'circle' | 'heart' | 'star' });

这 7 个参数,每一个都直接影响最终观感:

  • size: 太小(<2)看不见,太大(>8)像马赛克。4是平衡点,适配 1080P 屏幕;
  • speed: 控制粒子横向流动感。0.8让粒子缓慢漂移,1.5则像风吹过,适合“告白信随风飞走”场景;
  • gravity:0.02让粒子轻微下坠,形成自然弧线;设为0就是匀速直线,设为0.05则下坠明显,像雨滴;
  • bounce:0.7是真实橡胶球的反弹率,0.9就像弹力球,0.3则像湿毛巾砸地——调低它,能让粒子更快“安分下来”;
  • shape:'heart'是默认,但'star'在节日场景更应景;注意'star'渲染成本比'circle'高 30%,低端机慎用。

血泪经验:某次我把gravity错写成0.2(多了一个 0),粒子像被重力猛拽,3 秒内全砸到屏幕底部堆成一坨,用户以为页面崩了。后来我在engine.init()里加了校验:if (config.gravity > 0.1) console.warn('gravity 值过高,可能导致粒子异常堆积');—— 工程师的后悔药,就是提前埋好提示。


4. 交互逻辑与表单处理:从防抖提交到状态持久化,拒绝“点了没反应”的玄学问题

4.1 表单防重复提交的双重保险:DOM 层禁用 + JS 层锁

“点了没反应”是表白页最高频的投诉。根源往往是:用户手快连点,表单重复提交,后端返回 400 或 500,但前端没做任何反馈,按钮还亮着,用户以为“没点上”,继续狂点——恶性循环。

本组件用双重锁机制破局:

  1. DOM 层硬禁用:点击提交按钮瞬间,JS 立即执行:

    btn.disabled = true; btn.textContent = '发送中...';

    此时浏览器原生阻止再次点击,连 disabled 样式都自动生效(灰色 + 不可 hover)。

  2. JS 层软锁:在form-handler.js中维护一个isSubmitting标志位:

    let isSubmitting = false; function handleSubmit(e) { e.preventDefault(); if (isSubmitting) return; // 第二次点击直接退出 isSubmitting = true; // ... 执行校验与提交逻辑 // 提交完成后,无论成功失败,都重置 isSubmitting = false; }

为什么两层都要?因为 DOM 层禁用可被 JS 绕过(比如用户开控制台删掉disabled属性),而 JS 层锁能兜底。两者叠加,才是生产级防护。

4.2 输入校验的务实策略:不拦真爱,只拦乱码和超长

校验不是为了刁难用户,而是防止无效提交拖垮体验。本组件校验规则极简但有效:

  • 姓名字段:maxlength="12"+ JS 检查是否为空 + 过滤控制字符(\x00-\x1f);
  • 留言字段:maxlength="200"+ JS 检查是否为空 + 过滤连续空白符(\s{3,}替换为单空格);
  • 通用规则:禁止输入<script>、javascript:等 XSS 关键字(正则/<(script|iframe|img|a)[^>]*>/i)。

校验代码片段(form-handler.js):

function validateForm() { const name = document.querySelector('input[name="name"]').value.trim(); const message = document.querySelector('textarea[name="message"]').value.trim(); // 1. 非空检查 if (!name || !message) { alert('名字和留言都不能空哦~'); return false; } // 2. 长度检查(前端二次确认,防 maxlength 被绕过) if (name.length > 12 || message.length > 200) { alert('名字不能超过12字,留言不能超过200字'); return false; } // 3. XSS 关键字过滤(简单但有效) const badPatterns = [/<script/i, /<iframe/i, /javascript:/i]; if (badPatterns.some(pattern => pattern.test(name) || pattern.test(message))) { alert('请不要输入特殊代码哦'); return false; } return true; }

提示:这里没做“实时校验”(oninput 触发),因为表白场景下,用户更关注表达本身,而非打字时被频繁提示。校验只在提交前一刻执行,减少干扰。

4.3 状态持久化的轻量方案:localStorage 存什么、存多久、怎么清

用户填了一大段话,点错地方跳走了,回来发现全没了——这种挫败感,比表白被拒还伤人。本组件用localStorage做轻量持久化:

  • 存什么:只存name和message的值,不存按钮状态、不存粒子位置、不存音频播放进度(那些都是瞬态);
  • 存时机:每次input或textarea失去焦点(blur事件)时存,避免高频写入;
  • 存多久:设置 24 小时过期。存入时记录时间戳:
    function saveToStorage() { const now = Date.now(); localStorage.setItem('confession_draft', JSON.stringify({ name: nameInput.value, message: messageInput.value, timestamp: now })); }
  • 读取时机:页面加载后,检查timestamp是否在 24 小时内,是则自动填充:
    function loadFromStorage() { const draft = localStorage.getItem('confession_draft'); if (!draft) return; try { const data = JSON.parse(draft); if (Date.now() - data.timestamp < 24 * 60 * 60 * 1000) { nameInput.value = data.name; messageInput.value = data.message; } else { localStorage.removeItem('confession_draft'); } } catch (e) { localStorage.removeItem('confession_draft'); } }

这个方案的好处是:不增加后端负担,不依赖 Cookie,24 小时后自动失效,用户隐私可控。某次测试中,A同学填了 187 字留言,切 Tab 查资料 15 分钟后回来,内容完好,他当场截图发群:“这波属实贴心”。


5. 避坑指南:5 条真实踩过的坑与血泪解决方案

5.1 现象:手机端点击“开启心动”按钮无反应,PC 端正常

原因:iOS Safari 和部分安卓 WebView 对click事件有 300ms 延迟,且要求绑定事件的元素必须是“可点击”的(cursor: pointer或onclick属性)。本组件按钮用了button标签但未设cursor: pointer,导致 iOS 无法触发。
解决:在 CSS 中强制声明#start-btn { cursor: pointer; },并为所有交互元素添加touch-action: manipulation(提升触摸响应优先级)。

5.2 现象:粒子动画在 Chrome 115+ 上突然卡顿,CPU 占用飙升至 90%

原因:新版 Chrome 对requestAnimationFrame的节流策略更激进,当页面不在前台时,raf仍被调用但帧率极低,导致粒子位置计算累积误差,canvas.clearRect()清屏时出现大量重绘。
解决:在particle-engine.js的动画循环中加入可见性判断:

function animate() { if (!document.hidden) { // 页面在前台才执行 updateParticles(); render(); } requestAnimationFrame(animate); }

5.3 现象:表单提交后,页面刷新,用户看到白屏几秒

原因:默认form提交是同步的,会阻塞 UI 线程。即使你写了e.preventDefault(),如果后续 JS 逻辑(如fetch)没加await,也可能因异步未完成就进入下一帧。
解决:确保handleSubmit是async函数,且fetch调用前加await:

async function handleSubmit(e) { e.preventDefault(); if (isSubmitting) return; isSubmitting = true; btn.textContent = '发送中...'; try { const res = await fetch('/api/confess', { method: 'POST', body: formData }); if (res.ok) showSuccess(); } catch (err) { alert('发送失败,请重试'); } finally { isSubmitting = false; } }

5.4 现象:WPS 表格中粘贴 HTML 代码后,格式全乱,心形变成方块

原因:WPS 表格的 HTML 解析器极度简陋,不支持<canvas>、@keyframes、CSS 变量,甚至把<meta charset="utf-8">当作普通文本渲染。
解决:根本不要在 WPS 表格里运行 HTML。本资源是 Web 页面,正确用法是:用 Chrome/Firefox/Edge 打开.html文件,或部署到 Nginx/Apache 服务器。WPS 表格只适合编辑.csv或.xlsx数据,不是浏览器替代品。

5.5 现象:Ubuntu 系统下双击love-page.html打开,粒子不动,控制台报Failed to load resource: net::ERR_FILE_NOT_FOUND

原因:Ubuntu 默认文件管理器(Nautilus)用file://协议打开 HTML,但lib/下的 JS 文件路径是相对路径(如./lib/particle-engine.js),Chrome 会尝试从file:///home/user/加载,而实际文件在file:///home/user/project/下,路径错位。
解决:两种方案任选其一:

  • 方案 A(推荐):用python3 -m http.server 8000在项目根目录起一个本地 HTTP 服务,然后浏览器访问http://localhost:8000/love-page.html;
  • 方案 B:把lib/下所有 JS 内联到love-page.html的<script>标签中,彻底消灭外部引用(适合最终交付给非技术人员)。

6. 进阶技巧:如何把表白页嵌入现有项目、对接真实后端、并做成 PWA 离线可用

6.1 嵌入现有 Vue/React 项目:不改一行业务代码,用 iframe 安全隔离

很多同学问:“我的后台管理系统是 Vue 写的,能直接把表白页塞进去吗?”答案是:可以,但别直接import,用<iframe>最安全。

理由很实在:表白页的 CSS 变量、Canvas 动画、全局window事件监听,和 Vue 的响应式系统、CSS Scoped 机制天然冲突。强行融合,轻则样式错乱,重则v-model失效。

正确姿势是封装成独立路由页,用 iframe 嵌入:

<!-- Vue 项目中新建 LovePage.vue --> <template> <div class="love-embed-container"> <iframe src="/static/love-page.html" width="100%" height="600" frameborder="0" ref="loveIframe" @load="onIframeLoad" ></iframe> </div> </template> <script> export default { methods: { onIframeLoad() { // 可选:向 iframe 发送初始化消息 this.$refs.loveIframe.contentWindow.postMessage( { type: 'INIT', theme: 'dark' }, '*' ); } } } </script> <style scoped> .love-embed-container { margin: 20px auto; max-width: 800px; border-radius: 12px; overflow: hidden; box-shadow: 0 10px 30px rgba(0,0,0,0.15); } </style>

关键点:

  • src指向静态资源路径(/static/是 Vue CLI 默认静态目录),确保打包后仍可访问;
  • @load事件确保 iframe 加载完成后再通信;
  • postMessage是跨域通信唯一安全方式,表白页内需监听window.addEventListener('message', ...)接收指令。

这样,你的 Vue 项目完全不知道表白页内部怎么实现,表白页也绝不会污染 Vue 的数据流——各司其职,互不打扰。

6.2 对接真实后端:从 mock 接口到生产环境的三步平滑迁移

资源自带的fetch('/api/confess')是 mock 地址。迁移到真实后端,只需改三处:

文件修改点示例
form-handler.jsfetch()的 URLfetch('https://your-api.com/v1/confessions', { ... })
love-page.html<meta name="csrf-token">若后端需 CSRF Token,加<meta name="csrf-token" content="xxx">,JS 中读取document.querySelector('meta[name="csrf-token"]').getAttribute('content')
lib/form-handler.js响应处理逻辑将if (res.ok)改为if (res.status === 201),并解析await res.json()获取后端返回的id或url

特别提醒:永远不要在前端硬编码后端域名。某次某公司上线,开发环境用http://localhost:3000,生产环境用https://api.example.com,结果忘了改,表白页在生产环境疯狂请求本地 3000 端口,被安全扫描器标为“高危外连”。正确做法是:构建时注入环境变量,Vue CLI 用process.env.VUE_APP_API_BASE,纯 HTML 项目则用构建脚本(如sed -i 's|/api/confess|https://api.example.com/v1/confessions|g' love-page.html)。

6.3 打包成 PWA:让表白页像 App 一样安装到桌面,离线也能看粒子

PWA(Progressive Web App)能让网页获得 App 体验:添加到主屏幕、离线访问、推送通知。对表白页来说,“离线可用”是刚需——万一用户在地铁里打开,没信号,至少粒子动画和文案还能看。

实现只需三步:

  1. 写manifest.json(放在项目根目录):
{ "name": "致你", "short_name": "心动", "description": "一份可离线运行的浪漫表白页", "start_url": "/love-page.html", "display": "standalone", "background_color": "#ffffff", "theme_color": "#ff4b2b", "icons": [ { "src": "/assets/icon-192.png", "sizes": "192x192", "type": "image/png" }, { "src": "/assets/icon-512.png", "sizes": "512x512", "type": "image/png" } ] }

注意:start_url必须是相对路径,且与实际 HTML 文件名一致;icons需准备两个尺寸的 PNG,用 RealFaviconGenerator 一键生成。

  1. 注册 Service Worker(在love-page.html底部加):
<script> if ('serviceWorker' in navigator) { window.addEventListener('load', () => { navigator.serviceWorker.register('/sw.js') .then(reg => console.log('SW registered:', reg)) .catch(err => console.error('SW registration failed:', err)); }); } </script>
  1. 写sw.js(缓存核心资源):
const CACHE_NAME = 'love-v1'; const urlsToCache = [ '/love-page.html', '/lib/particle-engine.js', '/lib/sound-manager.js', '/lib/form-handler.js', '/assets/heart.png', '/assets/icon-192.png', '/assets/icon-512.png' ]; self.addEventListener('install', event => { event.waitUntil( caches.open(CACHE_NAME) .then(cache => cache.addAll(urlsToCache)) ); }); self.addEventListener('fetch', event => { event.respondWith( caches.match(event.request) .then(response => response || fetch(event.request)) ); });

完成这三步,用 Chrome 打开页面,地址栏会出现“安装”图标。点击即可添加到桌面,断网后刷新,粒子照常飘动——浪漫,不该被网络绑架。

从那以后我每次交付前端小工具,都强制走一遍 PWA 检查清单:manifest 是否合法、Service Worker 是否注册成功、Lighthouse 评分是否 ≥90。不是为了炫技,而是让用户在任何一个时刻、任何一种网络条件下,都能稳稳接住那份心意。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询