<canvas id="myCanvas" width="800" height="600" style="border: 1px solid #ccc;margin:50px;">您的浏览器不支持 HTML5 canvas 标签。</canvas>
//俄罗斯方块小游戏
var myTetris = {
init: function (id) {
var _this = this;
this.canvas = document.getElementById(id.replace("#", ""));
if (this.canvas.tagName.toLowerCase() !== "canvas") {
throw new Error("该元素不是canvas标签!")
}
this.ctx = this.canvas.getContext("2d");
this.width = this.canvas.width - 200 || 600;
this.height = this.canvas.height || 600;
this.unit = 30;
this.ctx.font = "18px Microsoft Yahei";
this.row = Math.floor(this.height / this.unit);
this.col = Math.floor(this.width / this.unit);
this.color = {
strokeColor: "#ddd",
backgroundColor: "#464741",
textColor: "#fff",
fillBlockColor: ["green"]
};
this.originRect = [
[[0, 0, 0, 0], [0, 0, 0, 0], [1, 1, 1, 1], [0, 0, 0, 0]], // 竖一
[[0, 0, 0, 0], [0, 1, 1, 0], [0, 1, 1, 0], [0, 0, 0, 0]], // 田
[[0, 0, 0, 0], [1, 1, 1, 0], [0, 1, 0, 0], [0, 0, 0, 0]], // 右T
[[0, 1, 1, 0], [0, 1, 0, 0], [0, 1, 0, 0], [0, 0, 0, 0]], // 左下L
[[0, 1, 1, 0], [0, 0, 1, 0], [0, 0, 1, 0], [0, 0, 0, 0]], // 左上L
[[0, 1, 0, 0], [0, 1, 1, 0], [0, 0, 1, 0], [0, 0, 0, 0]], // Z
[[0, 0, 1, 0], [0, 1, 1, 0], [0, 1, 0, 0], [0, 0, 0, 0]], // 反Z
[[0, 1, 0, 0], [0, 1, 0, 0], [0, 1, 0, 0], [0, 1, 0, 0]] // 横一
];
this.ctx.fillStyle = this.color.backgroundColor;
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
this.ctx.clearRect(0, 0, this.width, this.height);
this.reset();
// 绑定键盘方向键事件
this.addEventHandler("keydown", window, function (e) {
var e = e || window.event,
keyCode = e.keycode || e.which;
if (_this.keyEvents[keyCode]) {
// 只有在当前状态是进行中时才会触发键盘事件
if (_this.status !== 1) return
_this.keyEvents[keyCode].call(_this);
e.preventDefault();
}
});
this.addEventHandler("click", document, function (e) {
_this.ctx.rect(_this.width + 50, 330, 100, 40);
if (_this.ctx.isPointInPath(e.layerX, e.layerY)) {
switch (_this.status) { //0:尚未开始、重新开始;1:正在进行中;2:暂停;
case 0:
_this.status = 1;
_this.text = "暂停";
_this.timer = setInterval(function () {
_this.move(0, 1)
}, _this.speed);
break
case 1:
_this.status = 2;
_this.text = "继续";
clearInterval(_this.timer);
break
case 2:
_this.status = 1;
_this.text = "暂停";
_this.timer = setInterval(function () {
_this.move(0, 1)
}, _this.speed);
break
}
_this.drawText();
}
});
return this
},
reset: function () { //初始化上次运行产生的变量和数据
this.text = "开始";
this.score = 0;
this.status = 0;
this.timer = null;
this.speed = 1000;
this.nowRect = [];
this.nextRect = [];
this.containerArr = [];
for (var i = 0; i < this.row; i++) { //清空画布
var arr = [];
for (var j = 0; j < this.col; j++) { arr.push(0); };
this.containerArr.push(arr);
};
this.drawMap().drawText().drawNext();
return this
},
drawMap: function () { //左侧游戏主窗口
var unit = this.unit;
this.ctx.fillStyle = this.color.fillBlockColor;
this.ctx.strokeStyle = this.color.strokeColor;
this.ctx.clearRect(0, 0, this.width, this.height);
for (var i = 0; i < this.containerArr.length; i++) {
for (var j = 0; j < this.containerArr[i].length; j++) {
this.drawCanvas(this.containerArr[i][j], j * unit, i * unit, unit);
}
}
return this
},
drawText: function () { //右侧文字部分
this.ctx.save();
this.ctx.fillStyle = this.color.textColor;
this.ctx.fillText("下一个:", this.width + 30, 50);
this.ctx.fillStyle = this.color.backgroundColor;
this.ctx.fillRect(this.width + 30, 250, 120, 40);
this.ctx.fillStyle = this.color.textColor;
this.ctx.fillText("分数 :" + this.score, this.width + 30, 270);
this.ctx.fillStyle = this.color.fillBlockColor[0];
this.ctx.fillRect(this.width + 50, 330, 100, 40);
this.ctx.fillStyle = this.color.textColor;
this.ctx.fillText(this.text, this.width + 84, 356);
this.ctx.restore();
return this;
},
drawNext: function () { //右侧下一个方块部分
var unit = this.unit;
this.ctx.clearRect(this.width + 40, 80, unit * 4, unit * 4);
this.ctx.fillStyle = this.color.fillBlockColor;
this.ctx.strokeStyle = this.color.strokeColor;
this.nowRect = (this.nextRect.length == 0) ? this.clone(this.getRandomRect(), true) : this.clone(this.nextRect, true);
this.nowRect.left = 9;
this.nowRect.top = -4;
this.nextRect = this.clone(this.getRandomRect(), true);
for (var i = 0; i < this.nextRect.length; i++) {
for (var j = 0; j < this.nextRect[i].length; j++) {
this.drawCanvas(this.nextRect[i][j], (i * unit + this.width + 40), (j * unit + 80), unit);
}
}
return this
},
move: function (mx, my) {
this.changeMapData(0); //清空上次在画布上所占用位置
var state = this.ifmove(mx, my);
if (state) {
if (state == 1) {
this.nowRect.top += my;
this.nowRect.left += mx;
}
// 在画布上填充值
this.changeMapData(1);
} else {
// 不能移动,保存已固定在画布的值
this.changeMapData(1);
// 设置数组和变量保存满一行时消除
var clr = [];
for (i = 0; i < this.containerArr.length; i++) {
var boo = 0;
for (j = 0; j < this.containerArr[i].length; j++) {
if (this.containerArr[i][j]) { boo++ }
if (boo == 20) {
clr.unshift(i);
}
}
}
// clr = [19, 18, 17, 16];
// 消除并计算得分
if (clr.length > 0) {
this.score += 100 * (clr.length * 2 - 1);
for (var i = 0; i < clr.length; i++) {
this.containerArr.splice(clr[i], 1);
}
for (i = 0; i < clr.length; i++) {
var arr = [];
for (j = 0; j < this.containerArr[0].length; j++) {
arr.push(0);
}
this.containerArr.unshift(arr);
}
}
// 游戏结束
for (i = 0; i < this.containerArr[0].length; i++) {
if (this.containerArr[0][i]) {
clearInterval(this.timer);
this.reset();
break
}
}
this.drawText().drawNext();
}
this.drawMap();
},
rotate: function () { //旋转
this.changeMapData(0);
var rotateRect = [];
var saver = [];
for (var i = 0; i < this.nowRect.length; i++) { rotateRect.unshift([]); }
for (i = 0; i < this.nowRect.length; i++) {
for (var j = 0; j < this.nowRect[i].length; j++) {
rotateRect[j].unshift(this.nowRect[i][j]);
}
}
rotateRect.left = this.nowRect.left;
rotateRect.top = this.nowRect.top;
saver = this.nowRect;
this.nowRect = rotateRect;
if (this.ifmove(0, 0) != 1) {
this.nowRect = saver;
}
this.changeMapData(1);
this.drawMap();
},
ifmove: function (mx, my) {
// 0:往下不可移动,1:可以移动,2:左右不可移动
for (var i = 0; i < this.nowRect.length; i++) {
for (var j = 0; j < this.nowRect[i].length; j++) {
if (this.nowRect[i][j]) {
var px = j + this.nowRect.left + mx;
var py = i + this.nowRect.top + my;
if (px < 0) return 2
if (px > 19) return 2
if (py < 0) continue
if (py > 19) return 0
var fr = this.containerArr[py][px];
if (fr != 0) {
if (mx == 0) return 0
if (my == 0) return 2
}
}
}
}
return 1
},
keyEvents: {
37: function () {
this.move(-1, 0);
},
38: function () {
this.rotate();
},
39: function () {
this.move(1, 0);
},
40: function () {
this.move(0, 1);
}
},
drawCanvas: function (block, x, y, unit) { //提取绘制俄罗斯方块通用方法
this.ctx.save();
if (block) {
this.ctx.fillRect(x + 1, y + 1, unit - 1, unit - 1);
}
this.ctx.strokeRect(x, y, unit, unit);
this.ctx.restore();
},
changeMapData: function (type) { //提取改变画布对应方块值的通用方法
for (i = 0; i < this.nowRect.length; i++) {
for (j = 0; j < this.nowRect[i].length; j++) {
if (i + this.nowRect.top < 0) continue
if (this.nowRect[i][j]) {
this.containerArr[i + this.nowRect.top][j + this.nowRect.left] = type
}
}
}
},
getRandomRect: function (min, max) { //获取随机数
var min = 0,
max = this.originRect.length - 1,
random = Math.round(Math.random() * (max - min) + min);
return this.originRect[random]
},
addEventHandler: function (event, ele, callback) { //添加事件绑定
if (ele.addEventListener) {
ele.addEventListener(event, callback, false)
} else {
ele.attachEvent("on" + event, callback)
}
return this
},
isObjectType: function (obj) { //判断Object类型
var types = ["Object", "Array", "String", "Function", "Boolean", "Number", "Undefined", "Null", "Window", "Date", "RegExp", "HTMLDocument"];
for (var i = 0; i < types.length; i++) {
var type = types[i];
if (Object.prototype.toString.call(obj) === "[object " + type + "]") return type
}
},
clone: function (obj, deep) { //数组与纯对象的深拷贝
if (this.isObjectType(obj) === "Object") {
var target = {},
deep = (this.isObjectType(deep) === "Boolean") ? deep : false;
for (var key in obj) {
target[key] = (deep && this.isObjectType(obj[key]) === "Object") ? this.clone(obj[key], deep) : obj[key];
}
return target
} else if (this.isObjectType(obj) === "Array") {
return obj.concat()
} else {
throw new Error("拷贝对象类型错误!")
}
}
}
myTetris.init("#myCanvas");