两款超浪漫爱心代码分享!简单易学,一键运行
大家好!今天给大家分享两款超级治愈、颜值超高的爱心代码,一款是静态唯美爱心,一款是动态跳动爱心,代码简洁、零基础也能轻松看懂,复制即可直接运行,不管是用来练手学习,还是当做浪漫小彩蛋送给朋友都非常合适。
两款代码均采用前端原生代码编写,无需搭建复杂环境,电脑自带浏览器即可打开运行,兼容性拉满,下面直接给大家分享完整源码+详细使用教程。
一、静态唯美爱心代码(简约治愈风)
第一款是静态粉色爱心,样式简约干净,配色温柔高级,整体页面极简干净,没有多余冗余元素,适合新手入门学习,代码结构简单,方便大家自定义修改颜色、大小等样式。
完整爱心动画 HTML 源码
完整爱心动画 HTML 源码
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF‑8" />
<meta name="viewport" content="width=device‑width,initial‑scale=1.0,maximum‑scale=1.0,user‑scalable=no" />
<title>爱心~</title>
<style>
:root{
--glow‑r1:10px;
--glow‑r2:22px;
}
html,body{
height:100%;
margin:0;
overflow:hidden;
font‑family:"微软雅黑",system‑ui,‑apple‑system,Segoe UI,Roboto,sans‑serif;
transition:background 1s;
background: radial‑gradient(1200px 600px at 50% 45%, rgba(255,255,255,.65), rgba(255,255,255,0) 60%),
radial‑gradient(circle at 50% 45%, #fff4f7 0%, #c9eaff 100%);
}
body.dark{
background: radial‑gradient(1200px 600px at 50% 45%, rgba(18,18,18,.8), rgba(0,0,0,0) 60%),
radial‑gradient(circle at 50% 45%, #121212 0%, #000000 100%);
color:#eee;
}
body.dark.eco{
--glow‑r1:6px;
--glow‑r2:14px;
}
.stage{
position:relative;
width:100%;
height:100%;
}
.msg{
position:absolute;
left:50%;
top:50%;
padding:10px 16px;
border‑radius:16px;
color:#fff;
font‑weight:700;
font‑size:clamp(14px,3vmin,22px);
text‑shadow:0 2px 8px rgba(0,0,0,.28);
opacity:0;
transform:translate(‑50%,‑50%) translate(var(--dx),var(--dy)) scale(.75) rotate(var(--rot));
animation: flight var(--dur) cubic‑bezier(.25,.7,.3,1) var(--delay) both;
box‑shadow:0 8px 26px rgba(0,0,0,.22);
will‑change: transform, opacity;
background: var(--bg);
}
body.dark .msg{
box‑shadow:none;
filter: drop‑shadow(0 0 var(--glow‑r1) var(--glow1, rgba(0,0,0,0)))
drop‑shadow(0 0 var(--glow‑r2) var(--glow2, rgba(0,0,0,0)));
}
@keyframes flight{
0%{
opacity:0;
transform:translate(‑50%,‑50%) translate(var(--dx),var(--dy)) scale(.75) rotate(var(--rot));
}
18%{
opacity:1;
}
45%{
transform:translate(‑50%,‑50%) translate(calc(var(--dx)*.2),calc(var(--dy)*.2)) scale(1.06);
}
70%{
transform:translate(‑50%,‑50%) translate(calc(var(--dx)*1.15),calc(var(--dy)*1.15)) scale(.95) rotate(var(--rot));
}
88%{
transform:translate(‑50%,‑50%) translate(calc(var(--heart‑x)*.6),calc(var(--heart‑y)*.6)) scale(.9) rotate(var(--heart‑rot));
}
100%{
opacity:1;
transform:translate(‑50%,‑50%) translate(var(--heart‑x),var(--heart‑y)) scale(1) rotate(var(--heart‑rot));
}
}
.msg::before,.msg::after{
content:'';
position:absolute;
left:50%;
top:0;
background:inherit;
border‑radius:50px 50px 0 0;
width:22px;
height:34px;
opacity:0;
transition:opacity .28s ease;
transform‑origin:0 100%;
transform:rotate(‑45deg);
z‑index:0;
}
.msg::after{
left:0;
transform‑origin:100% 100%;
transform:rotate(45deg);
}
.msg.heart‑mode{
color:#fff;
}
.msg .text{
position:relative;
z‑index:1;
}
.msg.heart‑mode::before,.msg.heart‑mode::after{
opacity:1;
}
.msg.locked{
animation:none;
transform:translate(‑50%,‑50%) translate(var(--heart‑x),var(--heart‑y)) scale(1) rotate(var(--heart‑rot));
opacity:1;
}
.msg.locked.pulse{
animation:pulse 2.2s ease‑in‑out 1;
}
.msg.locked::before,.msg.locked::after{
display:none;
}
body.dark .msg.locked{
filter:none;
}
.msg.locked .text{
text‑shadow:0 1px 2px rgba(0,0,0,.12);
}
@keyframes pulse{
0%,100%{
transform:translate(‑50%,‑50%) translate(var(--heart‑x),var(--heart‑y)) scale(1);
}
50% {
transform:translate(‑50%,‑50%) translate(var(--heart‑x),var(--heart‑y)) scale(1.08);
}
}
.msg.burst{
animation: burst 1.4s cubic‑bezier(.17,.6,.2,1) forwards;
}
@keyframes burst{
0%{
opacity:1;
transform:translate(‑50%,‑50%) translate(var(--heart‑x),var(--heart‑y)) scale(1);
filter:blur(0);
}
70%{
opacity:.9;
transform:translate(‑50%,‑50%) translate(calc(var(--heart‑x) + var(--bx)*0.7), calc(var(--heart‑y) + var(--by)*0.7)) scale(.9) rotate(var(--rot));
filter:blur(.3px);
}
100%{
opacity:0;
transform:translate(‑50%,‑50%) translate(calc(var(--heart‑x) + var(--bx)), calc(var(--heart‑y) + var(--by))) scale(.7) rotate(var(--rot));
filter:blur(1px);
}
}
.center‑title{
position:absolute;
left:50%;
top:50%;
transform:translate(‑50%,‑50%);
font‑weight:800;
letter‑spacing:.12em;
font‑size:clamp(18px,4vmin,28px);
color:rgba(255,105,180,.7);
text‑shadow:0 2px 10px rgba(255,105,180,.18);
opacity:.0;
pointer‑events:none;
animation:titleIn 1.2s ease 0.8s both;
}
body.dark .center‑title{
color:rgba(255,160,200,.9);
text‑shadow:0 0 18px rgba(255,130,200,.35);
}
@keyframes titleIn{
from{opacity:0; transform:translate(‑50%,‑55%);}
to{opacity:.95; transform:translate(‑50%,‑50%);}
}
.controls{
position:absolute;
right:18px;
bottom:18px;
display:flex;
gap:12px;
align‑items:center;
}
.btn{
padding:10px 14px;
border:none;
border‑radius:10px;
background:rgba(255,255,255,.5);
backdrop‑filter:blur(8px);
color:#222;
font‑weight:600;
cursor:pointer;
box‑shadow:0 5px 20px rgba(0,0,0,.22);
}
body.dark .btn{
background:rgba(255,255,255,.15);
color:#fff;
}
.snow{
position:fixed;
top:‑10px;
color:white;
user‑select:none;
pointer‑events:none;
z‑index:99;
text‑shadow:0 0 8px rgba(255,255,255,.8);
animation:snowfall linear forwards;
}
@keyframes snowfall{
0%{ transform:translateY(0) rotate(0deg); opacity:1;}
100%{ transform:translateY(100vh) rotate(360deg); opacity:0;}
}
#bgMusic {
display: none;
}
</style>
</head>
<body>
<div class="stage" id="stage">
<div class="center‑title" id="title">我爱你 ♥</div>
</div>
<div class="controls">
<button class="btn" id="replay">重放 / 切换背景</button>
</div>
<audio id="bgMusic" loop>
<source src="https://lf26‑music‑east.douyinstatic.com/obj/ies‑music‑hj/7495920705440140091.mp3" type="audio/mpeg">
<source src="https://lf26‑music‑east.douyinstatic.com/obj/ies‑music‑hj/7495920705440140091.mp3" type="audio/ogg">
</audio>
<script>
const tips=[
'想念在风里飘','月亮为你亮','喜欢你很久了','你是我的心动','温柔落在人间',
'星光都为你闪','梦里有你真好','世界因你柔软','心跳为你加速','有你真好',
'你是独一无二的光','遇见你是浪漫的开始','晚风也温柔了','你的笑融化星海',
'想陪你看星星','今夜的梦给你','宇宙都在偏爱你','你是人间的奇迹',
'拥抱要紧一点','想你成习惯了','一瞬也是永恒','你眼里有星星','你的温柔让我沦陷',
'风都是甜的','为你心动不止','想成为你的例外','心有桃花一片','你是我梦里的海',
'想和你看海','此刻的风想你','遇见你后时间变慢','所有浪漫都与你有关',
'想靠近你一点','你的名字是我心事','心软是因为你','想牵你的手看日落',
'每次想你星星都亮了','心跳藏不住','有你世界亮一点','甜在风里也在心里'
];
const stage = document.getElementById('stage');
const replay = document.getElementById('replay');
const title = document.getElementById('title');
const bgMusic = document.getElementById('bgMusic');
let dark = false;
let eco = false;
let snowRunning = false;
let snowFrame;
let lastSnowTime = 0;
let snowIntervalMs = 700;
let snowMax = 120;
let totalCurrent = 0;
let lockedCount = 0;
const directions = [
{dx:'0vw',dy:'‑80vh'},{dx:'0vw',dy:'80vh'},
{dx:'‑80vw',dy:'0vh'},{dx:'80vw',dy:'0vh'},
{dx:'‑70vw',dy:'‑70vh'},{dx:'70vw',dy:'‑70vh'},
{dx:'‑70vw',dy:'70vh'},{dx:'70vw',dy:'70vh'},
{dx:'0vw',dy:'‑60vh'},{dx:'0vw',dy:'60vh'},
{dx:'‑60vw',dy:'0vh'},{dx:'60vw',dy:'0vh'}
];
function rand(min,max){return Math.random()*(max‑min)+min;}
function hexToRgba(hex,alpha=1){
const h=hex.replace('#','');
const short=h.length===3;
const r=parseInt(short? h[0]+h[0] : h.slice(0,2),16);
const g=parseInt(short? h[1]+h[1] : h.slice(2,4),16);
const b=parseInt(short? h[2]+h[2] : h.slice(4,6),16);
return `rgba(${r},${g},${b},${alpha})`;
}
function randomStyle(){
const arr=[
['#ff758c','#ff7eb3'],
['#a18cd1','#fbc2eb'],
['#f6d365','#fda085'],
['#84fab0','#8fd3f4'],
['#f093fb','#f5576c'],
['#4facfe','#00f2fe'],
['#43e97b','#38f9d7'],
['#fa709a','#fee140'],
['#30cfd0','#330867'],
['#5ee7df','#b490ca']
];
const [c1,c2]=arr[Math.floor(Math.random()*arr.length)];
return {
bg:`linear‑gradient(135deg,${c1},${c2})`,
glow1:hexToRgba(c1,0.55),
glow2:hexToRgba(c2,0.35)
};
}
function heartScale(){
const minDim = Math.min(window.innerWidth, window.innerHeight);
let s = Math.round(minDim * 0.023);
s = Math.max(8, Math.min(22, s));
if (dark && eco) s = Math.max(8, Math.floor(s * 0.92));
return s;
}
function centerYOffset(){
const portrait = window.innerHeight > window.innerWidth;
const minDim = Math.min(window.innerWidth, window.innerHeight);
return portrait ? ‑minDim * 0.06 : 0;
}
function heartXY(t){
const s = heartScale();
const x = s * (16 * Math.sin(t)**3);
const y = ‑s * (13*Math.cos(t) ‑ 5*Math.cos(2*t) ‑ 2*Math.cos(3*t) ‑ Math.cos(4*t));
return {x: x+'px', y: (y + centerYOffset())+'px'};
}
function createMsg(i,total){
const el = document.createElement('div');
el.className = 'msg';
const d = directions[Math.floor(Math.random()*directions.length)];
el.style.setProperty('--dx', d.dx);
el.style.setProperty('--dy', d.dy);
el.style.setProperty('--rot', rand(‑55,55)+'deg');
const baseDur = dark && eco ? rand(6.2,7.6) : rand(6.8,8.6);
el.style.setProperty('--dur', baseDur+'s');
el.style.setProperty('--delay', (i*0.11 + rand(0,0.35))+'s');
const sty = randomStyle();
el.style.setProperty('--bg', sty.bg);
el.style.setProperty('--glow1', sty.glow1);
el.style.setProperty('--glow2', sty.glow2);
const t = (i / total) * Math.PI * 2;
const {x,y} = heartXY(t);
el.style.setProperty('--heart‑x', x);
el.style.setProperty('--heart‑y', y);
el.style.setProperty('--heart‑rot', rand(‑18,18)+'deg');
const span = document.createElement('span');
span.className = 'text';
span.textContent = tips[Math.floor(Math.random()*tips.length)];
el.appendChild(span);
const delay = parseFloat(el.style.getPropertyValue('--delay'));
const dur = parseFloat(el.style.getPropertyValue('--dur'));
const switchAt = delay + dur * 0.86;
const switchTimer = setTimeout(()=>{
el.classList.add('heart‑mode');
}, switchAt*1000);
el.addEventListener('animationend',()=>{
clearTimeout(switchTimer);
el.classList.add('heart‑mode','locked','pulse');
el.style.willChange = 'auto';
const spread = rand(140, 260);
const ang = rand(0, Math.PI*2);
const bx = Math.cos(ang)*spread;
const by = Math.sin(ang)*spread;
el.style.setProperty('--bx', bx+'px');
el.style.setProperty('--by', by+'px');
lockedCount++;
if(lockedCount===totalCurrent) onAllLocked();
}, {once:true});
stage.appendChild(el);
}
function finalBurst(){
stage.querySelectorAll('.msg.locked').forEach(el=>{
el.classList.remove('pulse');
const jitter = rand(0, 200);
setTimeout(()=> el.classList.add('burst'), jitter);
el.addEventListener('animationend',()=> el.remove(), {once:true});
});
title.style.opacity = 0;
}
function play(){
stage.querySelectorAll('.msg').forEach(n=>n.remove());
title.style.opacity = 0;
const minDim = Math.min(window.innerWidth, window.innerHeight);
let TOTAL;
if (minDim <= 420) {
TOTAL = dark ? (eco ? 80 : 110) : 150;
} else if (minDim <= 720) {
TOTAL = dark ? (eco ? 96 : 130) : 180;
} else {
TOTAL = dark ? (eco ? 96 : 140) : 200;
}
totalCurrent = TOTAL;
lockedCount = 0;
for(let i=0;i<TOTAL;i++) createMsg(i,TOTAL);
setTimeout(()=>{
title.style.opacity = 1;
}, 1400);
playMusic();
}
function playMusic() {
bgMusic.currentTime = 0;
bgMusic.play().catch(error => {
console.log('音乐播放需要用户交互:', error);
});
}
function createSnow(){
const snow=document.createElement('div');
snow.className='snow';
snow.textContent='❄';
snow.style.left=(Math.random()*100)+'vw';
snow.style.fontSize=(Math.random()*12+8)+'px';
snow.style.opacity=(Math.random()*0.8+0.2);
const duration=(Math.random()*5+6);
snow.style.animationDuration=duration+'s';
document.body.appendChild(snow);
setTimeout(()=>snow.remove(),duration*1000);
}
function loopSnow(ts){
if(!lastSnowTime) lastSnowTime=ts;
const delta=ts‑lastSnowTime;
if(delta>snowIntervalMs){
if(snowRunning){
const count = document.querySelectorAll('.snow').length;
if(count < snowMax) createSnow();
}
lastSnowTime=ts;
}
snowFrame=requestAnimationFrame(loopSnow);
}
function startSnow(){
snowRunning=true;
lastSnowTime=0;
loopSnow();
}
function stopSnow(){
snowRunning=false;
cancelAnimationFrame(snowFrame);
document.querySelectorAll('.snow').forEach(s=>s.remove());
}
function decideEco(){
const cores = navigator.hardwareConcurrency || 4;
const mem = navigator.deviceMemory || 4;
const area = window.innerWidth * window.innerHeight;
return (cores <= 4) || (mem <= 4) || (area >= 2400*1440);
}
function applyMode(){
document.body.classList.toggle('dark', dark);
if(dark){
eco = decideEco();
document.body.classList.toggle('eco', eco);
snowIntervalMs = eco ? 800 : 500;
snowMax = eco ? 100 : 180;
startSnow();
}else{
stopSnow();
document.body.classList.remove('eco');
}
}
function onAllLocked(){
if(dark){
snowIntervalMs = eco ? 700 : 400;
snowMax = eco ? 140 : 240;
}
setTimeout(()=>{
finalBurst();
}, 2000);
}
play();
replay.onclick = ()=>{
dark = !dark;
applyMode();
play();
};
let resizeTimer;
window.addEventListener('resize', ()=>{
clearTimeout(resizeTimer);
resizeTimer = setTimeout(()=> play(), 150);
});
document.addEventListener('click', () => {
if (bgMusic.paused) {
playMusic();
}
}, { once: true });
</script>
</body>
</html>
复制全部源码,粘贴记事本,保存为 xxx.html,双击即可打开运行。
代码说明
- 1、通过CSS伪元素before和after拼接形成爱心轮廓,利用圆角和旋转属性打造标准爱心形状;
- 2、页面采用弹性布局,让爱心自动居中展示,视觉效果整洁美观;
- 3、可自定义修改:修改代码中 #ff6b8b 可更换爱心颜色,修改width、height数值可调整爱心大小。
二、动态跳动爱心代码(浪漫动态风)
第二款是动态心跳爱心,也是全网超火的浪漫代码!爱心会持续缩放跳动,模拟心跳效果,氛围感直接拉满,动态效果丝滑不卡顿,非常适合用来做浪漫表白、节日小惊喜。
代码说明
- 1、在静态爱心的基础上,新增CSS动画关键帧,实现爱心周期性缩放,还原真实心跳跳动效果;
- 2、采用黑色背景衬托红色爱心,视觉对比强烈,动态效果更加醒目;
- 3、动画参数可自定义:修改animation后的1s可调整跳动速度,修改scale数值可调整跳动幅度,修改色值可更换爱心配色。
三、通用运行教程(零基础必看)
- 1、新建一个文本文档(.txt文件);
- 2、复制上面对应版本的完整代码,粘贴到文本文档中;
- 3、将文件后缀名 .txt 修改为 .html;
- 4、双击文件,即可用浏览器直接打开运行,看到爱心效果。
四、总结
这两款爱心代码都是极简的前端基础代码,没有复杂逻辑,非常适合新手练习CSS布局和动画效果。静态款简约百搭,动态款浪漫氛围感十足,大家可以根据自己的喜好修改颜色、大小、动画速度,打造专属的个性化爱心效果。
喜欢的话可以收藏起来,随时可以运行使用,也可以分享给身边的朋友!后续还会更新更多浪漫、趣味的极简代码,欢迎持续关注~
暂无评论,快来抢沙发吧!