Untitled
unknown
plain_text
22 days ago
28 kB
6
Indexable
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>粉色梦幻·巨型爱心 | 心动合成与回归</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
user-select: none;
}
body {
min-height: 100vh;
background: radial-gradient(circle at 30% 10%, #ffb7c5, #ff7a95);
display: flex;
justify-content: center;
align-items: center;
font-family: 'Segoe UI', 'Quicksand', 'Poppins', system-ui, sans-serif;
overflow: hidden;
position: relative;
cursor: default;
}
/* 主舞台容器 */
.heart-stage {
position: relative;
width: 100vw;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
/* 画布: 粒子与核心大爱心绘制 */
canvas {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: block;
pointer-events: none;
}
/* 封面层 (一开始的梦幻小爱心界面) */
.cover {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: radial-gradient(ellipse at center, #ffc0d0, #ff8aab);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 30;
backdrop-filter: blur(2px);
transition: opacity 0.8s cubic-bezier(0.2, 0.9, 0.4, 1.2), visibility 0.8s;
cursor: pointer;
text-align: center;
}
.cover.hide {
opacity: 0;
visibility: hidden;
pointer-events: none;
}
/* 返回按钮 (合成爱心后显示) */
.back-button {
position: absolute;
bottom: 8%;
left: 50%;
transform: translateX(-50%);
background: rgba(255, 240, 245, 0.9);
backdrop-filter: blur(12px);
border: none;
padding: 12px 32px;
border-radius: 60px;
font-size: 1.2rem;
font-weight: 600;
color: #ff4370;
letter-spacing: 1px;
cursor: pointer;
z-index: 40;
box-shadow: 0 8px 20px rgba(255, 80, 120, 0.5);
transition: all 0.3s ease;
font-family: inherit;
display: flex;
align-items: center;
gap: 8px;
opacity: 0;
visibility: hidden;
pointer-events: none;
border: 1px solid #ffb7c5;
}
.back-button.show {
opacity: 1;
visibility: visible;
pointer-events: auto;
animation: gentleRise 0.5s ease-out;
}
.back-button:hover {
background: #ffebf0;
transform: translateX(-50%) scale(1.03);
box-shadow: 0 12px 28px #ff6085;
color: #ff2a5c;
}
@keyframes gentleRise {
0% { opacity: 0; transform: translateX(-50%) translateY(20px); }
100% { opacity: 1; transform: translateX(-50%) translateY(0); }
}
/* 封面浮动小爱心区域 */
.floating-hearts {
position: absolute;
width: 100%;
height: 100%;
overflow: hidden;
pointer-events: none;
z-index: 1;
}
.float-heart {
position: absolute;
font-size: 26px;
filter: drop-shadow(0 6px 12px rgba(255, 50, 90, 0.4));
opacity: 0.85;
animation: floatUp 7s linear infinite;
bottom: -60px;
left: calc(10% + 80% * var(--random-x, 0.5));
animation-delay: calc(var(--delay, 0) * 1s);
transform: scale(var(--scale, 1));
}
@keyframes floatUp {
0% {
transform: translateY(0) rotate(0deg) scale(var(--scale, 1));
opacity: 0.9;
}
75% {
opacity: 0.7;
}
100% {
transform: translateY(-120vh) rotate(12deg) scale(0.5);
opacity: 0;
}
}
/* 封面文字内容 */
.cover-content {
position: relative;
z-index: 12;
text-align: center;
transform: translateY(-8%);
}
.cover-title {
font-size: 3.6rem;
font-weight: 700;
color: #fffaf2;
text-shadow: 0 10px 25px #ff4d70;
letter-spacing: 3px;
margin-bottom: 20px;
background: rgba(255, 200, 210, 0.4);
padding: 12px 32px;
border-radius: 100px;
display: inline-block;
backdrop-filter: blur(8px);
}
.cover-sub {
font-size: 1.5rem;
color: #fff5f0;
background: rgba(255, 110, 135, 0.5);
padding: 8px 28px;
border-radius: 50px;
backdrop-filter: blur(6px);
margin-top: 18px;
font-weight: 500;
}
.click-hint {
position: absolute;
bottom: 12%;
left: 0;
right: 0;
font-size: 1.2rem;
font-weight: 500;
color: #fff0e6;
background: #ff7f9a90;
width: fit-content;
margin: 0 auto;
padding: 8px 28px;
border-radius: 60px;
backdrop-filter: blur(10px);
animation: softPulse 1.4s infinite;
pointer-events: none;
}
@keyframes softPulse {
0% { transform: scale(1); background: #ff7f9a90; }
50% { transform: scale(1.05); background: #ff6685b0; }
100% { transform: scale(1); background: #ff7f9a90; }
}
/* 合成结束后的温馨悬浮语(非必要,装饰) */
.heart-tip {
position: absolute;
bottom: 20%;
left: 0;
right: 0;
text-align: center;
font-size: 1.1rem;
color: #fff3e6;
background: rgba(255, 100, 130, 0.5);
backdrop-filter: blur(6px);
width: fit-content;
margin: 0 auto;
padding: 5px 18px;
border-radius: 40px;
z-index: 15;
opacity: 0;
transition: opacity 0.6s;
pointer-events: none;
white-space: nowrap;
}
.heart-tip.show {
opacity: 0.85;
}
@media (max-width: 650px) {
.cover-title { font-size: 2rem; padding: 6px 20px; }
.cover-sub { font-size: 1rem; }
.click-hint { font-size: 0.85rem; bottom: 10%;}
.back-button { padding: 8px 24px; font-size: 1rem; bottom: 6%;}
.heart-tip { font-size: 0.7rem; white-space: nowrap; bottom: 18%;}
}
</style>
</head>
<body>
<div class="heart-stage">
<!-- 背景画布(粒子+巨型爱心) -->
<canvas id="heartCanvas" width="800" height="800"></canvas>
<!-- 封面层,内含浮动小爱心 -->
<div class="cover" id="coverLayer">
<div class="floating-hearts" id="floatHeartsContainer"></div>
<div class="cover-content">
<div class="cover-title">💗 粉色梦境 💗</div>
<div class="cover-sub">✨ 轻触 · 凝聚永恒之心 ✨</div>
</div>
<div class="click-hint">🌸 点击任意位置 · 巨型爱心诞生 🌸</div>
</div>
<!-- 返回封页按钮(爱心合成后展示) -->
<button class="back-button" id="backBtn">💞 重返梦幻扉页 💞</button>
<div class="heart-tip" id="heartTip">❤️ 巨型爱心 · 为你跳动 ❤️</div>
</div>
<script>
(function(){
// ---------------- DOM 元素 ----------------
const canvas = document.getElementById('heartCanvas');
const ctx = canvas.getContext('2d');
const coverDiv = document.getElementById('coverLayer');
const backBtn = document.getElementById('backBtn');
const heartTipDiv = document.getElementById('heartTip');
const heartsContainer = document.getElementById('floatHeartsContainer');
let width, height;
let particles = []; // 粒子系统
let animationId = null;
// 状态机
let synthesizing = false; // 正在合成
let heartFormed = false; // 巨型爱心已经完成
let synthesisProgress = 0; // 0..1 (主要用于过渡)
// 心形目标点集合 (巨型爱心轮廓)
let targetPoints = [];
let heartbeatTime = 0;
// ---------------- 巨型爱心参数 (更大,更饱满) ----------------
// 经典心形曲线: x = 16 * sin(t)^3 , y = 13cos(t) - 5 cos(2t) - 2cos(3t) - cos(4t)
// 为了显示更大爱心,大幅放大 (scaleFactor 从2.8提升到3.5左右,且偏移中心)
function getHeartPoint(t, scaleFactor = 3.55, offsetX, offsetY) {
const xRaw = 16 * Math.pow(Math.sin(t), 3);
const yRaw = 13 * Math.cos(t) - 5 * Math.cos(2*t) - 2*Math.cos(3*t) - Math.cos(4*t);
let px = xRaw * scaleFactor;
let py = -yRaw * scaleFactor; // 翻转Y轴让爱心正立
px += offsetX;
py += offsetY;
return { x: px, y: py };
}
// 生成巨型爱心轮廓点 (增加密度,爱心更饱满)
function generateGiantHeartPoints(numPoints = 240, scale = 3.65, centerX, centerY) {
const points = [];
for (let i = 0; i < numPoints; i++) {
const t = (i / numPoints) * Math.PI * 2;
const { x, y } = getHeartPoint(t, scale, centerX, centerY);
points.push({ x, y, t });
}
return points;
}
// ----- 粒子系统初始化 (封面阶段 小粒子四处飘散) -----
function initParticlesForCover(count = 400) {
const newParticles = [];
for (let i = 0; i < count; i++) {
newParticles.push({
x: Math.random() * width,
y: Math.random() * height,
vx: (Math.random() - 0.5) * 1.0,
vy: (Math.random() - 0.5) * 0.8 - 0.2,
size: 3 + Math.random() * 9,
alpha: 0.6 + Math.random() * 0.4,
originalColor: `hsl(${340 + Math.random() * 25}, 85%, 68%)`,
targetX: null,
targetY: null,
isMovingToHeart: false
});
}
return newParticles;
}
// 为所有粒子分配巨型爱心上的目标点 (合成时调用)
function assignGiantHeartTargets() {
if (!targetPoints.length) return;
for (let i = 0; i < particles.length; i++) {
// 随机从心形点集里选取一个目标,保证爱心轮廓被填满,视觉密集成巨型爱心
const randomTarget = targetPoints[ Math.floor(Math.random() * targetPoints.length) ];
particles[i].targetX = randomTarget.x;
particles[i].targetY = randomTarget.y;
particles[i].isMovingToHeart = true;
// 停止随机飘移速度
particles[i].vx = 0;
particles[i].vy = 0;
}
}
// 合成动画:逐渐移动到目标巨型爱心位置
function updateSynthesisAnimation(speed = 0.058) {
let allArrived = true;
for (let p of particles) {
if (p.isMovingToHeart && p.targetX !== null && p.targetY !== null) {
const dx = p.targetX - p.x;
const dy = p.targetY - p.y;
const distance = Math.hypot(dx, dy);
if (distance > 1.5) {
allArrived = false;
// 优雅移动, 先快后慢 效果自然
const moveX = dx * speed;
const moveY = dy * speed;
p.x += moveX;
p.y += moveY;
// 逐渐缩小粒子使爱心更精致
p.size *= 0.99;
if (p.size < 2.2) p.size = 2.2;
} else {
p.x = p.targetX;
p.y = p.targetY;
}
}
}
if (allArrived && particles.length > 0 && !heartFormed) {
// 合成完毕 巨型爱心凝聚完成
synthesizing = false;
heartFormed = true;
// 显示返回按钮 + 温馨tip
backBtn.classList.add('show');
heartTipDiv.classList.add('show');
// 过几秒tip淡一点但保留
setTimeout(() => {
if(heartTipDiv) heartTipDiv.style.opacity = '0.6';
}, 3000);
}
}
// 心跳脉动效果: 让已经成型的爱心微微缩放 (模拟跳动)
function applyHeartbeatToParticles() {
if (!heartFormed) return;
heartbeatTime += 0.028;
const beatScale = 1 + Math.sin(heartbeatTime * 9) * 0.022;
const centerX = width / 2;
const centerY = height / 2 - 18; // 视觉中心微调
for (let p of particles) {
if (p.targetX && p.targetY) {
const dx = p.targetX - centerX;
const dy = p.targetY - centerY;
const originalDist = Math.hypot(dx, dy);
if (originalDist > 0.1) {
const angle = Math.atan2(dy, dx);
const newDist = originalDist * beatScale;
p.x = centerX + Math.cos(angle) * newDist;
p.y = centerY + Math.sin(angle) * newDist;
} else {
p.x = p.targetX;
p.y = p.targetY;
}
}
}
}
// 绘制所有粒子 (爱心形状或漂浮粒子)
function drawParticles() {
for (let p of particles) {
ctx.beginPath();
// 使用粉红/亮粉色径向渐变
const grad = ctx.createRadialGradient(p.x-2, p.y-2, 2, p.x, p.y, p.size);
grad.addColorStop(0, '#ffe0e8');
grad.addColorStop(0.6, '#ff5f89');
grad.addColorStop(1, '#ff2f60');
ctx.fillStyle = grad;
ctx.shadowBlur = 8;
ctx.shadowColor = '#ff7f9f';
ctx.arc(p.x, p.y, p.size * 0.72, 0, Math.PI * 2);
ctx.fill();
// 高光小白点
ctx.beginPath();
ctx.fillStyle = '#fff9fc';
ctx.arc(p.x-1.5, p.y-1.5, p.size*0.22, 0, Math.PI*2);
ctx.fill();
}
ctx.shadowBlur = 0;
}
// 封面阶段 自然飘动的粒子更新
function updateFloatingParticles() {
for (let p of particles) {
p.x += p.vx;
p.y += p.vy;
// 柔和边界环绕 (避免消失)
if (p.x < -30) p.x = width + 20;
if (p.x > width + 30) p.x = -20;
if (p.y < -40) p.y = height + 30;
if (p.y > height + 40) p.y = -30;
// 随机微飘
p.vx += (Math.random() - 0.5) * 0.08;
p.vy += (Math.random() - 0.5) * 0.06;
let maxSpeed = 1.1;
p.vx = Math.min(maxSpeed, Math.max(-maxSpeed, p.vx));
p.vy = Math.min(maxSpeed, Math.max(-maxSpeed, p.vy));
}
}
// 绘制额外巨型爱心光晕轮廓(增强大爱心视觉)
function drawGiantHeartGlow() {
if (!heartFormed) return;
if (targetPoints.length === 0) return;
ctx.save();
ctx.beginPath();
const step = Math.PI * 2 / 220;
for (let i = 0; i <= 220; i++) {
const t = i * step;
const pt = getHeartPoint(t, 3.7, width/2, height/2 - 18);
if (i === 0) ctx.moveTo(pt.x, pt.y);
else ctx.lineTo(pt.x, pt.y);
}
ctx.closePath();
ctx.shadowBlur = 22;
ctx.shadowColor = '#ff4870';
ctx.strokeStyle = '#ffb0ca';
ctx.lineWidth = 6;
ctx.stroke();
ctx.shadowBlur = 8;
ctx.strokeStyle = '#fff9ef';
ctx.lineWidth = 2.5;
ctx.stroke();
// 增加内发光
ctx.beginPath();
for (let i = 0; i <= 220; i++) {
const t = i * step;
const pt = getHeartPoint(t, 3.68, width/2, height/2 - 18);
if (i === 0) ctx.moveTo(pt.x, pt.y);
else ctx.lineTo(pt.x, pt.y);
}
ctx.strokeStyle = '#ff80a2';
ctx.lineWidth = 2;
ctx.stroke();
ctx.restore();
}
// 额外增加满屏飘浮小光晕(爱心形成时背景浪漫)
function drawBackgroundSparkles() {
if (!heartFormed) return;
for (let i = 0; i < 80; i++) {
if (i % 2 === 0) continue; // 轻量点缀
const fx = (Math.sin(heartbeatTime * 2 + i) * 0.2 + 0.5) * width;
const fy = (Math.cos(heartbeatTime * 1.7 + i) * 0.15 + 0.5) * height;
ctx.beginPath();
ctx.fillStyle = `rgba(255, 200, 210, ${0.2 + Math.sin(heartbeatTime * 3 + i) * 0.1})`;
ctx.arc(fx, fy, 3, 0, Math.PI*2);
ctx.fill();
}
}
// ----- 主渲染循环 (状态机) -----
function render() {
if (!ctx) return;
ctx.clearRect(0, 0, width, height);
// 梦幻粉色渐变背景
const gradBack = ctx.createLinearGradient(0, 0, width, height);
gradBack.addColorStop(0, '#ffb8ca');
gradBack.addColorStop(1, '#ff90ad');
ctx.fillStyle = gradBack;
ctx.fillRect(0, 0, width, height);
ctx.globalCompositeOperation = 'lighter';
if (synthesizing) {
// 合成动画:粒子向巨型爱心聚集
drawParticles();
updateSynthesisAnimation(0.062);
}
else if (heartFormed) {
// 巨型爱心已形成: 呼吸脉动 + 光效 + 粒子精美跳动
applyHeartbeatToParticles();
drawParticles();
drawGiantHeartGlow();
drawBackgroundSparkles();
}
else {
// 初始封面模式: 自由飘浮粒子 + 轻盈背景
drawParticles();
updateFloatingParticles();
}
ctx.globalCompositeOperation = 'source-over';
// 增加一层柔光
ctx.fillStyle = '#ffb7c520';
ctx.fillRect(0, 0, width, height);
requestAnimationFrame(render);
}
// ---------- 重置所有状态,回到粉色封面 (保留返回功能) ----------
function resetToCover() {
// 重置状态变量
synthesizing = false;
heartFormed = false;
synthesisProgress = 0;
heartbeatTime = 0;
// 重新生成粒子 (新的大批量漂浮小爱心粒子)
particles = initParticlesForCover(440);
// 清除爱心目标点引用
targetPoints = [];
for(let p of particles) {
p.isMovingToHeart = false;
p.targetX = null;
p.targetY = null;
}
// 隐藏返回按钮 和提示语
backBtn.classList.remove('show');
heartTipDiv.classList.remove('show');
heartTipDiv.style.opacity = '';
// 让封面层重新出现 (移除hide)
coverDiv.classList.remove('hide');
// 重新生成封面浮动小爱心 (丰富视觉效果)
regenerateCoverFloatingHearts();
// 重新适应画布目标 (确保所有粒子在尺寸内)
resizeAndAlignParticles();
}
// 辅助:让浮动小爱心封面重新丰盈
function regenerateCoverFloatingHearts() {
if(heartsContainer) {
heartsContainer.innerHTML = '';
const newCount = 48;
for(let i=0;i<newCount;i++) {
const heartDiv = document.createElement('div');
heartDiv.className = 'float-heart';
const scale = 0.6 + Math.random() * 1.3;
heartDiv.style.setProperty('--scale', scale);
heartDiv.style.left = Math.random() * 100 + '%';
heartDiv.style.animationDuration = 4 + Math.random() * 6 + 's';
heartDiv.style.animationDelay = Math.random() * 8 + 's';
heartDiv.style.fontSize = (18 + Math.random() * 30) + 'px';
const icons = ['💖','🌸','💗','💕','💓','✨','💞','🌷','💘'];
heartDiv.innerHTML = icons[Math.floor(Math.random() * icons.length)];
heartsContainer.appendChild(heartDiv);
}
// 动态补充
setInterval(() => {
if(coverDiv && !coverDiv.classList.contains('hide') && heartsContainer.children.length < 70) {
const extraHeart = document.createElement('div');
extraHeart.className = 'float-heart';
extraHeart.style.left = Math.random()*100+'%';
extraHeart.style.animationDuration = 3+Math.random()*6+'s';
extraHeart.style.fontSize = (16+Math.random()*28)+'px';
extraHeart.innerHTML = ['💖','🌸','💗','💞','💓','✨'][Math.floor(Math.random()*6)];
heartsContainer.appendChild(extraHeart);
setTimeout(()=> extraHeart.remove(), 7500);
}
}, 2000);
}
}
// 尺寸变更时调整粒子位置并重新生成心形轮廓时不影响已合成状态
function resizeAndAlignParticles() {
if(!heartFormed && !synthesizing) {
// 封面或未合成,重新随机分布粒子 适配新窗口
particles = initParticlesForCover(420);
} else if(heartFormed) {
// 如果已经合成爱心但改变窗口大小,重新映射巨型爱心位置,保持爱心展示
const centerX = width/2;
const centerY = height/2 - 18;
const newGiantTargets = generateGiantHeartPoints(260, 3.72, centerX, centerY);
targetPoints = newGiantTargets;
for(let i=0; i<particles.length && i<targetPoints.length; i++) {
if(particles[i]) {
particles[i].targetX = targetPoints[i % targetPoints.length].x;
particles[i].targetY = targetPoints[i % targetPoints.length].y;
particles[i].x = particles[i].targetX;
particles[i].y = particles[i].targetY;
particles[i].isMovingToHeart = true;
}
}
}
}
// 开启合成(封面点击时调用)
function startGiantHeartSynthesis() {
if(synthesizing || heartFormed) return;
synthesizing = true;
// 生成巨型爱心目标点(更大更明显)
const centerX = width/2;
const centerY = height/2 - 18;
targetPoints = generateGiantHeartPoints(280, 3.78, centerX, centerY);
assignGiantHeartTargets();
}
// ----- 封面点击触发合成 -----
function onCoverClick(e) {
if(heartFormed || synthesizing) return;
coverDiv.classList.add('hide');
startGiantHeartSynthesis();
}
// 返回按钮逻辑:重置所有,回到封面页
function onBackToCover() {
if(synthesizing) return; // 合成中不能打断过于生硬,但可以稍后重置
resetToCover();
}
// ----- 窗口自适应 -----
function resizeCanvas() {
width = window.innerWidth;
height = window.innerHeight;
canvas.width = width;
canvas.height = height;
if(!heartFormed && !synthesizing) {
particles = initParticlesForCover(430);
} else if(heartFormed) {
const centerX = width/2;
const centerY = height/2 - 18;
const newTargets = generateGiantHeartPoints(280, 3.75, centerX, centerY);
targetPoints = newTargets;
for(let i=0; i<particles.length && i<targetPoints.length; i++) {
if(particles[i]) {
particles[i].targetX = targetPoints[i % targetPoints.length].x;
particles[i].targetY = targetPoints[i % targetPoints.length].y;
particles[i].x = particles[i].targetX;
particles[i].y = particles[i].targetY;
}
}
} else if(synthesizing) {
// 合成中调整尺寸需谨慎: 重新生成目标且粒子重新映射
const centerX = width/2;
const centerY = height/2 - 18;
const freshTargets = generateGiantHeartPoints(280, 3.75, centerX, centerY);
targetPoints = freshTargets;
for(let i=0; i<particles.length; i++) {
if(particles[i].isMovingToHeart) {
const randTarget = targetPoints[Math.floor(Math.random() * targetPoints.length)];
particles[i].targetX = randTarget.x;
particles[i].targetY = randTarget.y;
}
}
}
}
// 事件绑定
coverDiv.addEventListener('click', onCoverClick);
backBtn.addEventListener('click', onBackToCover);
window.addEventListener('resize', () => {
resizeCanvas();
});
// 初始化
resizeCanvas();
regenerateCoverFloatingHearts();
particles = initParticlesForCover(440);
// 启动动画循环
animationId = requestAnimationFrame(render);
})();
</script>
</body>
</html>Editor is loading...
Leave a Comment