从零开始实现坦克大战1

356

有不足之处欢迎指出

这一篇实现的功能是坦克走动

结构

<div class="tank"> 
    我是坦克
    <div class="tube"></div>
</div>

目前结构很简单,外面是坦克壳子,里面是炮管

样式

body{
    max-width: 1000px;
    height: calc(100vh - 2px); /* 减去边框线的高度 */
    overflow: hidden;
    position: relative;
    border: 1px solid red;
    margin: 0 auto;
}
.tank{ /* 坦克 */
    width: 50px;
    height: 50px;
    background-color: red;
    position: absolute;
    top: 50%;
}
.tank .tube { /* 炮管 */
    width: 10px;
    height: 50%;
    position: absolute;
    left: 50%;
    transform: translateX(-50%);
    top: -50%;
    background-color: red;
}

body给个固定像素、居中、边框线,太大了不好看,计算量也多了

坦克要绝对定位脱离文档流,这点很重要,不然是跑不起来的。

现在效果是这样的

LRCGW`C991LQB5B)(}RZM{Y.png

行为( js )

构造实例

如果代码量太多还是把方法和属性放在构造函数里面方便,所以第一步是构造一个实例对象

class person {
  constructor() {
    this.el = { //存放标签
        tank: document.querySelector(".tank"),
        tube: document.querySelector(".tube")
      },
    this.timeID = { //计时器
      walkId: 0
    }
  }
  css(el, c1, c2) { //元素、属性、属性值
    el.style[c1] = c2;
    return this;
  }
}
const TankBattle = new person(); //构造实例

这里使用es6构造函数语法糖,把需要用到的东西都放在构造函数,方便调用。这里css是简单封装了一下,等会调用的时候就 this.css()就行了,方便了一点点。这里返回这个this是可以进行链式语法操作,以后没准会用上。

实现效果之前

想要实现效果,肯定需要键盘事件的,这里使用onkeydown键盘按下和onkeyup键盘抬起事件。

首先大家要知道onkeydown事件长按也是执行的,也就是说,你按住你键盘上的某一个键,函数内事件就会一直执行

但是有一个问题,就是太慢了,就算按住一直执行也太慢了,一个或者两个像素走跟乌龟爬一样,如果说一次太多个像素走又不自然。

后来的思路是,按住的时候执行一个setInterval定时器,抬起的时候清除定时器。

实现效果

判断按键

每当事件触发会穿一个event实例对象,判断event里的key

KPNF63J9YU(3@L}LBBI75.png

当我们有了这个以后,就可以为所欲为了,比如我想按↑↓←→这四个键执行

if(event.key==="ArrowUp"){

}else if(event.key==="ArrowDown"){
    
}else if(event.key==="ArrowLeft"){

}else if(event.key==="ArrowRight"){

}

这样写也没问题,但是重复代码太多,看起来太乱

所以我使用的方法是,把这些放在外面。

const tankData = {
  ArrowUp: {
    name: "上",
    rotate: "rotate(0)"
  },
  ArrowDown: {
    name: "下",
    rotate: "rotate(180deg)"
  },
  ArrowLeft: {
    name: "左",
    rotate: "rotate(270deg)"
  },
  ArrowRight: {
    name: "右",
    rotate: "rotate(90deg)"
  }
}

思路就是遍历对象再判断,如

for (k in tankData) {
if (k === e.key) { //判断上下左右
        
    }
}

移动

person.prototype.position = function (el) { //获取四维
  return {
    top:parseInt(window.getComputedStyle(el).top),
    left:parseInt(window.getComputedStyle(el).left),
    width:parseInt(window.getComputedStyle(el).width),
    height:parseInt(window.getComputedStyle(el).height)
  }
}

person.prototype.walk = function () { //移动、事件
  let count = 0, //计数,长按无效
    tank = this.el.tank,
    down = e => { //按下
      count++
      if (count > 1) return; //大于一不执行下面的
      for (k in tankData) {
        if (k === e.key) { //判断点击的key值是否和遍历出的k相同
          this.setInter(tank, 1, tankData[k].name);
          this.css(tank, "transform", tankData[k].rotate);//方向
        }
      }
    },
    up = e => { 
      count = 0; //键盘抬起为零
      clearInterval(this.timeID.walkId) //键盘抬起时清除
    };
    document.onkeydown = down; //键盘按下
    document.onkeyup = up; //键盘抬起
};

person.prototype.setInter = function (el, dalay, post) { //移动、执行
  let { //解构
    top,
    left,
    width,
    height
  } = this.position(el),
    body = this.position(document.body);//调用方法获取body宽高
  this.timeID.walkId = setInterval(() => { //计数器,并把它赋给实例上的属性,方便清除
    if (post === "上" || post === "下") {//判断上下,并且判断、不能出边界
      post === "上" && top > 0 ? top -= 1 : false;
      post === "下" && top < (body.height - height) ? top += 1 : false;
      this.css(el, "top", top + "px");
    } else {
      post === "左" && left > 0 ? left -= 1 : false;
      post === "右" && left < (body.width - width) ? left += 1 : false;
      this.css(el, "left", left + "px");
    };
  }, dalay);
};

TankBattle.walk(); //把事件放在函数里面。所以要执行

基本思路就是利用元素的left和top值进行移动,然后上下左右旋转不同的角度。

最后效果是这样

动画.gif