-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
112 additions
and
140 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,206 +1,178 @@ | ||
let interval; // 将 interval 变量定义在外部,以便在 updateCountdown 函数内部访问 | ||
|
||
// 粒子类定义 | ||
// 定义粒子类 | ||
class Particle { | ||
// 粒子构造函数 | ||
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 | ||
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; | ||
} | ||
|
||
// 绘制粒子的方法 | ||
draw() { | ||
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(); // 恢复之前保存的绘图状态 | ||
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(); | ||
} | ||
|
||
// 更新粒子状态的方法 | ||
update() { | ||
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.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.draw(); // 绘制更新后的粒子 | ||
} | ||
|
||
// 判断粒子是否仍然存活(透明度大于0) | ||
isAlive() { | ||
return this.alpha > 0; | ||
} | ||
} | ||
|
||
// 烟花类定义 | ||
// 定义烟花类 | ||
class Firework { | ||
// 烟花构造函数 | ||
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轴速度模拟风 | ||
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(); | ||
} | ||
} | ||
|
||
// 烟花爆炸方法 | ||
explode() { | ||
const particleCount = 200 + Math.random() * 1000; // 确定爆炸生成的粒子数量 | ||
const effectType = Math.floor(Math.random() * 4); // 随机选择0到3之间的效果类型 | ||
const particleCount = 100; // 每种效果的粒子数量 | ||
|
||
for (let i = 0; i < particleCount; i++) { | ||
const speed = Math.random() * 5 + 2; // 粒子速度 | ||
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(Math.PI * 2 * i / particleCount) * speed, | ||
y: Math.sin(Math.PI * 2 * i / particleCount) * speed | ||
}, decay, gravity)); | ||
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)); | ||
} | ||
} | ||
|
||
|
||
// 更新烟花状态的方法 | ||
update() { | ||
if (!this.exploded) { | ||
this.y += this.velocity.y; // 更新烟花的y坐标,使其上升 | ||
this.x += this.velocity.x; // 更新烟花的x坐标,模拟风效果 | ||
this.draw(); // 绘制上升中的烟花 | ||
this.velocity.y += 0.1; // gravity | ||
this.x += this.velocity.x; | ||
this.y += this.velocity.y; | ||
|
||
// 通过增加随机性来调整爆炸高度 | ||
const explodeHeight = canvas.height * (0.5 + Math.random() * 0.3); // 示例:最低高度为画布高度的 50%,最高为 80% | ||
if (this.y < explodeHeight) { | ||
if (this.y >= this.targetY) { | ||
this.exploded = true; | ||
this.explode(); | ||
} | ||
this.draw(); | ||
} else { | ||
// 更新所有粒子的状态,并移除已经“死亡”的粒子 | ||
this.particles = this.particles.filter(p => p.isAlive()); | ||
this.particles.forEach(p => p.update()); | ||
this.particles.forEach((particle, index) => { | ||
particle.update(); | ||
if (particle.alpha <= 0) { | ||
this.particles.splice(index, 1); | ||
} | ||
}); | ||
} | ||
} | ||
|
||
// 绘制上升中的烟花方法 | ||
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 isLaunching = false; // 标记是否正在启动烟花 | ||
let fireworks = []; | ||
let timerId; | ||
|
||
// 动画循环函数,用于不断更新画布上的内容 | ||
function animate() { | ||
requestAnimationFrame(animate); // 请求下一帧动画 | ||
ctx.fillStyle = 'rgba(0, 0, 0, 0.1)'; // 设置画布覆盖颜色(用于创建尾迹效果) | ||
ctx.fillRect(0, 0, canvas.width, canvas.height); // 覆盖整个画布 | ||
requestAnimationFrame(animate); | ||
ctx.fillStyle = 'rgba(0, 0, 0, 0.2)'; | ||
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 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 triggerFireworks(fireworksCount = 50, intervalDuration = 1000) { | ||
if (isLaunching) return; // 如果已经在启动烟花,则直接返回 | ||
isLaunching = true; // 标记正在启动烟花 | ||
|
||
for (let i = 0; i < fireworksCount; i++) { | ||
setTimeout(createFirework, i * (intervalDuration / fireworksCount)); | ||
} | ||
|
||
// 在一定时间后重置 isLaunching 标记,以便可以再次触发烟花 | ||
setTimeout(() => { | ||
isLaunching = false; | ||
}, intervalDuration); | ||
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); | ||
} | ||
|
||
// 为预览按钮添加点击事件监听器,用于测试烟花效果 | ||
document.getElementById('previewButton').addEventListener('click', () => triggerFireworks(50, 5000)); | ||
document.getElementById('previewButton').addEventListener('click', startFireworks); | ||
|
||
// 获取页面上的倒计时元素 | ||
const countdownElement = document.getElementById('countdown'); | ||
// 设置目标日期为2024年2月9日23:59:59 | ||
const targetDate = new Date('2024-02-09T23: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}秒`; | ||
|
||
// 如果时间差小于0,说明目标日期已经到达 | ||
// 检查倒计时是否结束 | ||
if (distance < 0) { | ||
clearInterval(interval); // 停止倒计时 | ||
countdownElement.style.display = 'none'; // 隐藏倒计时元素 | ||
triggerFireworks(100, 10000); // 触发烟花效果,参数分别表示烟花数量和持续时间(毫秒) | ||
clearInterval(timerId); // 停止倒计时 | ||
countdownElement.innerHTML = "新年快乐!"; // 显示新年快乐的信息 | ||
startFireworks(); // 触发烟花效果 | ||
} | ||
} | ||
|
||
// 使用 setInterval 每秒调用一次 updateCountdown 函数,以更新倒计时 | ||
interval = setInterval(updateCountdown, 1000); | ||
|
||
// 触发烟花效果的函数,参数为烟花数量和烟花展示的总时长(毫秒) | ||
function triggerFireworks(fireworksCount = 50, intervalDuration = 1000) { | ||
if (isLaunching) return; // 如果烟花已经在触发中,则不再触发 | ||
isLaunching = true; // 标记烟花开始触发 | ||
|
||
for (let i = 0; i < fireworksCount; i++) { | ||
// 以间隔平均分布的方式触发每个烟花 | ||
setTimeout(createFirework, i * (intervalDuration / fireworksCount)); | ||
} | ||
|
||
// 一段时间后重置触发标记,允许再次触发烟花 | ||
setTimeout(() => { | ||
isLaunching = false; | ||
}, intervalDuration); | ||
} | ||
// 使用setInterval每秒更新倒计时 | ||
timerId = setInterval(updateCountdown, 1000); | ||
|
||
// 为预览按钮添加点击事件,点击时触发烟花效果 | ||
document.getElementById('previewButton').addEventListener('click', () => triggerFireworks(50, 5000)); | ||
animate(); |