diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..6f3a291 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "liveServer.settings.port": 5501 +} \ No newline at end of file diff --git a/index.html b/index.html index 42d616d..46174f2 100644 --- a/index.html +++ b/index.html @@ -7,10 +7,15 @@ +
距离新年还有:00:00:00
- + - \ No newline at end of file + diff --git a/script.js b/script.js index ec681b4..7b745cd 100644 --- a/script.js +++ b/script.js @@ -1,178 +1,199 @@ -// 定义粒子类 +// 粒子类定义 class Particle { - constructor(x, y, color, velocity) { - this.x = x; - this.y = y; - this.color = color; - this.velocity = velocity; - this.alpha = 1; - this.friction = 0.99; - this.gravity = 0.05; + // 粒子构造函数 + constructor(x, y, radius, color, velocity, decay, gravity) { + this.x = x; // 粒子的x坐标 + this.y = y; // 粒子的y坐标 + this.radius = radius; // 粒子的半径 + this.color = color; // 粒子的颜色 + this.velocity = velocity; // 粒子的速度对象,包含x和y方向的速度 + this.decay = decay || 0.015; // 粒子的衰减速率,默认值为0.015 + this.alpha = 1; // 粒子的透明度,初始值为1 + this.gravity = gravity || 0.05; // 粒子受到的重力加速度,默认值为0.05 } - + // 绘制粒子的方法 draw() { - ctx.save(); - ctx.globalAlpha = this.alpha; - ctx.beginPath(); - ctx.fillStyle = this.color; - ctx.arc(this.x, this.y, 3, 0, Math.PI * 2, false); - ctx.fill(); - ctx.restore(); + ctx.save(); // 保存当前绘图状态 + ctx.globalAlpha = this.alpha; // 设置绘图透明度 + ctx.beginPath(); // 开始绘制 + ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false); // 绘制圆形粒子 + ctx.fillStyle = this.color; // 设置填充颜色 + ctx.fill(); // 填充颜色 + ctx.restore(); // 恢复之前保存的绘图状态 } - + // 更新粒子状态的方法 update() { - this.velocity.x *= this.friction; - this.velocity.y *= this.friction; - this.velocity.y += this.gravity; - this.x += this.velocity.x; - this.y += this.velocity.y; - this.alpha -= 0.01; - - if (this.alpha > 0) { - this.draw(); + this.velocity.y += this.gravity; // 应用重力,使粒子下降 + this.x += this.velocity.x; // 更新粒子的x坐标 + this.y += this.velocity.y; // 更新粒子的y坐标 + this.alpha -= this.decay; // 更新粒子的透明度 + if (this.alpha <= this.decay) { + this.alpha = 0; // 当透明度小于衰减速率时,将透明度设置为0 } + this.draw(); // 绘制更新后的粒子 + } + // 判断粒子是否仍然存活(透明度大于0) + isAlive() { + return this.alpha > 0; } } - -// 定义烟花类 +// 烟花类定义 class Firework { - constructor(x, y, targetY, color) { - this.x = x; - this.y = y; - this.targetY = targetY; - this.color = color; - this.velocity = { x: (Math.random() - 0.5) * 3, y: Math.random() * -3 - 4 }; - this.particles = []; - this.exploded = false; - } - - draw() { - if (!this.exploded) { - ctx.beginPath(); - ctx.fillStyle = this.color; - ctx.arc(this.x, this.y, 4, 0, Math.PI * 2, false); - ctx.fill(); - } + // 烟花构造函数 + constructor(x, y, color, riseSpeed = -1, particleSize = 2) { + this.x = x; // 烟花的x坐标 + this.y = y; // 烟花的y坐标 + this.color = color; // 烟花的颜色 + this.riseSpeed = riseSpeed; // 烟花上升速度 + this.particleSize = particleSize; // 爆炸后生成的粒子大小 + this.particles = []; // 存储爆炸后生成的粒子数组 + this.exploded = false; // 标记烟花是否已经爆炸 + this.velocity = { x: Math.random() * 2 - 1, y: this.riseSpeed }; // 烟花的速度,x轴速度模拟风 } - - explode() { - const effectType = Math.floor(Math.random() * 4); // 随机选择0到3之间的效果类型 - const particleCount = 100; // 每种效果的粒子数量 - - for (let i = 0; i < particleCount; i++) { - let velocity; - switch (effectType) { - case 0: // 标准圆形爆炸 - const angle = Math.random() * Math.PI * 2; - const speed = Math.random() * 6 + 1; - velocity = { x: Math.cos(angle) * speed, y: Math.sin(angle) * speed }; - break; - case 1: // 心形爆炸 - const angleHeart = Math.PI * i / (particleCount / 2); - const speedHeart = Math.sin(angleHeart) * 12; - velocity = { x: 16 * Math.pow(Math.sin(angleHeart), 3), y: -13 * Math.cos(angleHeart) + 5 * Math.cos(2 * angleHeart) - 2 * Math.cos(3 * angleHeart) - Math.cos(4 * angleHeart) }; - break; - case 2: // 螺旋形爆炸 - const angleSpiral = 0.1 * i; - const speedSpiral = 0.2 * i; - velocity = { x: Math.cos(angleSpiral) * speedSpiral, y: Math.sin(angleSpiral) * speedSpiral }; - break; - case 3: // 随机散射爆炸 - velocity = { x: (Math.random() - 0.5) * 12, y: (Math.random() - 0.5) * 12 }; - break; - default: // 默认为标准圆形爆炸 - const defaultAngle = Math.random() * Math.PI * 2; - const defaultSpeed = Math.random() * 6 + 1; - velocity = { x: Math.cos(defaultAngle) * defaultSpeed, y: Math.sin(defaultAngle) * defaultSpeed }; - } - this.particles.push(new Particle(this.x, this.y, this.color, velocity)); - } + // 烟花爆炸方法 +explode() { + const pattern = Math.floor(Math.random() * 5); // 随机选择爆炸图案类型 +const particleCount = 100 + Math.random() * 1000; // 确定爆炸生成的粒子数量 +for (let i = 0; i < particleCount; i++) { + let speed, angle; + // 根据图案类型生成粒子速度和角度 + switch(pattern) { + case 0: // 圆形 + speed = Math.random() * 5 + 2; // 粒子速度 + angle = Math.PI * 2 * i / particleCount; // 角度 + break; + case 1: // 星星 + speed = Math.random() * 5 + 2; // 粒子速度 + angle = Math.PI * 2 * i / particleCount + Math.PI / 5; // 角度 + break; + case 2: // 爱心 + speed = Math.random() * 3 + 1; // 粒子速度 + angle = Math.PI * 2 * i / particleCount; // 角度 + break; + case 3: // 散射 + speed = Math.random() * 5 + 2; // 粒子速度 + angle = Math.PI * 2 * i / particleCount + Math.random() * Math.PI / 2 - Math.PI / 4; // 角度 + break; + case 4: // 螺旋 + speed = Math.random() * 5 + 2; // 粒子速度 + angle = Math.PI * 2 * i / particleCount + Math.PI * 5 / 4 * Math.sin(Math.PI * 2 * i / particleCount); // 角度 + break; + default: + speed = Math.random() * 5 + 2; // 粒子速度 + angle = Math.PI * 2 * i / particleCount; // 默认角度 } - + const decay = Math.random() * 0.04 + 0.01; // 粒子衰减速率 + const gravity = Math.random() * 0.05 + 0.03; // 粒子重力加速度 + // 创建新粒子并添加到粒子数组中 + this.particles.push(new Particle(this.x, this.y, this.particleSize, `hsl(${Math.random() * 360}, 100%, 50%)`, { + x: Math.cos(angle) * speed, + y: Math.sin(angle) * speed + }, decay, gravity)); +} +} + // 更新烟花状态的方法 update() { if (!this.exploded) { - this.velocity.y += 0.1; // gravity - this.x += this.velocity.x; - this.y += this.velocity.y; - - if (this.y >= this.targetY) { - this.exploded = true; - this.explode(); + this.y += this.velocity.y; // 更新烟花的y坐标,使其上升 + this.x += this.velocity.x; // 更新烟花的x坐标,模拟风效果 + this.draw(); // 绘制上升中的烟花 + // 判断烟花是否达到爆炸高度 + if (this.y < canvas.height * (0.18 + Math.random() * 0.22)) { + this.exploded = true; // 标记烟花为已爆炸 + this.explode(); // 触发烟花爆炸 } - this.draw(); } else { - this.particles.forEach((particle, index) => { - particle.update(); - if (particle.alpha <= 0) { - this.particles.splice(index, 1); - } - }); + // 更新所有粒子的状态,并移除已经“死亡”的粒子 + this.particles = this.particles.filter(p => p.isAlive()); + this.particles.forEach(p => p.update()); } } + // 绘制上升中的烟花方法 + draw() { + ctx.save(); // 保存当前绘图状态 + ctx.globalAlpha = 1; // 设置绘图透明度为不透明 + ctx.beginPath(); // 开始绘制 + ctx.arc(this.x, this.y, 2, 0, Math.PI * 2, false); // 绘制表示烟花的小圆点 + ctx.fillStyle = this.color; // 设置填充颜色 + ctx.fill(); // 填充颜色 + ctx.restore(); // 恢复之前保存的绘图状态 + } } - +// 获取canvas元素并设置其宽高 const canvas = document.getElementById('fireworksCanvas'); const ctx = canvas.getContext('2d'); canvas.width = window.innerWidth; canvas.height = window.innerHeight; - -let fireworks = []; -let timerId; - +let fireworks = []; // 存储所有烟花的数组 +// 动画循环函数,用于不断更新画布上的内容 function animate() { - requestAnimationFrame(animate); - ctx.fillStyle = 'rgba(0, 0, 0, 0.2)'; - ctx.fillRect(0, 0, canvas.width, canvas.height); - + requestAnimationFrame(animate); // 请求下一帧动画 + ctx.fillStyle = 'rgba(0, 0, 0, 0.1)'; // 设置画布覆盖颜色(用于创建尾迹效果) + ctx.fillRect(0, 0, canvas.width, canvas.height); // 覆盖整个画布 + // 遍历所有烟花,更新它们的状态,并在必要时将它们从数组中移除 fireworks.forEach((firework, index) => { firework.update(); if (firework.exploded && firework.particles.length === 0) { - fireworks.splice(index, 1); + fireworks.splice(index, 1); // 移除已经爆炸且粒子消失的烟花 } }); } - -function startFireworks() { - timerId = setInterval(() => { - const x = Math.random() * canvas.width; - const y = canvas.height + 10; - const targetY = canvas.height * (Math.random() * 0.5 + 0.1); - const color = `hsl(${Math.random() * 360}, 100%, 50%)`; - fireworks.push(new Firework(x, y, targetY, color)); - }, 400); +// 创建新烟花的函数,用于在画布上添加新的烟花 +function createFirework() { + const x = Math.random() * canvas.width; // 在画布宽度范围内随机选择x坐标 + const y = canvas.height; // y坐标设置为画布底部 + const color = `hsl(${Math.random() * 360}, 100%, 50%)`; // 随机选择颜色 + const riseSpeed = -Math.random() * 8 + 1; // 随机生成上升速度 + const particleSize = Math.random() * 3 + 2; // 随机生成粒子大小 + // 创建新的烟花并添加到烟花数组中 + fireworks.push(new Firework(x, y, color, riseSpeed, particleSize)); +} +function launchFireworks() { + const initialFireworks = 30; // 第一次触发时的烟花数量 + // 立即生成初始数量的烟花粒子 + for (let i = 0; i < initialFireworks; i++) { + createFirework(); + } + // 定时生成新的烟花粒子 + const interval = setInterval(() => { + createFirework(); + // 可以在这里添加逻辑来停止生成新的烟花粒子,例如设置一个生成烟花的总数限制 + }, 200); // 每隔1秒生成一个新的烟花粒子 } -document.getElementById('previewButton').addEventListener('click', startFireworks); +// 触发烟花效果的函数,用于在特定条件下(如倒计时结束)启动一系列烟花 +function triggerFireworks() { + const fireworksCount = 5; // 定义触发的烟花数量 + for (let i = 0; i < fireworksCount; i++) { + // 每隔一秒触发一个烟花,创建连续的烟花效果 + setTimeout(createFirework, i * 1000); + } +} -// 获取页面上的倒计时元素 +// 倒计时逻辑,假设倒计时结束后触发烟花 const countdownElement = document.getElementById('countdown'); -// 设置目标日期为2024年2月9日23:59:59 -const targetDate = new Date('2024-02-09T23:59:59').getTime(); +const targetDate = new Date('Feb 9, 2024 23:59:59').getTime(); -// 倒计时更新函数 +// 倒计时更新函数,用于显示距离目标日期的时间并在到达时触发烟花 function updateCountdown() { - const now = new Date().getTime(); // 获取当前时间戳 - const distance = targetDate - now; // 计算当前时间与目标时间的差值 - + const now = new Date().getTime(); // 获取当前时间 + const distance = targetDate - now; // 计算距离目标日期的时间差 // 计算天、小时、分钟和秒 const days = Math.floor(distance / (1000 * 60 * 60 * 24)); const hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); const minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60)); const seconds = Math.floor((distance % (1000 * 60)) / 1000); - // 更新倒计时显示 - countdownElement.innerHTML = `距离新年还有:${days}天 ${hours}小时 ${minutes}分 ${seconds}秒`; - - // 检查倒计时是否结束 + countdownElement.innerHTML = `距离新年还有:${days}天${hours}小时${minutes}分${seconds}秒`; + // 如果时间差小于0,说明目标日期已到达 if (distance < 0) { - clearInterval(timerId); // 停止倒计时 - countdownElement.innerHTML = "新年快乐!"; // 显示新年快乐的信息 - startFireworks(); // 触发烟花效果 + clearInterval(interval); // 停止倒计时更新 + countdownElement.style.display = 'none'; // 隐藏倒计时显示 + document.getElementById('greetings').style.display = 'block'; // 显示新年祝福信息 + launchFireworks(); // 使用 launchFireworks 函数触发烟花效果 } } +const interval = setInterval(updateCountdown, 1000); // 每秒更新倒计时 -// 使用setInterval每秒更新倒计时 -timerId = setInterval(updateCountdown, 1000); - -animate(); +animate(); // 开始动画循环 \ No newline at end of file diff --git a/styles.css b/styles.css index 695b2e1..fc6859e 100644 --- a/styles.css +++ b/styles.css @@ -7,41 +7,32 @@ body, html { } body { - font-family: 'Arial', sans-serif; + font-family: '宋体', sans-serif; /* 使用宋体字体 */ + position: relative; /* 确保z-index生效 */ +} + +.background-image { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: -1; /* 背景图片位于下层 */ background-image: url('assets/a.png'); background-size: cover; background-position: center; /* 确保背景图片居中显示 */ } #countdown, #greetings, #previewButton { - position: relative; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); /* 居中显示 */ z-index: 2; /* 确保这些元素在canvas之上 */ -} - -#countdown { + text-align: center; /* 文字居中 */ + color: #fff; /* 文字颜色设置为白色 */ font-size: 36px; /* 调整字体大小确保可见 */ - text-align: center; - margin: 50px 0; - color: #fff; /* 如果背景是暗色的,设置为白色或其他明亮的颜色 */ -} - -#previewButton { - display: block; - width: 200px; - margin: 20px auto; - padding: 10px; - font-size: 16px; - cursor: pointer; - background-color: #4CAF50; - color: white; - border: none; - border-radius: 5px; -} - -#greetings { - font-size: 32px; - text-align: center; - margin-top: 20px; + margin: 0; /* 移除默认的边距 */ } #fireworksCanvas { @@ -54,3 +45,32 @@ body { z-index: 1; pointer-events: none; /* 允许鼠标事件穿透canvas */ } + +#header { + position: absolute; + top: 10px; /* 调整到顶部 */ + right: 10px; /* 调整到最右边 */ + color: #fff; /* 文字颜色设置为白色 */ + z-index: 2; /* 确保这些元素在canvas之上 */ +} + +#footer { + position: absolute; + bottom: 10px; /* 调整到底部 */ + left: 10px; /* 调整到最左边 */ + color: #fff; /* 文字颜色设置为白色 */ + z-index: 2; /* 确保这些元素在canvas之上 */ +} + +#footer a { + color: white; + text-decoration: none; /* 如果需要去除下划线的话 */ +} + + +/* 媒体查询:适应不同设备 */ +@media only screen and (max-width: 600px) { + #countdown, #greetings { + font-size: 24px; /* 在小屏幕上调整字体大小 */ + } +}