有不足之处欢迎指出
这一篇实现的功能是坦克走动
结构
<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给个固定像素、居中、边框线,太大了不好看,计算量也多了
坦克要绝对定位脱离文档流,这点很重要,不然是跑不起来的。
现在效果是这样的
行为( 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
当我们有了这个以后,就可以为所欲为了,比如我想按↑↓←→这四个键执行
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值进行移动,然后上下左右旋转不同的角度。
最后效果是这样