爱心代码这种东西,网上随便一搜就是一大把,什么C语言版、Python版、HTML版,看起来都挺唬人。但真要我选,我会直接说:把爱心代码做成一个HTML网页,是最实在的玩法。不用装Python,不用开编译器,一台手机一部电脑,双击文件就能在浏览器里看到动态效果,发给对方也是秒开。这篇东西我打算把从纯CSS画一颗静态爱心,到Canvas粒子爱心,再到加交互特效的完整过程全部写出来,每个阶段的代码都能直接复制运行。适合纯新手拿来练手,也适合想要个现成爱心网页的人拿去改一改直接用。
1. 项目思路与效果拆解
1.1 这套爱心代码要做什么
先说说我为什么要写这套东西。你打开搜索引擎敲“爱心代码”,排在前面的要么是一大段C语言代码,黑窗口里跑个字符拼出来的心,要么是Python turtle画出来的循环线条。这两种不是说不好,但你扪心自问:做出来之后,你能把它发给别人看吗?C语言编出来的exe,对方可能因为杀毒软件直接拦截;Python代码发过去,对方电脑还得装着Python环境。兜一圈下来,还是HTML网页最省事。
我给自己定的目标是做三个版本,难度从低到高:
- 版本一:纯CSS代码画一颗爱心,不写一行JavaScript,适合刚入门的新手理解盒子模型、伪元素和transform。
- 版本二:用Canvas画一颗由几百个粒子组成的爱心,粒子从四周飞过来,最终汇聚成爱心形状,这版就有点视觉冲击力了。
- 版本三:在版本二的基础上加上点击屏幕出现粒子爆炸特效、背景渐变、音乐开关、文字展示等,最终整合成一个可以直接发出去的完整单文件网页。
三个版本共用同一个核心知识点:爱心形状到底怎么来的。这个搞明白,后面所有漂亮效果都是在同一个底子上做加法。
1.2 为什么推荐HTML而不是其他语言
可能有人会问:网上最火的那个爱心代码不是C语言写的吗?对,没错。之前有部电视剧里出现过一段控制台爱心代码,很多人跑去搜“李峋爱心代码”,搜出来的多数是C语言版本。C语言版本确实有视觉冲击,字符像雪花一样在控制台里飘,慢慢拼出一个心形,说实话挺帅。但它的硬伤也很明显:你不懂编译就跑不起来,只能干瞪眼;而且它在控制台里运行,想要加点背景音乐、照片这些,操作难度直接翻倍。
HTML版就不一样。HTML本身是浏览器直接解释的,不需要任何编译环境;CSS负责样式和动画;JavaScript负责逻辑和粒子效果。三者结合,能做出来的效果上限非常高,下限又足够低——就算你一行JS不会,光用CSS也能折腾出不错的样式。我的建议很直接:学这个项目,认准HTML+CSS+JavaScript这一条线就对了。学完之后你会发现,不只是爱心,以后想做任何网页小特效,这套底子都能复用。
2. 基础版:纯CSS画一颗标准爱心
2.1 爱心的几何原理:两个圆加一个方块
先讲原理,这个原理很重要,因为它决定了你对整个项目底层结构的理解。一颗标准的心形,可以拆成三部分:一个旋转45度的正方形,一个在正方形左边的大圆,一个在正方形上方的大圆。三块拼在一起,就是爱心上半部分的两个圆弧和下半部分的尖尖角。
我知道你在想什么:一个小方块加两个圆,这也太朴素了吧?对,就是这么朴素。很多看起来高级的视觉效果,底层就是几何拼接。CSS里要实现这个,我习惯用一个div,加上它的两个伪元素::before和::after。div本身作为旋转45度的正方形,::before放在左边当“左肺”,::after放在上方当“右肺”…不对,准确说是“上方那个圆管左边那个圆”,大家理解成两个圆弧就行。
这里有细节要讲清楚:div转了45度之后,伪元素的位置计算很容易踩坑。我的做法是,先让div旋转45度,这时候它看起来像一个菱形;然后::before相对div偏移到左侧,::after偏移到上方,两者都是圆形,半径和正方形边长一样。最终效果就是正方形左下那条边被左边的圆盖住,右上那条边被上面的圆盖住,露出来的轮廓刚好是一个爱心。
2.2 手把手写出第一颗静态爱心
完整代码如下。新建一个index.html,把代码粘进去,双击用浏览器打开就能看到:
<!DOCTYPE html> <html lang="zh-cn"> <head> <meta charset="utf-8"> <title>爱心</title> <style> body { min-height: 100vh; display: flex; align-items: center; justify-content: center; background: #111; } .heart { width: 120px; height: 120px; background: #ff4d6d; transform: rotate(45deg); position: relative; } .heart::before, .heart::after { content: ""; width: 120px; height: 120px; background: #ff4d6d; border-radius: 50%; position: absolute; } .heart::before { left: -60px; top: 0; } .heart::after { top: -60px; left: 0; } </style> </head> <body> <div class="heart"></div> </body> </html>这段代码里最关键的是伪元素定位。::before的left值取-60px,也就是往左挪半个边长,圆心的x坐标落在div左边缘,圆的上边框刚好和div顶端对齐;::after的top值取-60px,圆心y坐标落在div上边缘,圆的左边框和div左端对齐。两个圆和div三者同色、无边框,拼出来就是一个完整的爱心。
我建议你手动改几个值试试:把-60px改成-40px,看看爱心会变成什么怪样子;把border-radius去掉,又会变成什么。这种“故意改坏”的练习,比光看代码有效得多。
2.3 让爱心跳起来:心跳动画与常见坑点
静态爱心为的是理解结构,想要动态效果,就得在transform上做文章。这里有一个我踩过很深的坑:在animation的keyframes里只写了scale,没写rotate。因为动画过程中transform会被整体覆盖,父元素原本的rotate(45deg)直接被抹掉,导致爱心在动画一开始瞬间变成了一个正方形加两个圆的奇怪形状,跳了两下还会闪一下。
正确写法是把旋转和缩放绑在一起,永远不要单独漏掉rotate。我用的是下面这一段:
@keyframes beat { 0%, 100% { transform: rotate(45deg) scale(1); } 20% { transform: rotate(45deg) scale(1.15); } 35% { transform: rotate(45deg) scale(1); } 50% { transform: rotate(45deg) scale(1.3); } 70% { transform: rotate(45deg) scale(1); } } .heart { animation: beat 1.2s ease-in-out infinite; }这里的关键帧节奏我调过很多次,1.2秒一整个周期接近真实心跳频率。20%和50%的两次放大分别对应心脏收缩时“咚哒”的两个节奏点,比单纯匀速呼吸式的缩放要有生命力得多。你如果觉得跳太快,把1.2s改成1.5s;想更急促,改成0.9s也行,看你想表达的情绪。
2.4 给爱心加点质感:渐变与高光
纯色爱心看着有点扁平。我后来给爱心加了渐变,让颜色从深红过渡到浅粉,瞬间有了一点立体感。做法很简单,改background属性:
.heart { background: linear-gradient(135deg, #ff4d6d 0%, #ff85a1 100%); } .heart::before, .heart::after { background: linear-gradient(135deg, #ff4d6d 0%, #ff85a1 100%); }不过我还要提醒一句:因为爱心是由div和两个伪元素拼起来的,渐变方向如果设置得不一致,拼接处会看到明显的颜色断层。我实际用下来,三个元素都设置同样的135deg方向,视觉上基本看不出接缝。要是你还想加高光,可以在爱心中央叠一个半透明白色小圆,或者直接用radial-gradient做一层内发光,这两种办法我都试过,后者更自然。
3. 进阶版:Canvas粒子爱心
3.1 粒子爱心是怎么实现的
CSS版看完很多人会觉得不够惊艳。确实,要是就发一个静态红的爱心,对方大概只能说一句“哦”。真正能让人“哇”一下的,是那种几百个小点从四面八方飞过来,慢慢汇聚成一个爱心轮廓的粒子动画。这种效果就是Canvas粒子系统在做的事。
用大白话解释Canvas:它就像一块画板,你在上面画的每一个点、每一条线,都必须用JavaScript告诉它“从哪个坐标开始,画到哪个坐标,用什么颜色”。一帧画完,如果要动起来,就得清空画板,重新画下一帧。人眼的视觉暂留效应会把连续的画面拼成动画,原理和翻页动画书一样。
粒子系统的核心其实就三样东西:
- 一群粒子对象,每个粒子有当前位置和坐标目标位置,以及大小、颜色。
- 一个循环,每帧更新每个粒子的位置。
- 一个绘制动作,把每个粒子画到Canvas上。
“目标位置”怎么来?答案就是爱心公式。
3.2 爱心曲线的数学公式真没那么玄
这一步是整个粒子爱心能不能成型的灵魂。网上流传最广的爱心曲线参数方程是这样的:
x = 16 * sin³(t) y = 13 * cos(t) - 5 * cos(2t) - 2 * cos(3t) - cos(4t)这里的t从0取到2π,把每一个t代进去,就会得到一对(x, y),这就是爱心曲线上的一个点。t取得越密,点越多,爱心轮廓就越精细。t每增加0.02,一圈下来大概能收集三百多个点,足够用了。
为什么x和y要这么算?这个公式本身就是从数学上对心形曲线的拟合,你不用纠结它怎么推导出来的,只要知道它是程序员圈子里用了很多年的经典公式就行。真正需要注意的,其实是坐标变换:Canvas和数学坐标系不一样,数学坐标系y轴向上是正,Canvas的y轴向下是正。所以从公式算出y之后,在绘制时要把它取反,否则你会得到一个倒立的爱心。
还有一个坐标缩放问题。公式直接算出来的x、y范围大概在-16到16之间,直接画到Canvas上就变成指甲盖那么大。所以要把每个坐标乘以一个缩放系数,再加上屏幕中心点的偏移量。我的做法是:scale取canvas宽度除以20,这样在手机和电脑上都能自适应大小。
3.3 粒子从四面八方汇聚成爱心
粒子不能一开始就整齐排在爱心轮廓上,那样就失去了“汇聚”的感觉。我的做法是:在生成每个粒子的时候,给它一个随机的起始位置,位置范围是整个画布;同时让每个粒子从当前位置向目标位置移动。为了让运动过程更自然,我不会让粒子直线冲过去,而是加了一点缓动效果。
核心逻辑只有两行:
particle.x += (particle.targetX - particle.x) * 0.03; particle.y += (particle.targetY - particle.y) * 0.03;这样每次移动当前位置与目标位置距离的3%,越靠近目标越慢,最终停下来,形成一种从四周“飘过去”又渐渐减速落定的观感。系数0.03我亲测比较舒服,调太大粒子会碰一下就冲到终点,太小则聚拢太慢,看的人会等得不耐烦。
下面是完整可运行的粒子爱心HTML文件:
<!DOCTYPE html> <html lang="zh-cn"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>粒子爱心</title> <style> body { margin: 0; height: 100vh; background: #0d0d1a; overflow: hidden; } canvas { display: block; width: 100%; height: 100%; } </style> </head> <body> <canvas id="heartCanvas"></canvas> <script> const canvas = document.getElementById('heartCanvas'); const ctx = canvas.getContext('2d'); function resize() { canvas.width = window.innerWidth; canvas.height = window.innerHeight; } resize(); window.addEventListener('resize', resize); const cx = canvas.width / 2; const cy = canvas.height / 2; const scale = canvas.width / 20; const points = []; for (let t = 0; t < Math.PI * 2; t += 0.02) { const x = 16 * Math.pow(Math.sin(t), 3); const y = 13 * Math.cos(t) - 5 * Math.cos(2 * t) - 2 * Math.cos(3 * t) - Math.cos(4 * t); points.push({ x: x * scale + cx, y: -y * scale + cy }); } class Particle { constructor(targetX, targetY) { this.targetX = targetX; this.targetY = targetY; this.x = Math.random() * canvas.width; this.y = Math.random() * canvas.height; this.r = Math.random() * 2 + 0.5; this.speed = Math.random() * 0.03 + 0.015; } update() { this.x += (this.targetX - this.x) * this.speed; this.y += (this.targetY - this.y) * this.speed; } draw() { ctx.beginPath(); ctx.arc(this.x, this.y, this.r, 0, Math.PI * 2); ctx.fillStyle = 'rgba(255, 80, 120, 0.9)'; ctx.fill(); } } const particles = points.map(p => new Particle(p.x, p.y)); function animate() { ctx.clearRect(0, 0, canvas.width, canvas.height); particles.forEach(p => { p.update(); p.draw(); }); requestAnimationFrame(animate); } animate(); </script> </body> </html>几个我实际调过的参数可以直接抄:
- t的步长0.02。太密比如0.005,粒子数量会飙到一千多个,移动端容易掉帧;太稀又看不出爱心轮廓。0.02产生的粒子数量,是性能与视觉的平衡点。
- 粒子半径r随机在0.5到2.5之间,分散一点,避免所有粒子一样大显得死板。
- 颜色用的rgba粉色系,想要更少女的浅粉还是更艳的正红,直接改fillStyle里的rgb值。
这个版本跑起来之后,你会看到几百个粒子从屏幕各个角落慢慢飘向中间,最后拼成一个粉色的爱心轮廓。第一次跑出来那个瞬间,确实很有成就感。
3.4 再进一步:粒子连线与性能取舍
有的高级爱心代码里,粒子之间还会连出细线,距离近的粒子互相连线,形成一张布满光线的网。这个效果确实好看,但代价是性能。要计算每两个粒子之间的距离,粒子数量是三百个时,每帧就要算四万五千多次,手机端很容易卡。
如果想让粒子之间有一点连接的效果,我建议只连相邻的、距离很短的粒子,把判断阈值控制在30像素以内,并且只在粒子数量少于200时才启用。真要做完整版,我会把粒子数量降到150个左右再去连线,画面没空,帧率也能稳住。这个属于进阶玩法,等基础粒子动画跑通了再折腾不迟。
4. 高级版:加文字、背景、点击特效的完整网页
4.1 点击屏幕,炸出一片爱心粒子
粒子爱心成型之后,我想给它加一个互动:点击屏幕任意位置,那个地方会冒出一圈粉色粒子,带着随机速度往外扩散,然后渐渐消失。这个交互的实现思路很直接。
监听canvas的click事件,拿到点击坐标,然后在那个坐标位置一次性生成30到50个粒子,每个粒子的速度方向是随机的,同时给一个很小的重力加速度,让粒子往下坠一点点,看起来更像花瓣飘散而不是爆炸。
function burstParticles(x, y) { for (let i = 0; i < 40; i++) { const angle = Math.random() * Math.PI * 2; const speed = Math.random() * 4 + 1; burst.push({ x: x, y: y, vx: Math.cos(angle) * speed, vy: Math.sin(angle) * speed - 2, life: 1, r: Math.random() * 3 + 1 }); } }然后每一帧里,对这些burst粒子做更新:x和y分别加上vx和vy,vy额外加上一个0.08的重力,life每帧减少一点,当life小于等于0时,把这个粒子从数组里删掉。绘制的时候根据life调整透明度,实现“慢慢消失”的效果。
这里有个非常重要的性能习惯:粒子销毁一定要做。如果不删掉那些life为0的粒子,数组会越积越长,页面越跑越卡,最后直接卡死。我见过不少新手写的Canvas项目,粒子只有生没有死,跑几分钟后整个浏览器标签页直接崩溃。
4.2 背景渐变、流光文字和音乐开关
一个只有爱心的页面还是有点素。我做最终版的时候,给页面加了这几样东西:
- 背景用了深色渐变,从深蓝慢慢过渡到暗紫,比纯黑更有氛围。
- 爱心汇聚完成后,页面中央浮现出一行字,用CSS的渐变文字效果做成了从粉到金的过渡。
- 加了一个背景音乐开关,默认是关闭状态,点击按钮才播放,避免对方一打开页面被声音吓到。
渐变文字的核心CSS是background-clip: text配合透明文字颜色,这个技巧很多人知道但容易记不住,我直接贴出来:
.text { background: linear-gradient(90deg, #ff7eb3, #ffd166, #ff7eb3); background-size: 200% 100%; -webkit-background-clip: text; background-clip: text; color: transparent; animation: shine 3s ease infinite; }这里再配一个@keyframes,让背景position左右移动,文字就会产生一种流光扫过的感觉。我个人测试下来,渐变从左到右扫一个来回用3秒最自然,太快会显得晃眼,太慢又拖沓。
4.3 最终完整版代码,直接拿去用
下面是整合了粒子爱心、点击特效、渐变文字、音乐开关的完整版HTML。新建一个文件,复制进去,保存后双击打开即可:
<!DOCTYPE html> <html lang="zh-cn"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>爱心粒子</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { height: 100vh; overflow: hidden; background: linear-gradient(135deg, #0d0d1a 0%, #1a0b2e 50%, #2d1b4e 100%); font-family: "Microsoft YaHei", sans-serif; } canvas { display: block; width: 100%; height: 100%; cursor: pointer; } .text { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -70%); font-size: 28px; font-weight: bold; background: linear-gradient(90deg, #ff7eb3, #ffd166, #ff7eb3); background-size: 200% 100%; -webkit-background-clip: text; background-clip: text; color: transparent; animation: shine 3s ease infinite; pointer-events: none; white-space: nowrap; text-align: center; } .text small { display: block; font-size: 14px; font-weight: normal; margin-top: 10px; opacity: 0.8; } @keyframes shine { 0%, 100% { background-position: 0% 50%; } 50% { background-position: 100% 50%; } } #musicBtn { position: absolute; top: 20px; right: 20px; padding: 8px 16px; border: 1px solid rgba(255,255,255,0.5); border-radius: 20px; background: transparent; color: #fff; cursor: pointer; z-index: 10; } </style> </head> <body> <canvas id="heartCanvas"></canvas> <div class="text">I love you<small>你是我写过最浪漫的一行代码</small></div> <button id="musicBtn">音乐:关</button> <audio id="bgm" src="bgm.mp3" loop></audio> <script> const canvas = document.getElementById('heartCanvas'); const ctx = canvas.getContext('2d'); let w, h, cx, cy, scale; const points = []; const particles = []; const burst = []; function resize() { w = canvas.width = window.innerWidth; h = canvas.height = window.innerHeight; cx = w / 2; cy = h / 2; scale = w / 20; } resize(); window.addEventListener('resize', () => { resize(); points.length = 0; generatePoints(); }); function generatePoints() { for (let t = 0; t < Math.PI * 2; t += 0.02) { const x = 16 * Math.pow(Math.sin(t), 3); const y = 13 * Math.cos(t) - 5 * Math.cos(2*t) - 2 * Math.cos(3*t) - Math.cos(4*t); points.push({ x: x * scale + cx, y: -y * scale + cy }); } } generatePoints(); class Particle { constructor(targetX, targetY) { this.targetX = targetX; this.targetY = targetY; this.x = Math.random() * w; this.y = Math.random() * h; this.r = Math.random() * 2 + 0.5; } update() { this.x += (this.targetX - this.x) * 0.03; this.y += (this.targetY - this.y) * 0.03; } draw() { ctx.beginPath(); ctx.arc(this.x, this.y, this.r, 0, Math.PI * 2); ctx.fillStyle = 'rgba(255, 100, 140, 0.9)'; ctx.fill(); } } particles.push(...points.map(p => new Particle(p.x, p.y))); canvas.addEventListener('click', (e) => { for (let i = 0; i < 40; i++) { const angle = Math.random() * Math.PI * 2; const speed = Math.random() * 4 + 1; burst.push({ x: e.clientX, y: e.clientY, vx: Math.cos(angle) * speed, vy: Math.sin(angle) * speed - 2, life: 1, r: Math.random() * 3 + 1 }); } }); const bgm = document.getElementById('bgm'); const musicBtn = document.getElementById('musicBtn'); let musicOn = false; musicBtn.addEventListener('click', () => { musicOn = !musicOn; musicBtn.textContent = musicOn ? '音乐:开' : '音乐:关'; if (musicOn) bgm.play(); else bgm.pause(); }); function animate() { ctx.clearRect(0, 0, w, h); for (const p of particles) { p.update(); p.draw(); } for (let i = burst.length - 1; i >= 0; i--) { const b = burst[i]; b.x += b.vx; b.y += b.vy; b.vy += 0.08; b.life -= 0.02; if (b.life <= 0) { burst.splice(i, 1); continue; } ctx.beginPath(); ctx.arc(b.x, b.y, b.r, 0, Math.PI * 2); ctx.fillStyle = `rgba(255, 160, 180, ${b.life})`; ctx.fill(); } requestAnimationFrame(animate); } animate(); </script> </body> </html>几个使用上的提醒:
完整版HTML理论上可以当作独立文件发送,但如果你加了背景音乐,注意音乐文件的路径问题。直接把bgm.mp3放在和HTML同一个目录下,相对路径引用最省事;如果你想只发一个单独文件,可以把音乐转成base64塞进audio标签,但文件会很大,打开要等一会儿,我不推荐。
关于跨设备显示。这个页面我在电脑上开发时一切正常,拿到手机上一看,爱心偏到了左下角。问题出在viewport和尺寸自适应上。我给的代码已经加了viewport标签,canvas尺寸用window.innerWidth和window.innerHeight实时计算,并且窗口大小变化时会重新resize。如果你准备把页面嵌入到聊天软件的内置浏览器里打开,遇到显示异常先检查这两处。
5. 常见问题与避坑指南
5.1 复制代码打开却是白屏或一堆源码文字
这个问题我见得太多了。很多人从网上把HTML源码复制下来,粘贴到记事本里,直接发给对方。对方用聊天软件打开,看到的不是爱心而是一大堆代码文字。原因很简单:聊天软件内置的文本预览不会执行HTML,你必须把这个文本保存成扩展名为.html的文件,再用浏览器打开。
正确做法是:在电脑上新建一个文本文件,把代码粘贴进去,然后把这个文件重命名,把后缀.txt改成.html,最后用浏览器打开。手机上也一样,用文件管理器新建文档,粘贴进去,改后缀。发别人的时候,建议把文件后缀改为.html再发,不要发.txt。
还有一个取巧的办法是在浏览器地址栏输入data:text/html,然后接上URL编码后的HTML内容。这个我之前试过,确实能跑,但它对编码要求比较高,中文容易乱码,而且超长URL在不少聊天软件里会被截断,所以我不建议新手用这个方式发。老老实实发.html文件最稳妥。
5.2 动画不生效或爱心形状不对怎么办
动画不生效,十有八九是keyframes写错了,或者animation属性没挂上去。检查三件事:
- animation的name和@keyframes后面的名字必须完全一致,大小写都不能错。
- animation写在加载这个元素的CSS里,而不是写在body或父元素上。
- 如果元素同时设置了transform,keyframes里的每一帧transform必须包含rotate和scale,否则会闪跳。
爱心形状不对,先检查伪元素有没有加content: "",没有这个属性伪元素根本不会显示。再检查伪元素的left和top值,很多新手会把::before的left设成负数后忘设top,导致圆的位置偏了。
我把常见问题整理成一张表,方便你直接对着排查:
| 现象 | 原因 | 解决办法 |
|---|---|---|
| 页面白屏 | HTML结构不完整或标签闭合错误 | 用浏览器F12查看控制台报错,补全结构 |
| 中文乱码 | 文件不是UTF-8编码 | 用支持UTF-8的编辑器保存,保证head中有meta charset |
| 爱心变成方块 | transform被覆盖,rotate丢失 | keyframes每帧都写rotate(45deg) |
| 伪元素不显示 | 缺少content属性 | 给::before和::after加content: "" |
| 爱心倒置 | Canvas坐标系y轴方向错误 | 公式算出的y强制取反 |
| 粒子不汇聚 | 目标坐标计算错误 | 检查scale和中心点偏移是否生效 |
| 页面越来越卡 | 粒子只生成不销毁 | 为粒子设置生命周期,及时从数组移除 |
| 手机上爱心太小 | 没有适配视口 | 加viewport标签,用canvas宽高动态计算scale |
5.3 性能优化和个性化调整经验
最后分享一点性能优化的经验。粒子数量不是越多越好。我在低端安卓手机上实测过,粒子数超过800就会明显掉帧。如果想让效果更流畅,有这几个手段:
- 减少粒子数量,比如t步长从0.02改成0.03,粒子数直接减少三分之一。
- 缩小粒子半径范围,大半径粒子填充面积大,绘制压力也大。
- 用requestAnimationFrame做动画循环,它会和屏幕刷新率对齐,省电又流畅。
- 清屏时不要用全屏透明rect,直接用clearRect,性能更好。
个性化上,你可以改粒子颜色,让爱心变成紫色、蓝色,甚至做成渐变色粒子;也可以把爱心曲线公式替换成其他形状的曲线公式,比如玫瑰曲线、蝴蝶曲线,这些在开源社区都能搜到,替换掉坐标生成那一段就行。学到这里,你会发现自己对整条技术线的理解已经远超“复制粘贴”的层次了。
我自己做这套东西的时候,最有感触的一点是:很多人以为做一个爱心代码很难,其实从零到一也就是三步路,CSS画形状、Canvas撒粒子、再加点交互。更难的是你想清楚“你要用它表达什么”,想清楚了,代码只是表达工具。真要用它去做点什么,我的建议是别光甩一个文件过去就完事了。把它做成一个页面,里面放上你们的照片、一首对彼此有意义的歌、一句只有你们懂的话,再让爱心粒子在前面开场,效果完全不一样。这个方向,才算把一套爱心代码真正做活了。