supce's blog

IFE Task 13


任务目的

  • 练习JavaScript面向对象设计
  • 实践一些基础的设计模式

任务描述

  • 创建一个虚拟宇宙,包括一个行星和飞船
  • 每个飞船由以下部分组成
  • 动力系统,可以完成飞行和停止飞行两个行为,暂定所有飞船的动力系统飞行速度是一致的,比如每秒20px,飞行过程中会按照一定速率消耗能源(比如每秒减5%)
  • 能源系统,提供能源,并且在宇宙中通过太阳能充电(比如每秒增加2%,具体速率自定)
    信号接收处理系统,用于接收行星上的信号
  • 自爆系统,用于自我销毁
  • 每个飞船的能源是有限的,用一个属性来表示能源剩余量,这是一个百分比,表示还剩余多少能源。
  • 能源耗尽时,飞船会自动停止飞行
  • 飞船有两个状态:飞行中和停止,飞船的行为会改变这个属性状态
  • 飞船的自我销毁方法会立即销毁飞船自身
  • 行星上有一个指挥官(不需要在页面上表现出其形象),指挥官可以通过行星上的信号发射器发布如下命令
  • 创建一个新的飞船进入轨道,最多可以创建4个飞船,刚被创建的飞船会停留在某一个轨道上静止不动
  • 命令某个飞船开始飞行,飞行后飞船会围绕行星做环绕运动,需要模拟出这个动画效果
  • 命令某个飞船停止飞行
  • 命令某个飞船销毁,销毁后飞船消失、飞船标示可以用于下次新创建的飞船
  • 你需要设计类似如下指令格式的数据格式
    {id: 1,commond: 'stop'}
  • 指挥官通过信号发射器发出的命令是通过一种叫做Mediator的介质进行广播
  • Mediator是单向传播的,只能从行星发射到宇宙中,在发射过程中,有30%的信息传送失败(丢包)概率,你需要模拟这个丢包率,另外每次信息正常传送的时间需要1秒
  • 指挥官并不知道自己的指令是不是真的传给了飞船,飞船的状态他是不知道的,他只能通过自己之前的操作来假设飞船当前的状态
  • 每个飞船通过信号接收器,接受到通过Mediator传达过来的指挥官的广播信号,但因为是广播信号,所以每个飞船能接受到指挥官发出给所有飞船的所有指令,因此需要通过读取信息判断这个指令是不是发给自己的

整体思路

首先是HTML框架,需要一个虚拟宇宙,一个指挥操控板,一个消息框。

<div id="main" class="clear_fix">
    <div id="universe">
        <div class="path path_four">
            <div class="craft craft_four"></div>
            <div class="path path_three">
                <div class="craft craft_three"></div>
                <div class="path path_two">
                    <div class="craft craft_two"></div>
                    <div class="path path_one">
                        <div class="craft craft_one"></div>
                        <div class="planet">用爱发电</div>  
                    </div>
                </div>
            </div>
        </div>
    </div>
    <div id="messager">
        <h1>消息窗</h1>
    </div>
</div>

<div id="control_panel">
    <ul>
        <li class="li_hidden" id="0">
            <label>轨道一: </label> 
            <button type="button" value="start" class="btn_start">开始</button>
            <button type="button" value="stop" class="btn_stop">暂停</button>
            <button type="button" value="destroy" class="btn_destroy">销毁</button>
        </li>
        <li class="li_hidden" id="1">
            <label>轨道二: </label> 
            <button type="button" value="start" class="btn_start">开始</button>
            <button type="button" value="stop" class="btn_stop">暂停</button>
            <button type="button" value="destroy" class="btn_destroy">销毁</button>
        </li>
        <li class="li_hidden" id="2">
            <label>轨道三: </label> 
            <button type="button" value="start" class="btn_start">开始</button>
            <button type="button" value="stop" class="btn_stop">暂停</button>
            <button type="button" value="destroy" class="btn_destroy">销毁</button>
        </li>
        <li class="li_hidden" id="3">
            <label>轨道四: </label> 
            <button type="button" value="start" class="btn_start">开始</button>
            <button type="button" value="stop" class="btn_stop">暂停</button>
            <button type="button" value="destroy" class="btn_destroy">销毁</button>
        </li>
    </ul>
    <label id="panel_title">飞船操控板</label>
    <button id="craft_creat">增加新飞船</button>
</div>

然后需要创建一个飞船类,有飞船id,飞船能量,飞船状态,飞船能量控制四个属性、还有飞船启动,停止,销毁三个方法和飞船能量消耗,能量增加两个方法。

//飞船类
function Craft(craftId,onPath){
    //飞船id
    this.id = craftId;
    //飞船是否部署到预定轨道
    this.onPath = onPath;
    //飞船能量
    this.power = 100;
    //飞船是否正在飞行
    this.state = "stop";
    //控制飞船动力系统
    this.timer = null;
}
Craft.prototype = {
    constructor: Craft,
    receiveCommond : function(commond){
        if(commond.id == this.id){
            order = commond.commond;
            switch(order){
                case 'start':
                    msgControl("有嘎达~ 信息传递成功!");
                    this.powerConsume();
                    this.state = "start";
                    this.start();
                    break;
                case 'stop':
                    msgControl("有嘎达~ 信息传递成功!");
                    this.powerAdd();
                    this.state = "stop";
                    this.stop();
                    break;
                case 'destroy':
                    msgControl("有嘎达~ 信息传递成功!");
                    this.destroy();
                    break;
            }
        }
    },
    start : function(){
        msgControl("Nozomi power注入! 嗨~ 噗咻!" + (this.id+1) + "号飞船已经起飞");
        var craftImg = document.querySelector("." + craftsMap[this.id]);
        craftImg.classList.add("craft_start");
    },
    stop : function(){
        msgControl("sa si su se so!" + (this.id+1) + "号飞船停止飞行");
        var craftImg = document.querySelector("." + craftsMap[this.id]);
        craftImg.classList.remove("craft_start"); 
    },
    destroy : function(){
        msgControl("dame! dame!" + (this.id+1) + "号飞船被销毁了");
        //先停止再销毁
        // this.stop();
        var craftImg = document.querySelector("." + craftsMap[this.id]);
        craftImg.classList.remove("craft_start");
        clearInterval(this.timer);
        var id = this.id;
        //销毁飞船
        var craftImg = document.querySelector("." + craftsMap[id]);
        craftImg.classList.remove("craft_create");
        //销毁控制板
        document.getElementById(id).classList.add("li_hidden");
        //将飞船从轨道撤下
        for(var i=0,len=crafts.length;i<len;i++){
            if(crafts[i].id == id){
                crafts[i].onPath = "off";
                crafts[i].power = 100;
                crafts[i].state = "stop";
            }
        }
    },
    powerConsume : function(){
        var _this = this;
        if(this.timer){
            clearInterval(this.timer);
        }
        this.timer = setInterval(run,100);
        function run(){
            if(_this.power>0){
                _this.power = _this.power - 1;
                var craftImg = document.querySelector("." + craftsMap[_this.id]);
                craftImg.innerHTML = _this.power + "%";
            }else{
                _this.stop();
                _this.state = "charge";
                clearInterval(_this.timer);
                _this.powerAdd();
            }
        }
    },
    powerAdd : function(){
        var _this = this;
        if(this.timer){
            clearInterval(this.timer);
        }
        this.timer = setInterval(run,300);
        function run(){
            if(_this.power<100){
                _this.power = _this.power + 1;
                var craftImg = document.querySelector("." + craftsMap[_this.id]);
                craftImg.innerHTML = _this.power + "%";
            }else{
                if(_this.state == "charge"){
                    _this.start();
                    _this.state = "start";
                    clearInterval(_this.timer);
                    _this.powerConsume();
                }else{
                    clearInterval(_this.timer);
                }
            }
        }
    }
}

然后需要一个指挥官类,可以创建飞船和指挥飞船

//指挥官类
function Commonder(){
    this.craftId = 0;
}
Commonder.prototype = {
    createCommond : function(id,order){
        return {
            id: id,
            commond: order
        };
    },
    createCraft : function(){
        for(var i=0,len=crafts.length;i<len;i++){
            if(crafts[i].onPath == "off"){
                var id = crafts[i].id;
                crafts[i].onPath = "on";
                //使飞船送达预定轨道
                var craftImg = document.querySelector("." + craftsMap[id]);
                craftImg.classList.add("craft_create");
                craftImg.innerHTML = crafts[i].power + "%";
                //创建panel
                var panel = document.getElementById(id);
                panel.classList.remove("li_hidden");
                var btns = panel.getElementsByTagName('button');
                for(var i=0,len=btns.length;i<len;i++){
                    addHandler(btns[i],"click",function(e){
                        btnHandler(e);
                    });
                }
                msgControl("飞船" + (id+1) + "号已到达预定轨道,请发送下个命令撒~");
                break;
            }
        }
    }
}

最后是一个消息发射器,负责信号的发送。这里每次会给所有飞船发送信号

//信号发射器类
function Mediator(){
}
Mediator.prototype = {
    sendCommond : function(commond){
        var num = Math.floor(Math.random()*10+1);
        if(num > 3){
            for(var i=0,len=crafts.length;i<len;i++){
            if(crafts[i].onPath == "on"){   
                crafts[i].receiveCommond(commond);
            }
        }
        }else{
            msgControl("信息传递失败!再试一次,fight だよ!");
        }
    },
}

对了,还有一个信号显示器,负责消息的显示

//信号显示器
function msgControl(str){
    var msg = $("messager");
    msg.innerHTML += "<p>" + str + "</p>";
    msg.scrollTop = msg.scrollHeight;
    console.log(str);
}

飞船的飞行效果是利用animation实现的,当飞船能量消耗完毕后,会自动暂停恢复能量。如果期间收到起飞命令,会起飞,否则会在能量到达100%时自动启动。


最终代码

最终代码
demo