欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

JS 面向对象 实现烟花特效

程序员文章站 2024-03-18 10:20:58
...

基本效果如图:
JS 面向对象 实现烟花特效
这里的烟花特效 是配合鼠标点击去实现的 (你要是想绑定别的事件也可)

  1. 创建一个烟花,从底部升起运动到目标位置
  2. 到达目标位置之后,删除它的同时 炸出一堆烟花

HTML布局+CSS样式
    <div class="container"></div>
    <style>
        .container{
            width: 80%;
            height: 300px;
            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;
        }
    </style>

JavaScript代码
// OOA
    // 1.创建一个烟花元素
    // 2.元素运动 运动结束之后删除
    // 3.烟花爆炸 循环多个烟花  爆炸之后也要删除
    // 4.随机位置
    // 5.烟花的随机颜色
// OOD
    // function Firework(x,y){
    //     this.init(x,y);
    // }
    
    // Firework.prototype = {
    //     constructor : Firework,
    //     // 初始化
    //     init : function(x,y){//xy是目标位置
    //         // 创建元素
    //         // 元素运动  把目标点当做参数x、y
    //         // 调用函数

    //     },
    //     // 1.创建元素
    //     createFireWorkEle : function(){

    //     },
    //     // 2.元素运动
    //     fireWorkUp : function(ele){
    //         // 两部分运动 left直接到达、top运动达到
    //     },
    //     // 3.烟花爆炸
    //     fireWorkBlast : function(){
    //         // 3.1创建非常多的元素
    //     },
    //     // 4.随机位置
    //     randomBoundary : function(){

    //     },
    //     // 5.随机颜色
    //     randomColor : function(ele){

    //     }
    // }
    <script>
    // OOP
        function Firework(x,y , selector){
            // 选择父级元素
            this.main = document.querySelector(selector);
            
            this.init(x,y);
        }
        
        Firework.prototype = {
            constructor : Firework,
            // 初始化
            init : function(x,y){
                //x、y是点击事件创建的烟花的位置
                this.x = x;
                this.y = y;

                // 创建元素
                this.ele = this.createFireWorkEle();
                // 爆炸烟花的随机位置 最大值
                this.left_max = this.main.offsetWidth - this.ele.offsetWidth;
                this.top_max = this.main.offsetHeight - this.ele.offsetHeight;

                // 元素添加背景色
                this.randomColor(this.ele);
                // 烟花主体升起
                this.fireWorkUp(this.ele );
                // 烟花爆炸
                this.fireWorkBlast(this.ele);
            },
            // 1.创建元素
            createFireWorkEle : function(){
                // 往containe里面放进一个烟花fire
                var ele = document.createElement("div");
                ele.className = "fire";
                // 在页面显示 
                this.main.appendChild(ele);
                return ele;
            },
            // 2.元素运动
            fireWorkUp : function(ele){
                // 两部分的运动 left直接到达、top运动达到
                ele.style.left = this.x + "px";
                animate(ele, {top : this.y} , function(){
                    // 烟花运动结束后 要删除它
                    // console.log(this);//这时候指向windoe,用bind修改指向
                    ele.remove();
                    // 然后调用烟花爆炸
                    this.fireWorkBlast();
                }.bind(this));
            },
            // 3.烟花爆炸
            fireWorkBlast : function(){
                // 3.1创建非常多的元素
                for(var i = 0 ; i < 20 ; i ++){
                    var ele = this.createFireWorkEle();
                    this.randomColor(ele);
                    // 3.1初始样式设置
                    ele.style.left = this.x + "px";
                    ele.style.top = this.y + "px";
                    // 和点击之后创建的烟花 区分开
                    ele.style.borderRadius = "50%";
                    // 3.3让元素有运动目标
                    animate(ele , this.randomBoundary(ele) , function(callback_ele){
                        // 爆炸之后删除所有元素
                        // 用bind 给每个匿名函数都绑定一个ele。否则只会删除最后一个ele
                        callback_ele.remove();
                    }.bind(this , ele));
                }
            },
            // 4.随机位置
            randomBoundary : function(){
                // min 是0 
                // max中offset性能消耗大,所以放在init()里,只获取一次即可
                return{
                    left : parseInt(Math.random()*(this.left_max + 1)),
                    top : parseInt(Math.random()*(this.top_max + 1))
                }
            },
            // 5.随机颜色
            randomColor : function(ele){
                // 随机颜色方法很多
                var r = parseInt(256 * Math.random());
                var g = parseInt(256 * Math.random());
                var b = parseInt(256 * Math.random());
                var random_color = "rgb("+r + "," + g + "," + b +")";
                return ele.style.backgroundColor = random_color;
                
            }
        }

        document.querySelector(".container").addEventListener("click",function(evt){
            var e = evt || event;
            new Firework(e.offsetX , e.offsetY , ".container");
        })
    </script>


script 引入的 "animate.js" 运动封装
//               元素,属性,回调函数(动画执行结束,调用这个函数)
function animate( ele , attr_options , callback ){
    // 获取当前属性
    for(var attr in attr_options){
        // 同时判断是否是opacity属性
        attr_options[attr] = {
            // 目标点(传入的数据)
            target : attr === "opacity" ? attr_options[attr] * 100 : attr_options[attr],
            // 元素当前的属性值
            iNow   : attr === "opacity" ? parseInt( getComputedStyle(ele)[attr] * 100 ) : parseInt( getComputedStyle(ele)[attr])
        }
    }
    // 定时器的开启和关闭
    clearInterval( ele.timer );
    ele.timer = setInterval( function(){
        // 获取运动所必须的值
        for(var attr in attr_options){
            // 取出每一条数据
            var item = attr_options[attr];
            // console.log(item , attr);//target和iNow , "属性名"
            var target = item.target;
            var iNow   = item.iNow;
            // 计算速度
            var speed = (target - iNow) / 10;
            // 速度取整
            speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
            // 运动的终止条件  
            // (目标 - 当前位置) 的绝对值 <= 速度 , 此时判定已经到达
            if( Math.abs( target - iNow) <= Math.abs(speed) ){
                // 送他到底目标点
                ele.style[attr] = attr === "opacity" ? target / 100 :  target + "px";
                // if里的终止条件不严谨:
                // 因为目标的不一致会让运动次数执行不同,有可能会提前关闭定时器
                // 解决办法:完成一条运动后 删除对象里的数据
                delete attr_options[attr];
            
                for(var num in attr_options){
                    // 如果attr_options里面有属性,不终止定时器
                    return false;
                }
                // 如果对象里面没有属性了,就可以关闭定时器
                clearInterval(ele.timer);
                // 可能会不传callback
                typeof callback === "function" ? callback() : "";
            }else{
                // 元素继续运动
                // 如果直接设置iNow 每次循环iNow都会被重置,iNow是一个临时变量
                // 所以不能去操作iNow,要去操作iNow的数据源
                attr_options[attr].iNow += speed;
                ele.style[attr] = attr === "opacity" ? attr_options[attr].iNow / 100 : attr_options[attr].iNow + "px";
            }
        }
    } , 30)
}