这个和之前一样,都是用到了多属性运动函数的。
烟花散开呈现的圆形的效果
CSS部分
<style>
#box{
width: 80%;
height: 600px;
border: 2px solid red;
background: #000;
margin:20px auto;
cursor: pointer;
position: relative;
left: 0;
top: 0;
overflow: hidden;
}
.fire{
width: 20px;
height:20px;
position: absolute;
bottom: 0;
}
.small-fire{
width: 20px;
height:20px;
position: absolute;
border-radius: 50%;
}
</style>
html部分,,,,就一个框。。。。
引用下方的js文件
JS部分
var obox = document.querySelector("#box");
obox.onclick = function(eve){
var e = eve || window.event;
// 需要将鼠标点击的位置、box元素都传入到new构造函数中
new mainFire({
box:obox, //将obox传入,生成的主体烟花插入到obox中
x:e.pageX-obox.offsetLeft, //
y:e.pageY-obox.offsetTop //page 避免可视问题
});
}
// OOP:
function mainFire(options){
// 将传入的三个参数与构造函数建立连接
this.obox = options.box;
this.x = options.x;
this.y = options.y;
this.addFire();
}
mainFire.prototype.addFire = function(){
this.f = document.createElement("div");
this.f.className = "fire";
this.obox.appendChild(this.f);
this.f.style.background = "red";
//主体烟花初始位置为距离obox的left,== obox.offsetX;
var l = this.x -this.f.offsetWidth/2+ "px";
this.f.style.left = l;
this.move();
}
mainFire.prototype.move = function(){
// //利用多属性运动move(),改变主体烟花的top距离为 obox.offsetY
// //move()中传入回调函数,再执行删除主体烟花,创建小烟花
move(this.f,{top:this.y-this.f.offsetHeight/2},()=>{
this.f.remove();
this.smallFire();
})
// var that = this;
// move(this.f,{top:this.y},function(){
// //可以用箭头函数,this指向上层,即指向实例,
// that.f.remove();
// that.smallFire();
// });
}
mainFire.prototype.smallFire = function(){
// 随机创建小烟花个数
var num = random(5,10);
//定义随机圆的半径
var r = random(100,200);
// console.log(num);
for(var i = 0;i<num;i++){
// 使用let声明变量。每次都是独立的区域。使每次循环一次,拿到一次i的值。每个都是独立的
let s = document.createElement("div");
this.obox.appendChild(s);
s.className = "small-fire";
// console.log(s.offsetWidth)
s.style.left = this.x -s.offsetWidth/2 + "px";
s.style.top = this.y - s.offsetHeight/2 + "px";
s.style.background = "yellow";
s.setAttribute("index",i);
//小烟花爆炸的目标位置
var target = {
x:parseInt(Math.cos( Math.PI/180 * (360/num*i) ) * r) + this.x - s.offsetWidth/2,
y:parseInt(Math.sin( Math.PI/180 * (360/num*i) ) * r) + this.y - s.offsetHeight/2
}
// /fireworks
// x = Math.sin(Math.PI/180*deg) * r
// y = Math.cos(Math.PI/180*deg) * r
// deg = (360 / 小烟花个数 * 第几个小烟花) 第几个小烟花的角度
// 需要parseInt取整,因为每个小烟花的位置不一定是整数,没到达目标时就会一直在动。
move(s,{
left:target.x,
top:target.y
},()=>{
//箭头函数没有自己的this,
s.remove();
})
}
}
// 随机数
function random(max,min){
return Math.round(Math.random()*(max-min)+min);
}
封装的多属性运动函数,直接引用到js中就行了。
//data传入一个对象,key对应ele的样式属性,value对应ele的属性值,callback回调函数
function move(ele, data, callback) {
clearInterval(ele.t);
//将for in 放入计时器中,节省性能,每次计时器开启,再进行for in的操作。
ele.t = setInterval(function() {
//1、设置目标到达状态
var mySwitch = true;
//for..in遍历对象,i为样式attr,data[i]为对象的value; 对应目标值target
for (var i in data) {
//获取元素的当前样式
var iNow = parseInt(getStyle(ele, i));
//设置步长(每次改变的值),每次为现在值和目标值的十分之一,speed永远不会为0
var speed = (data[i] - iNow) / 10;
//判断步长,小于0向下取整,大于0向上取整
// if (speed < 0) {
// speed = Math.floor(speed);
// } else {
// speed = Math.ceil(speed);
// }
// 下方简写
speed = speed < 0 ? Math.floor(speed) : Math.ceil(speed);
//开启之后,关闭之前,作一个判断
//2、当有一个属性没到达目标时,改变状态,不清除计时器
if (iNow != data[i]) {
mySwitch = false;
}
ele.style[i] = iNow + speed + "px";
}
//3、(for循环结束)所有目标达到状态时清除计时器
if (mySwitch) {
//清除计时器,动画结束
clearInterval(ele.t);
//如果传入了callback,就执行。如果没有这个就不执行
callback && callback();
}
}, 30)
}
//获取样式兼容写法
function getStyle(ele, attr) {
if (getComputedStyle) {
return getComputedStyle(ele, false)[attr]; //false表示非伪元素
} else {
return ele.currentStyle[attr];
}
}