一.需求分析
html结构非常简单
只要一个烟花背景,其他内容都是动态生成的
JavaScript部分
1,点击时,生成两个div标签
一个在点击位置
一个在烟花背景最下方
要让下方的烟花 上升到 鼠标点击位置 的div
2,两个div重合时
清除两个div
生成若干个 小烟花
小烟花,向四周随机运动
3,当小烟花到达随机位置后,删除小烟花
*********************
html结构非常简单
只要一个烟花背景,其他内容都是动态生成的
JavaScript部分
1,点击时,生成两个div标签
一个在点击位置
一个在烟花背景最下方
要让下方的烟花 上升到 鼠标点击位置 的div
2,两个div重合时
清除两个div
生成若干个 小烟花
小烟花,向四周随机运动
3,当小烟花到达随机位置后,删除小烟花
步骤总结
一,给 背景区域标签,添加点击事件
1,获取点击位置 offsetX offsetY
2,设定极值:
最小是 0
最大是 背景区域 宽高
3,生成 大烟花标签节点
4,给 大烟花标签节点 设定 class样式
5,给 大烟花标签节点 设定 随机背景颜色
6,给 大烟花标签节点 设定 定位
上方: left x top y
下方: left x bottom 0
7,通过move()函数,让下方大烟花运动
move函数,参数1:下方大烟花
move函数,参数2:top:y
move函数,参数3:运动终止执行的程序
(1),清除两个大烟花
(2),设定随机小烟花数量
(3),根据随机小烟花数量,通过for循环,来生成小烟花标签节点
a,生成定义,小烟花标签节点
b,给 小烟花标签节点 添加class样式
c,给 小烟花标签节点 添加随机背景颜色
d,给 小烟花标签节点 定义原始位置
原始位置就是大烟花消失的位置,就是鼠标点击的位置
e,生成 小烟花标签节点 随机的运动目标位置
f,通过 move() 函数,让 小烟花标签节点 运动至 随机目标位置
move函数,参数1:小烟花标签节点
move函数,参数2:left:随机x位置 top:随机y位置
move函数,参数3:小烟花标签节点运动终止时执行的程序
删除小烟花节点
二.html+css+内部js
三.外部js-最终
class Fire{
constructor(ele){
this.ele=ele;
}
getEvent(){
this.ele.addEventListener('click',(e)=>{
let x=e.offsetX;
let y=e.offsetY;
if(x<0){
x=0;
}
if(y<0){
y=0;
}
if(x>this.ele.clientWidth){
x=this.ele.clientWidth;
}
if(y>this.ele.clientHeight){
y=this.ele.clientHeight;
}
// 生成大烟花并设定
const fire1=document.createElement('p');
const fire2=document.createElement('p');
fire1.className='bigFire';
fire2.className='bigFire';
let color=this.setColor();
fire1.style.backgroundColor=color;
fire2.style.backgroundColor=color;
fire1.style.left=x+'px';
fire1.style.top=y+'px';
fire2.style.left=x+'px';
fire2.style.bottom=0+'px';
fire1.addEventListener('click',(e)=>{
e.stopPropagation();
});
fire2.addEventListener('click',(e)=>{
e.stopPropagation();
});
this.ele.appendChild(fire1);
this.ele.appendChild(fire2);
this.move(fire2,{top:y},()=>{
this.ele.removeChild(fire1);
this.ele.removeChild(fire2);
let num=parseInt(Math.random()*(50-20+1)+20);
for(let i=1;i<=num;i++){
const smallFire=document.createElement('p');
smallFire.className='smallFire';
smallFire.style.backgroundColor=this.setColor();
smallFire.style.left=x+'px';
smallFire.style.top=y+'px';
this.ele.appendChild(smallFire);
let randomX=parseInt(Math.random()*((x+150)-(x-150)+1)+(x-150));
let randomY=parseInt(Math.random()*((y+150)-(y-150)+1)+(y-150));
this.move(smallFire,{left:randomX,top:randomY},(e)=>{
this.ele.removeChild(smallFire);
});
}
});
});
}
setColor(){
let a1=parseInt(Math.random()*256);
let a2=parseInt(Math.random()*256);
let a3=parseInt(Math.random()*256);
return `rgb(${a1},${a2},${a3})`;
}
move(ele, obj, callback) {
let time = {};
for (let type in obj) {
let oldVal = 0;
if (type === 'opacity') {
oldVal = parseFloat(window.getComputedStyle(ele)[type]) * 100;
} else {
oldVal = parseInt(window.getComputedStyle(ele)[type]);
}
time[type] = setInterval(() => {
let val = (obj[type] - oldVal) / 10;
val = val > 0 ? Math.ceil(val) : Math.floor(val);
oldVal += val;
if (type === 'opacity') {
ele.style[type] = oldVal / 100;
} else {
ele.style[type] = oldVal + 'px';
}
if (oldVal == obj[type]) {
clearInterval(time[type]);
delete (time[type]);
}
if (Object.keys(time) == 0) {
callback();
}
}, 10)
}
}
}
四.外部js-步骤解释
// 生成烟花的构造函数
// 参数1:生成烟花的背景标签区域
class Fire{
constructor(ele){
// 存储参数
this.ele = ele;
}
// 定义方法
// 生成烟花的方法
getEvent(){
// 1,获取鼠标点击的坐标
// 所有的烟花的事件,都是在点击之后生成的
this.ele.addEventListener('click' , (e)=>{
// 1,获取坐标,设定极限值
// 只是点击,不是拖拽,可以直接获取相对于父级div的坐标
let x = e.offsetX;
let y = e.offsetY;
// 设定边界值,点击到边框线时,数值会超出div的显示范围
// 最小值是 0
// 最大值是 div的宽高 this.ele.clientWidth
// 不需要边框线宽度的
if(x < 0){
x = 0;
}
if(y < 0){
y = 0;
}
if(x > this.ele.clientWidth){
x = this.ele.clientWidth;
}
if(y > this.ele.clientHeight){
y = this.ele.clientHeight;
}
// 2,生成大烟花,并且设定样式
// 通过节点操作,生成两个标签节点
const fire1 = document.createElement('p');
const fire2 = document.createElement('p');
// 添加大烟花样式
fire1.className = 'bigFire';
fire2.className = 'bigFire';
// 获取随机的颜色,设定为背景颜色
let color = this.setColor();
fire1.style.backgroundColor = color;
fire2.style.backgroundColor = color;
// 设定定位
// 大烟花1在上,大烟花2在下
fire1.style.left = x + 'px';
fire1.style.top = y + 'px';
// 下方的烟花,left是实参,靠div下方显示,bottom为0
fire2.style.left = x + 'px';
fire2.style.bottom = 0 + 'px';
// 阻止默认事件
// 点击生成的p标签,会触发父级的点击事件,也会生成新的大烟花
// 必须阻止事件的传播 / 阻止冒泡事件
fire1.addEventListener('click' , (e)=>{
e.stopPropagation();
})
fire2.addEventListener('click' , (e)=>{
e.stopPropagation();
})
// 将大烟花写入页面
this.ele.appendChild(fire1);
this.ele.appendChild(fire2);
// 通过move函数,让下方的大烟花升空
// 1,让下方的大烟花,上升
// 是要运动效果完成,不能瞬间完成,应该调用move函数
// 我们之前,定义的move函数,都是单属性的运动模式
// 生成小烟花时,运动方式是至少两个属性运动,不能使用单属性move函数
// 要使用终极版的运动函数
// 我们不用自己会写,只要会调用就可以了
// this.move() 终极版运动函数,并且赋值参数
// 参数1: 执行运动的标签 --- 下方的大烟花
// 参数2: 运动方式 --- 下方的top 运动至 大烟花的 top 坐标
this.move(fire2,{top: y} , ()=>{
// 当运动终止,也就是两个烟花重合了,删除两个烟花
this.ele.removeChild(fire1);
this.ele.removeChild(fire2);
// 生成若干个小烟花
// 定义小烟花随机数量 随便定义的,目前就来一个20-50试试
let num = parseInt(Math.random()*(50+1-20)+20);
// 通过循环,根据随机数量,生成对应的小烟花
for(let i = 1 ; i <= num ; i++){
// 创建小烟花的节点
// 这里定义小烟花,可以let,可以是const,不能是var
// 当前程序是在循环中,如果使用var,是定义一个变量
// 然后对这个变量,反复进行赋值,最终,var 变量中,存储的是最后一个标签,最后一个小烟花
// 当执行删除时,循环已经执行结束,所有的小烟花都已经生成
// 执行删除,只会删除最后一个小烟花
// 必须使用 let 或者 const 每次循环都生成一个独立的变量,来存储数据
// 不会发生覆盖存储的效果,是独立存储相同不影响的不同小烟花
let smallFire = document.createElement('p');
// 添加样式
smallFire.className = 'smallFire';
// 添加随机背景颜色
smallFire.style.backgroundColor = this.setColor();
// 定义小烟花的原始位置,就是大烟花消失的位置,就是鼠标点击的位置
smallFire.style.left = x + 'px';
smallFire.style.top = y + 'px';
// 将小烟花,写入到页面
this.ele.appendChild(smallFire);
// 定义随机坐标 --- 小烟花运动的目标位置
// 坐标可以设定范围,鼠标点击,大烟花消失位置 正负150 范围内
// 最小值 (x-150) 最大值 (x+150)
// 定义随机数值
let randomX = parseInt(Math.random()*(x+150+1-(x-150)) + (x-150));
let randomY = parseInt(Math.random()*(y+150+1-(y-150)) + (y-150));
// 通过this.move()让小烟花运动
this.move(smallFire,{left:randomX , top:randomY} , ()=>{
// 当运动终止,也就是小烟花到达随机目标位置
// 清除小烟花
// 不是只删除一次,是每次都会删除
// 第一次把最后一个小烟花删除,之后再执行删除最后一个小烟花,会报错
this.ele.removeChild(smallFire);
})
}
})
})
}
// 随机颜色方法
setColor() {
let c1 = parseInt(Math.random() * 256);
let c2 = parseInt(Math.random() * 256);
let c3 = parseInt(Math.random() * 256);
return `rgb(${c1},${c2},${c3})`;
}
// move多属性运动函数终极版
move(ele, obj, callback) {
let time = {};
for (let type in obj) {
let oldVal = 0;
if (type === 'opacity') {
oldVal = parseFloat(window.getComputedStyle(ele)[type]) * 100;
} else {
oldVal = parseInt(window.getComputedStyle(ele)[type]);
}
time[type] = setInterval(() => {
let val = (obj[type] - oldVal) / 10;
val = val > 0 ? Math.ceil(val) : Math.floor(val);
oldVal += val;
if (type === 'opacity') {
ele.style[type] = oldVal / 100;
} else {
ele.style[type] = oldVal + 'px';
}
if (oldVal == obj[type]) {
clearInterval(time[type]);
delete (time[type]);
}
if (Object.keys(time) == 0) {
callback();
}
}, 10)
}
}
}