<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>心跳倒计时与烟火绽放</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
min-height: 100vh;
background: linear-gradient(135deg, #0c0c0c 0%, #1a1a2e 50%, #16213e 100%);
font-family: 'Arial', sans-serif;
color: #fff;
overflow: hidden;
padding: 20px;
}
.container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
z-index: 10;
}
h1 {
font-size: 2.8rem;
margin-bottom: 20px;
text-shadow: 0 0 10px rgba(255, 50, 50, 0.7);
background: linear-gradient(90deg, #ff3366, #ff9966);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
.subtitle {
font-size: 1.2rem;
margin-bottom: 40px;
color: #aaa;
max-width: 600px;
line-height: 1.6;
}
/* 心跳容器 */
.heart-container {
position: relative;
width: 300px;
height: 300px;
margin: 30px 0;
display: flex;
justify-content: center;
align-items: center;
}
/* 心跳样式 */
.heart {
position: absolute;
width: 150px;
height: 150px;
background-color: #ff3366;
transform: rotate(45deg);
animation: heartbeat 1s infinite;
box-shadow: 0 0 50px rgba(255, 51, 102, 0.8);
cursor: pointer;
z-index: 2;
}
.heart:before,
.heart:after {
content: '';
position: absolute;
width: 150px;
height: 150px;
background-color: #ff3366;
border-radius: 50%;
}
.heart:before {
top: -75px;
left: 0;
}
.heart:after {
top: 0;
left: -75px;
}
/* 心跳动画 - 调整为更急促的心跳 */
@keyframes heartbeat {
0% {
transform: rotate(45deg) scale(1);
}
10% {
transform: rotate(45deg) scale(1.15);
}
20% {
transform: rotate(45deg) scale(1);
}
30% {
transform: rotate(45deg) scale(1.25);
}
40%, 100% {
transform: rotate(45deg) scale(1);
}
}
/* 倒计时显示 */
.countdown {
font-size: 5rem;
font-weight: bold;
margin: 20px 0;
text-shadow: 0 0 15px rgba(255, 50, 50, 0.9);
color: #ff3366;
background: rgba(0, 0, 0, 0.5);
padding: 15px 40px;
border-radius: 15px;
min-width: 200px;
letter-spacing: 5px;
transition: all 0.3s ease;
}
.countdown-label {
font-size: 1.4rem;
color: #ccc;
margin-bottom: 10px;
letter-spacing: 3px;
}
/* 提示文字 */
.instructions {
font-size: 1.1rem;
color: #aaa;
margin-top: 30px;
max-width: 500px;
line-height: 1.6;
padding: 15px;
border-radius: 10px;
background: rgba(255, 255, 255, 0.05);
}
/* 烟火画布 */
#fireworks-canvas {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 1;
}
/* 完成消息 */
.completion-message {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 3.5rem;
font-weight: bold;
text-align: center;
color: #ffcc00;
text-shadow: 0 0 20px rgba(255, 204, 0, 0.9);
opacity: 0;
z-index: 5;
animation: fadeIn 2s forwards;
display: none;
}
@keyframes fadeIn {
to {
opacity: 1;
}
}
/* 最后3秒提示 */
.warning {
color: #ff6600 !important;
text-shadow: 0 0 10px rgba(255, 100, 0, 0.8) !important;
}
.critical {
color: #ff0000 !important;
text-shadow: 0 0 15px rgba(255, 0, 0, 0.9) !important;
animation: pulse 0.5s infinite !important;
}
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.1); }
100% { transform: scale(1); }
}
/* 控制按钮 */
.controls {
margin-top: 20px;
display: flex;
gap: 15px;
}
.control-btn {
background: rgba(255, 51, 102, 0.2);
color: #ff3366;
border: 2px solid #ff3366;
padding: 10px 20px;
border-radius: 50px;
font-size: 1rem;
cursor: pointer;
transition: all 0.3s ease;
}
.control-btn:hover {
background: rgba(255, 51, 102, 0.4);
transform: translateY(-3px);
box-shadow: 0 5px 15px rgba(255, 51, 102, 0.3);
}
/* 响应式调整 */
@media (max-width: 768px) {
h1 {
font-size: 2rem;
}
.heart-container {
width: 250px;
height: 250px;
}
.heart {
width: 120px;
height: 120px;
}
.heart:before,
.heart:after {
width: 120px;
height: 120px;
}
.heart:before {
top: -60px;
}
.heart:after {
left: -60px;
}
.countdown {
font-size: 4rem;
}
.completion-message {
font-size: 2.5rem;
}
}
</style>
</head>
<body>
<div class="container">
<h1>心跳倒计时</h1>
<p class="subtitle">一颗跳动的红心,将在10秒倒计时结束后,绽放成绚丽的烟火</p>
<div class="countdown-label">倒计时</div>
<div class="countdown" id="countdown">10</div>
<div class="heart-container">
<div class="heart" id="heart"></div>
<div class="completion-message" id="completionMessage">心花怒放!</div>
</div>
<div class="controls">
<button class="control-btn" id="startBtn">开始倒计时</button>
<button class="control-btn" id="resetBtn">重新开始</button>
</div>
<p class="instructions">
红心正在跳动,10秒后它将绽放成美丽的烟火。<br>
倒计时最后3秒时心跳会加速并变为红色,请注意观看!
</p>
</div>
<canvas id="fireworks-canvas"></canvas>
<script>
document.addEventListener('DOMContentLoaded', function() {
// 获取DOM元素
const countdownElement = document.getElementById('countdown');
const heartElement = document.getElementById('heart');
const completionMessage = document.getElementById('completionMessage');
const startBtn = document.getElementById('startBtn');
const resetBtn = document.getElementById('resetBtn');
const canvas = document.getElementById('fireworks-canvas');
const ctx = canvas.getContext('2d');
// 设置canvas尺寸为窗口大小
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
window.addEventListener('resize', resizeCanvas);
resizeCanvas();
// 倒计时变量
let countdown = 10;
let countdownInterval = null;
let isRunning = false;
let fireworks = [];
let particles = [];
let isFireworksActive = false;
// 初始化倒计时
countdownElement.textContent = countdown;
// 开始倒计时
function startCountdown() {
if (isRunning) return;
isRunning = true;
startBtn.disabled = true;
// 重置为初始状态
countdown = 10;
countdownElement.textContent = countdown;
countdownElement.classList.remove('warning', 'critical');
heartElement.style.backgroundColor = '#ff3366';
heartElement.style.animationDuration = '1s';
countdownInterval = setInterval(() => {
countdown--;
countdownElement.textContent = countdown;
// 当倒计时接近结束时,改变心跳颜色和速度
if (countdown <= 5) {
heartElement.style.backgroundColor = '#ff6600';
heartElement.style.animationDuration = '0.7s';
countdownElement.classList.add('warning');
}
if (countdown <= 3) {
heartElement.style.backgroundColor = '#ff0000';
heartElement.style.animationDuration = '0.4s';
countdownElement.classList.remove('warning');
countdownElement.classList.add('critical');
}
// 倒计时结束
if (countdown <= 0) {
clearInterval(countdownInterval);
countdownElement.textContent = "0";
startBtn.disabled = false;
isRunning = false;
startFireworks();
}
}, 1000);
}
// 开始烟火效果
function startFireworks() {
isFireworksActive = true;
// 隐藏红心
heartElement.style.display = 'none';
// 显示完成消息
completionMessage.style.display = 'block';
// 创建多个烟花爆炸
for (let i = 0; i < 8; i++) {
setTimeout(() => {
createFirework(
Math.random() * canvas.width,
Math.random() * canvas.height
);
}, i * 300);
}
// 持续生成烟花
const fireworkInterval = setInterval(() => {
if (fireworks.length < 20) {
createFirework(
Math.random() * canvas.width,
Math.random() * canvas.height * 0.5
);
}
}, 500);
// 6秒后停止生成新烟花
setTimeout(() => {
clearInterval(fireworkInterval);
// 12秒后完全停止所有效果
setTimeout(() => {
isFireworksActive = false;
fireworks = [];
particles = [];
}, 12000);
}, 6000);
// 开始动画循环
animateFireworks();
}
// 创建单个烟花
function createFirework(x, y) {
fireworks.push({
x,
y,
radius: 2,
color: `hsl(${Math.random() * 360}, 100%, 60%)`,
velocity: {
x: Math.random() * 6 - 3,
y: Math.random() * 3 - 6
},
gravity: 0.1,
opacity: 1,
life: 100
});
}
// 烟花爆炸效果
function explodeFirework(firework) {
// 创建粒子
const particleCount = 200;
for (let i = 0; i < particleCount; i++) {
particles.push({
x: firework.x,
y: firework.y,
radius: Math.random() * 3 + 1,
color: firework.color,
velocity: {
x: Math.random() * 10 - 5,
y: Math.random() * 10 - 5
},
gravity: 0.05,
opacity: 1,
life: 100,
fade: Math.random() * 0.05 + 0.02
});
}
}
// 动画循环
function animateFireworks() {
if (!isFireworksActive && fireworks.length === 0 && particles.length === 0) {
return;
}
// 清除画布,使用半透明黑色创造拖尾效果
ctx.fillStyle = 'rgba(10, 10, 20, 0.2)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// 更新和绘制烟花
for (let i = fireworks.length - 1; i >= 0; i--) {
const firework = fireworks[i];
// 移动烟花
firework.x += firework.velocity.x;
firework.y += firework.velocity.y;
firework.velocity.y += firework.gravity;
firework.life -= 1;
// 绘制烟花
ctx.beginPath();
ctx.arc(firework.x, firework.y, firework.radius, 0, Math.PI * 2);
ctx.fillStyle = firework.color;
ctx.globalAlpha = firework.opacity;
ctx.fill();
// 如果烟花生命结束或到达一定高度,爆炸
if (firework.life <= 0 || firework.velocity.y >= 0) {
explodeFirework(firework);
fireworks.splice(i, 1);
}
}
// 更新和绘制粒子
for (let i = particles.length - 1; i >= 0; i--) {
const particle = particles[i];
// 移动粒子
particle.x += particle.velocity.x;
particle.y += particle.velocity.y;
particle.velocity.y += particle.gravity;
particle.opacity -= particle.fade;
particle.life -= 1;
// 绘制粒子
ctx.beginPath();
ctx.arc(particle.x, particle.y, particle.radius, 0, Math.PI * 2);
ctx.fillStyle = particle.color;
ctx.globalAlpha = particle.opacity;
ctx.fill();
// 移除消失的粒子
if (particle.opacity <= 0 || particle.life <= 0) {
particles.splice(i, 1);
}
}
// 重置全局透明度
ctx.globalAlpha = 1;
// 继续动画循环
requestAnimationFrame(animateFireworks);
}
// 重置动画
function resetAnimation() {
clearInterval(countdownInterval);
countdownInterval = null;
// 重置变量
countdown = 10;
fireworks = [];
particles = [];
isFireworksActive = false;
isRunning = false;
// 重置UI
countdownElement.textContent = countdown;
countdownElement.classList.remove('warning', 'critical');
heartElement.style.display = 'block';
heartElement.style.backgroundColor = '#ff3366';
heartElement.style.animationDuration = '1s';
completionMessage.style.display = 'none';
startBtn.disabled = false;
// 清除画布
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
// 事件监听
startBtn.addEventListener('click', startCountdown);
resetBtn.addEventListener('click', resetAnimation);
// 点击心形可以开始/重新开始
heartElement.addEventListener('click', function() {
if (!isRunning) {
startCountdown();
} else {
resetAnimation();
}
});
// 自动开始倒计时
startCountdown();
});
</script>
</body>
</html>
index.html
style.css
index.js
index.html