Skip to content

Commit

Permalink
重新编写烟花代码
Browse files Browse the repository at this point in the history
  • Loading branch information
XXYoLoong committed Feb 9, 2024
1 parent f26cde1 commit db72045
Showing 1 changed file with 112 additions and 140 deletions.
252 changes: 112 additions & 140 deletions script.js
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();

0 comments on commit db72045

Please sign in to comment.