js模拟烟花特效

这是效果图
在这里插入图片描述
思路分析:
一、烟花,可能会存在多个,每个烟花都是独立的对象,需要在点击的一瞬间被创建。因为点击时才创建对象,所以点击事件之前的过程不属于面向对象的过程
二、提前处理好:选择元素,绑定事件,触发事件时,执行面向对象的启动(new)。
OOA:烟花:点击页面,出现运动的元素到达目的之后,炸开到随机的位置
1.创建主题烟花的元素,设置初始位置,颜色,等信息,插入页面。
2.开始运动到鼠标点击的位置。
3.到目标之后,删除,然后,创建一堆小烟花,设置小烟花的位置(主题烟花消失的位置),颜色,个数和半径,插入页面。
4.小烟花开始运动,运动到随机位置。
5.结束之后删除。

这里我们开始之前先封装一个多属性缓冲运动的函数,方便后面的运动。

//这里ele是元素,data是对象,用来设置属性,end是回调函数,用来进行延迟运动
function move(ele,data,end){
        clearInterval(ele.t);
        ele.t = setInterval(() => {
            // 1.计时器开启之后,设定状态为关闭计时器
            var onoff = true;
            for(var i in data){
                var iNow = parseInt(getStyle(ele,i));
                var speed = (data[i] - iNow)/7;
                speed = speed<0 ? Math.floor(speed) : Math.ceil(speed);
                // 必须所有属性都到目标才能清计时器
                // 每次只能拿到一个属性
                // 只能判断一个属性是否到目标
                // 如果有一个属性到目标了,一定会清除计时器么?不一定
                // 如果有一个属性没到目标,一定不清除计时器!!!

                if(data[i] != iNow){
                    // 3.但是在设定状态之后,关闭计时器之前,判断是否有属性没到目标,只要有一个属性没到目标,就把状态改成不关闭计时器
                    onoff = false;
                }
                ele.style[i] = iNow + speed + "px";
            }
            // 2.根据状态决定关闭计时器
            if(onoff){
                clearInterval(ele.t);
                // if(end){
                //     end();
                // }
                end && end(); //有一个为false就没有执行的必要。没有end这个回调函数,就不执行;有就执行。
            }
        }, 30);
    }
   //获取非行内样式元素属性兼容
function getStyle(ele,attr){
    if(getComputedStyle){
        return getComputedStyle(ele,false)[attr]; //其他浏览器,只能获取不能设置
    }else{
        return ele.currentStyle[attr]; //IE,只能获取不能设置
    }
}

这是css样式代码:

#container{
            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: 10px;
            height:10px;
            position: absolute;
            bottom: 0;
        }
        .small-fire{
            width: 10px;
            height:10px;
            position: absolute;
            border-radius: 50%;
        }

这是html代码

<body>
<div id="container"></div>
</body>

这是js代码:(请务必加上之前的缓冲运动函数)

<script>
    var ocont=document.querySelector("#container");
    //点击外框,new个主题烟花
    ocont.onclick = function (eve) {
        var e=eve||window.event;
        new Fire({
            cont:ocont,
            x:e.clientX-this.offsetLeft-5,
            y:e.clientY-this.offsetTop-5
        });
    }
    //主题烟花的构造函数
    function Fire(options) {
        this.cont=options.cont;
        this.x=options.x;
        this.y=options.y;
        this.init();
    }
    //用来创建主题烟花,放在外框中,给其设置class和初始left
    Fire.prototype.init=function () {
        this.f=document.createElement("div");
        this.f.className = "fire";
        this.cont.appendChild(this.f);
        this.f.style.background=randomColor();
        this.f.style.left=this.x+"px";
        this.move();
    }
    //让主题烟花从外框底部到鼠标点击的位置
    Fire.prototype.move=function () {
        //大烟花从先到达鼠标的位置然后,消失,生成无数个各个方向飞的小烟花
        move(this.f,{top:this.y},()=>{//这里箭头函数可以代替回调函数,让其this指向他的外部
            this.f.remove();
            this.smallFire();
        })
    }

    //把s变成变量,因为move里回调函数里面的箭头函数没办法每次拿到s的值,所以把var改成let,让move外面的匿名函数的{}变成块级作用域,方便将来查找s。
    //想象把move用作用域包起来,不让箭头函数到最后再执行,可以获取到s每次变化的值。
    Fire.prototype.smallFire=function () { //定义创建小烟花的功能
        var num=random(10,20); //小烟花的个数
        var r=random(100,200)//小烟花的半径
        for (var i=0;i<num;i++){
            //问题1说明:循环中在创建小烟花,如果小烟花的元素保存到实例身上,那么一个属性只能保存一个元素,等待将来的元素时,只能删除最后一个
            //问题1解决办法:不保存在实例身上,单独保存成变量。
            //问题2说明保存成变量后,由于move里面有定时器,属于异步范畴,在for循环之后执行,重复覆盖,将来只能拿到最后一个
            //问题2解决方式:
            //1.利用作用域的嵌套(let块级作用域,匿名函数作用域),保存这个变量,方便将来查找;
            //2.利用bind将这个变量强行传参,传给this,通过将来的this找到每个小烟花。
            let s=document.createElement("div"); //构造小烟花
            s.className = "small-fire";
            s.style.left=this.x+"px";//以点击位置为中心
            s.style.top=this.y+"px";
            s.style.background=randomColor();
            this.cont.appendChild(s);
            // s.setAttribute("index",i);
            var target={
                x:parseInt(Math.sin(Math.PI/180*(360/num*i))*r)+this.x,//360/num是每个小烟花之间的角度,在用i把它们遍历成一个圆
                y:parseInt(Math.cos(Math.PI/180*(360/num*i))*r)+this.y
            }

            move(s,{   //move函数里面有计时器,在循环之后执行
                left:target.x,
                top:target.y
            },()=>{
                s.remove(); //延迟执行的函数里不能拿到for循环里的计数器也就是i,因为for循环立刻执行完,而move函数里的计时器最后执行,只消失一个小火焰,所以要用let,把for循环里面的花括号作为作用域,
            })
        }
    }
    function random(a,b) {
        return Math.round(Math.random()*(a-b)+b)
    }
    function randomColor() {
        return `rgb(${random(0,255)},${random(0,255)},${random(0,255)})`;
    }
</script>

这里的难点就是move函数里有定时器,如何让remove()方法的对象是每个小烟花。
1.利用作用域的嵌套(let块级作用域,匿名函数作用域),保存这个变量,方便将来查找;
2.利用bind将这个变量强行传参,传给this,通过将来的this找到每个小烟花。
3.可以将move方法外面套一个匿名函数,将s变量作为参数传入,自己构造一个作用域。

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值