看代码注释

完整代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>2048</title>
<style>
* {
padding: 0;
margin: 0;
}
#main {
margin: 20px auto;
width: 500px;
height: 700px;
/* border: 1px solid #000; */
}
</style>
</head>
<body>
<div id="main"></div>
<script>
class Animate {
startData = 0;// 起始数据
endData = 0; // 终止数据
tempData = 0; // 当前数据
time = 1000; // 动画完成时间
timer; // 内部定时器
status = 0; // 状态 -- start: 1 -- pause: 2 -- stop: 0
pauseTime = 0; // 暂停时间
startTime = 0; // 开始时间
tempPauseTime = 0; // 开始暂停的时间
finishCallBack = () => { }; // 结束回调
updateCallBack = () => { }; // 更新回调
startCallBack = () => { }; // 开始回调
ani = () => { };
easeIn(startTime, nowTime, time, startNum, length) {
const t = (nowTime - startTime) / time;
const point = this.CalculateBezierPointForCube(t, [0, 0], [0, 0.4], [0.6, 1], [1, 1]);
return startNum + length * point[0];
}
/**
* 三阶贝塞尔曲线
* B(t) = start * (1-t)^3 + 3 * p0 * t * (1-t)^2 + 3 * p1 * t^2 * (1-t) + end * t^3, t ∈ [0,1]
* @param {Number} t 曲线长度比例 t ∈ [0,1]
* @param {Object{x,y}} start 起始点
* @param {Object{x,y}} p0 控制点1
* @param {Object{x,y}} p1 控制点2
* @param {Object{x,y}} end 终止点
* @return t比例时对应的点 {x,y}
*/
CalculateBezierPointForCube(t, start, p0, p1, end) {
const temp = 1 - t;
let point;
if (start instanceof Array) {
point = [];
point[0] = start[0] * temp * temp * temp + 3 * p0[0] * t * temp * temp + 3 * p1[0] * t * t * temp + end[0] * t * t * t;
point[1] = start[1] * temp * temp * temp + 3 * p0[1] * t * temp * temp + 3 * p1[1] * t * t * temp + end[1] * t * t * t;
} else {
point = {};
point.x = start.x * temp * temp * temp + 3 * p0.x * t * temp * temp + 3 * p1.x * t * t * temp + end.x * t * t * t;
point.y = start.y * temp * temp * temp + 3 * p0.y * t * temp * temp + 3 * p1.y * t * t * temp + end.y * t * t * t;
}
return point;
}
/**
* 开始数值
* @param {Number/Array} start 必填
* @returns this
*/
from(start) {
this.startData = start;
return this;
}
/**
* 结束数值
* @param {Number/Array} end 必填
* @returns this
*/
to(end) {
this.endData = end;
return this;
}
/**
* 动画时间
* @param {Number} time 选填
* @returns this
*/
setTime(time) {
this.time = time;
return this;
}
calculating(startTime, nowTime, time, startNum, length) {
return this.easeIn(startTime, nowTime, time, startNum, length);
}
/**
* 暂停
* @returns this
*/
pause() {
if (this.status === 1) {
this.tempPauseTime = Date.now();
cancelAnimationFrame(this.timer);
this.status = 2;
}
return this;
}
/**
* 停止
* @param {Function} stopCallBack ---- params(start, end, temp) 停止回调 选题
* @returns this
*/
stop(stopCallBack) {
this.status = 0;
this.pauseTime = 0;
cancelAnimationFrame(this.timer);
this.timer = null;
stopCallBack instanceof Function && stopCallBack(this.startData, this.endData, this.tempData);
return this;
}
/**
* 继续
* @returns this
*/
continue() {
if (this.status === 2) {
this.pauseTime += Date.now() - this.tempPauseTime;
this.status = 1;
this.ani();
}
return this;
}
/**
* 开始回调
* @param {Function} startCallBack 开始回调
* @returns
*/
onStart(startCallBack) {
startCallBack instanceof Function && (this.startCallBack = startCallBack);
return this;
}
onUpdate(updateCallBack) {
updateCallBack instanceof Function && (this.updateCallBack = updateCallBack);
return this;
}
finish() {
this.finishCallBack(this.startData, this.endData);
}
/**
* 结束回调
* @param {Function} finishCallBack ---- (start, end) 结束回调
* @returns
*/
onFinish(finishCallBack) {
this.finishCallBack = finishCallBack;
return this;
}
}
class ArrayAnimation extends Animate {
constructor() {
super();
}
ani = () => {
const nowDate = Date.now();
if (nowDate - this.startTime - this.pauseTime >= this.time) {
this.status = 0;
this.updateCallBack(this.startData, this.endData, this.endData);
this.finish();
} else {
this.tempData = [];
for (let i = 0; i < this.startData.length; i++) {
this.tempData[i] = this.calculating(this.startTime, nowDate - this.pauseTime, this.time, this.startData[i], this.length[i]);
}
this.updateCallBack(this.startData, this.endData, this.tempData);
this.timer = requestAnimationFrame(this.ani);
}
}
start() {
this.frameCount = this.time / 1000 * this.fps;
this.length = [];
for (let i = 0; i < this.startData.length; i++) {
this.length[i] = this.endData[i] - this.startData[i];
}
this.frameNow = 0;
this.startCallBack();
this.status = 1;
this.startTime = Date.now();
this.ani();
return this;
}
}
class Cell {
constructor(id, position, x, y, size, value = 0) {
this.id = id;
this.position = position;
this.x = x;
this.y = y;
this.size = size;
this.value = value;
}
copy() {
return new this.constructor(this.id, this.position, this.x, this.y, this.size, this.value);
}
}
class Game2048 {
canvas = document.createElement('canvas'); // 画布
ctx = this.canvas.getContext('2d'); // 画笔
cells = []; // 格子数据
viewCells = []; // 格子视图数据
animCells = []; // 动画中的格子
cellFontSize = 30; // 数字尺寸
#size = 4; // 尺寸
get size() {
return this.#size;
}
set size(value) {
this.#size = value;
this.cellFontSize = ~~(15 * (8 / value));
}
gamePadding = 20; // 游戏内边距
cellMargin = 10; // 格子外边距
gameWidth = 500; // 游戏尺寸
header = 200; // 头部信息高度
headerPadding = 10; // 头部内边距
background = 'rgb(100,100,200)'; // 游戏背景
cellTextColor = 'rgb(255,255,255)'; // 格子数字颜色
titleColor = 'rgb(255,255,255)'; // 描述本文颜色
gameStatus = 0; // 游戏状态 0:未开始 1:进行中 2:结束
isGameOver = true;
_scoreValue = 0;
get scoreValue() { // 本次游戏总分
return this._scoreValue;
}
set scoreValue(value) {
this._scoreValue = value;
if (this._scoreValue > this.bestScore) {
this.bestScore = this._scoreValue;
}
}
bestScore = 0;// 最佳得分
hover; // 高亮的按钮
hoverColor = 'rgba(0,0,0, 0.2)'; // 按钮hover背景
startButtonSize = { // 开始按钮定位
id: 'start',
x: 5,
y: 150,
width: 80,
height: 40
};
resetButtonSize = { // 重置按钮定位
id: 'reset',
x: 103,
y: 150,
width: 80,
height: 40
};
sizeAddButtonSize = { // 增加尺寸按钮定位
id: 'sizeAdd',
x: 75,
y: 103,
width: 30,
height: 30
};
sizeSubtractButtonSize = { // 减小尺寸按钮定位
id: 'sizeSubtract',
x: 135,
y: 103,
width: 30,
height: 30
};
gameContentSize = { // 游戏内容尺寸
x: 0,
y: 200,
size: 500
}
gameOverImg = { // 游戏结束图形定位
x: 250,
y: 450,
width: 500,
height: 500,
radius: 200
};
shouldCreateRandomValue = false; // 本次操作是否生成新数字
buttons = [this.startButtonSize, this.resetButtonSize, this.sizeAddButtonSize, this.sizeSubtractButtonSize];
animSum = 0; // 当前执行的动画个数
animTime = 60; // 每次操作动画执行时间
isChange = false; // 是否通过操作改变数据
canChange = true; // 是否可以操作
touchstart = null;
isTouchmove = false;
cellColorList = new Map([ // 格子颜色组
[0, 'rgba(50, 50, 50,0.3)'],
[2, 'rgb(140, 200, 130)'],
[4, 'rgb(140, 200, 130)'],
[8, 'rgb(90, 160, 255)'],
[16, 'rgb(140, 90, 180)'],
[32, 'rgb(230, 200, 80)'],
[64, 'rgb(200, 60, 50)'],
[128, 'rgb(90, 120, 100)'],
[256, 'rgb(190, 120, 60)'],
[512, 'rgb(70, 120, 120)'],
[1024, 'rgb(120, 80, 130)'],
[2048, 'rgb(120, 40, 100)'],
[4096, 'rgb(120,180,190)'],
[8192, 'rgb(80,90,110)'],
[8192, 'rgb(60,70,80)'],
[16384, 'rgb(40,40,50)'],
]);
constructor(el) {
this.el = el;
this.init();
}
// 初始化
init() {
this.initCanvas();
this.el.appendChild(this.canvas);
this.initData();
this.initEvent();
this.ani();
}
// 初始化canvas
initCanvas() {
this.canvas.width = this.gameWidth;
this.canvas.height = this.gameWidth + this.header;
this.canvas.style.background = this.background;
this.cellSize = (this.canvas.width - this.gamePadding * 2 - (this.size - 1) * this.cellMargin) / this.size;
}
// 初始化数据
initData() {
for (let i = 0; i < this.size; i++) {
this.cells[i] = [];
this.viewCells[i] = [];
for (let j = 0; j < this.size; j++) {
const x = this.gamePadding + this.cellMargin * j + j * this.cellSize;
const y = this.header + this.gamePadding + this.cellMargin * i + i * this.cellSize;
this.cells[i][j] = new Cell(i + j, [i, j], x, y, this.cellSize);
this.viewCells[i][j] = new Cell(i + j, [i, j], x, y, this.cellSize);
}
}
}
// 获取颜色
getCellColor(value) {
return this.cellColorList.get(value) || 'rgb(30,30,40)';
}
draw() {
this.drawHeader();
this.drawCell();
this.drawAnimCell();
this.drawGameOverWarp();
}
drawGameOverWarp() {
if (this.gameStatus === 2) {
const { x, y, width, height, radius } = this.gameOverImg;
this.ctx.beginPath();
this.ctx.fillStyle = 'rgb(0,0,0,0.2)';
this.ctx.fillRect(0, this.header, this.gameWidth, this.gameWidth);
this.ctx.closePath();
this.ctx.beginPath();
this.ctx.fillStyle = 'rgb(0,0,0,0.3)';
this.ctx.arc(x, y, radius, 0, Math.PI * 2);
this.ctx.fill();
this.ctx.closePath();
this.ctx.beginPath();
this.ctx.font = `60px cursive`;
this.ctx.fillStyle = this.titleColor;
this.ctx.textAlign = 'center';
this.ctx.textBaseline = 'middle';
this.ctx.fillText('GAME OVER', x, y);
this.ctx.closePath();
}
}
drawHeader() {
if (this.hover) {
const { x, y, width, height } = this.hover;
this.ctx.fillStyle = this.hoverColor;
this.ctx.fillRect(x, y, width, height);
this.canvas.style.cursor = 'pointer';
} else {
this.canvas.style.cursor = 'default';
}
{
// 绘制title
this.ctx.beginPath();
this.ctx.font = `70px fantasy`;
this.ctx.fillStyle = this.titleColor;
this.ctx.textAlign = 'center';
this.ctx.textBaseline = 'middle';
this.ctx.fillText('2048', 250, 50);
this.ctx.closePath();
// 绘制size
this.ctx.beginPath();
this.ctx.font = `20px cursive`;
this.ctx.fillStyle = this.titleColor;
this.ctx.textAlign = 'left';
this.ctx.textBaseline = 'middle';
this.ctx.fillText('Size:', this.headerPadding + 5, 120);
this.ctx.closePath();
// 绘制size ++
this.ctx.beginPath();
this.ctx.font = `30px cursive`;
this.ctx.fillStyle = this.titleColor;
this.ctx.textAlign = 'center';
this.ctx.textBaseline = 'middle';
this.ctx.fillText('+', this.headerPadding + 80, 120);
this.ctx.closePath();
// 绘制size
this.ctx.beginPath();
this.ctx.font = `30px cursive`;
this.ctx.fillStyle = this.titleColor;
this.ctx.textAlign = 'center';
this.ctx.textBaseline = 'middle';
this.ctx.fillText(this.size, this.headerPadding + 110, 120);
this.ctx.closePath();
// 绘制size --
this.ctx.beginPath();
this.ctx.font = `30px cursive`;
this.ctx.fillStyle = this.titleColor;
this.ctx.textAlign = 'center';
this.ctx.textBaseline = 'middle';
this.ctx.fillText('-', this.headerPadding + 140, 120);
this.ctx.closePath();
// 绘制Play
this.ctx.beginPath();
this.ctx.font = `30px fangsong`;
this.ctx.fillStyle = this.titleColor;
this.ctx.textAlign = 'left';
this.ctx.textBaseline = 'middle';
this.ctx.fillText('Play', this.headerPadding + 8, 170);
this.ctx.closePath();
// 绘制reset
this.ctx.beginPath();
this.ctx.font = `30px fangsong`;
this.ctx.fillStyle = this.titleColor;
this.ctx.textAlign = 'left';
this.ctx.textBaseline = 'middle';
this.ctx.fillText('Reset', this.headerPadding + 100, 170);
this.ctx.closePath();
// 绘制Score title
this.ctx.beginPath();
this.ctx.font = `20px cursive`;
this.ctx.fillStyle = this.titleColor;
this.ctx.textAlign = 'right';
this.ctx.textBaseline = 'middle';
this.ctx.fillText('Score:', 400, 170);
this.ctx.closePath();
// 绘制Score
this.ctx.beginPath();
this.ctx.font = `22px cursive`;
this.ctx.fillStyle = this.titleColor;
this.ctx.textAlign = 'left';
this.ctx.textBaseline = 'middle';
this.ctx.fillText(this.scoreValue, 410, 120);
this.ctx.closePath();
// 绘制Best Score title
this.ctx.beginPath();
this.ctx.font = `20px cursive`;
this.ctx.fillStyle = this.titleColor;
this.ctx.textAlign = 'right';
this.ctx.textBaseline = 'middle';
this.ctx.fillText('BestScore:', 400, 120);
this.ctx.closePath();
// 绘制Best Score
this.ctx.beginPath();
this.ctx.font = `22px cursive`;
this.ctx.fillStyle = this.titleColor;
this.ctx.textAlign = 'left';
this.ctx.textBaseline = 'middle';
this.ctx.fillText(this.bestScore, 410, 170);
this.ctx.closePath();
}
}
drawCell() {
for (let i = 0; i < this.size; i++) {
for (let j = 0; j < this.size; j++) {
this.drawRect(this.viewCells[i][j]);
this.drawText(this.viewCells[i][j]);
}
}
}
// 绘制移动中的格子
drawAnimCell() {
for (let i = 0; i < this.animCells.length; i++) {
this.drawRect(this.animCells[i]);
this.drawText(this.animCells[i]);
}
}
drawRect(cell) {
const { x, y, value } = cell;
this.ctx.beginPath();
this.ctx.moveTo(x, y);
this.ctx.lineTo(x + this.cellSize, y);
this.ctx.lineTo(x + this.cellSize, y + this.cellSize);
this.ctx.lineTo(x, y + this.cellSize);
this.ctx.lineTo(x, y);
this.ctx.fillStyle = this.getCellColor(value);
this.ctx.fill();
this.ctx.closePath();
}
drawText(cell) {
const { x, y, value } = cell;
if (value) {
this.ctx.beginPath();
this.ctx.font = `${this.cellFontSize}px Verdana`;
this.ctx.fillStyle = this.cellTextColor;
this.ctx.textAlign = 'center';
this.ctx.textBaseline = 'middle';
this.ctx.fillText(value, x + this.cellSize / 2, y + this.cellSize / 2);
this.ctx.closePath();
}
}
clear() {
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
}
// 判断游戏状态并生成新的数字
randomEmptyCellValue() {
const emptyCells = [];
for (let i = 0; i < this.size; i++) {
for (let j = 0; j < this.size; j++) {
const cell = this.cells[i][j];
if (cell.value === 0) {
emptyCells.push(cell);
}
}
}
// 没有空格子进入判断游戏状态
if (emptyCells.length) {
if (this.shouldCreateRandomValue) {
this.isChange = true;
emptyCells[this.random(emptyCells.length - 1)].value = Math.random() > 0.75 ? 4 : 2;
}
} else {
this.isGameOver = true;
this.isGameOver && this.moveUp(true);
this.isGameOver && this.moveDown(true);
this.isGameOver && this.moveLeft(true);
this.isGameOver && this.moveRight(true);
if (this.isGameOver) {
console.log('game over');
this.gameStatus = 2;
}
}
}
random(max, min = 0) {
return ~~(Math.random() * (max - min) + min);
}
initEvent() {
document.addEventListener('keydown', (e) => {
if (this.isChange) {
return;
}
if (this.gameStatus === 1) {
// 操作方法合并变量过多,故分开处理
switch (e.keyCode) {
case 37:
this.moveLeft();
this.randomEmptyCellValue();
break;
case 38:
this.moveUp();
this.randomEmptyCellValue();
break;
case 39:
this.moveRight();
this.randomEmptyCellValue();
break;
case 40:
this.moveDown();
this.randomEmptyCellValue();
break;
default:
break;
}
}
});
this.canvas.addEventListener('click', (e) => {
if (this.hover) {
switch (this.hover.id) {
case 'start':
this.start();
break;
case 'reset':
this.reset();
break;
case 'sizeAdd':
if (this.size < 8) {
this.size++;
}
this.reset();
break;
case 'sizeSubtract':
if (this.size > 4) {
this.size--;
}
this.reset();
break;
default:
break;
}
}
});
this.canvas.addEventListener('mousemove', (e) => {
const { offsetX, offsetY } = e;
let i = this.buttons.length;
this.hover = null;
while (i--) {
const button = this.buttons[i];
if (
offsetX > button.x
&& offsetX < button.x + button.width
&& offsetY > button.y
&& offsetY < button.y + button.height
) {
this.hover = button;
break;
}
}
});
this.canvas.addEventListener('touchstart', (e) => {
if (this.gameStatus !== 1) {
return;
}
const { x, y, size } = this.gameContentSize;
if (e.changedTouches.length) {
const { clientX, clientY } = e.changedTouches[0];
if (clientX > x && clientX < x + size && clientY > y && clientY < y + size) {
this.touchstart = [clientX, clientY];
} else {
this.touchstart = null;
}
}
});
this.canvas.addEventListener('touchmove', (e) => {
if (this.gameStatus !== 1) {
return;
}
const { x, y, size } = this.gameContentSize;
if (e.changedTouches.length) {
const { clientX, clientY } = e.changedTouches[0];
if (clientX > x && clientX < x + size && clientY > y && clientY < y + size) {
e.preventDefault();
}
}
this.isTouchmove = true;
});
this.canvas.addEventListener('touchend', (e) => {
setTimeout(() => { // 取消按钮点击效果
this.hover = null;
}, 200);
if (this.gameStatus !== 1) {
return;
}
if (this.isTouchmove) {
if (this.touchstart) {
if (e.changedTouches.length) {
const [x, y] = this.touchstart;
const { clientX, clientY } = e.changedTouches[0];
const disX = clientX - x;
const disY = clientY - y;
if (Math.abs(disX) > Math.abs(disY)) { // 横向
if (disX > 0) {
this.moveRight();
} else {
this.moveLeft();
}
} else { // 纵向
if (disY > 0) {
this.moveDown();
} else {
this.moveUp();
}
}
this.randomEmptyCellValue();
}
}
}
this.touchstart = null;
this.isTouchmove = false;
});
}
// 创建动画
createAnim(start, end) {
const animCell = start.copy();
this.animCells.push(animCell);
new ArrayAnimation().from([start.x, start.y]).to([end.x, end.y]).setTime(this.animTime).onStart(() => {
this.animSum++;
}).onUpdate((s, e, t) => {
animCell.x = t[0];
animCell.y = t[1];
}).onFinish(() => {
this.animSum--;
this.animCells = this.animCells.filter(item => item.id !== animCell.id);
}).start();
}
// 从上往下比较合并
moveUp(check) {
this.shouldCreateRandomValue = false;
for (let j = 0; j < this.size; j++) { // 每列
let row = 0; // 目标元素所在行
let i = row + 1; // 对比元素所在行
while (i < this.size) {
if (row === this.size - 1) break; // 目标元素为倒数第二行时终止
const topCell = this.cells[row][j]; // 目标元素
const cell = this.cells[i][j];
const viewCell = this.viewCells[i][j];
if (topCell.value === 0) { // 目标元素为0时将当前列数据赋值给目标元素
topCell.value = cell.value;
if (topCell.value !== 0) {
this.shouldCreateRandomValue = true;
// 创建动画并执行
this.createAnim(cell, topCell);
}
cell.value = 0;
viewCell.value = 0;
i++;
} else if (topCell.value === cell.value) { // 相等时顶个目标元素值 * 2
if (check) { // 是否为游戏结束检查状态
this.isGameOver = false;
return false;
} else {
this.scoreValue += topCell.value;
this.shouldCreateRandomValue = true;
topCell.value *= 2;
// 创建动画并执行
this.createAnim(cell, topCell);
}
row++; // 修改目标元素
i = row + 1;
cell.value = 0;
viewCell.value = 0;
} else if (cell.value !== 0 && cell.value !== topCell.value) { // 目标元素有值,且当前列有值
row++;
i = row + 1;
} else {
i++;
}
}
}
}
moveDown(check) {
this.shouldCreateRandomValue = false;
for (let j = this.size; j--;) { // 每列
let row = this.size - 1;
let i = row - 1;
while (i >= 0) {
if (row === 0) break; // 目标元素为最后行时终止
const topCell = this.cells[row][j]; // 目标元素
const cell = this.cells[i][j];
const viewCell = this.viewCells[i][j];
if (topCell.value === 0) { // 目标元素为0时将当前列数据赋值给目标元素
topCell.value = cell.value;
if (topCell.value !== 0) {
this.shouldCreateRandomValue = true;
// 创建动画并执行
this.createAnim(cell, topCell);
}
cell.value = 0;
viewCell.value = 0;
i--;
} else if (topCell.value === cell.value) { // 相等时顶个目标元素值 * 2
if (check) {
this.isGameOver = false;
return false;
} else {
this.scoreValue += topCell.value;
this.shouldCreateRandomValue = true;
topCell.value *= 2;
// 创建动画并执行
this.createAnim(cell, topCell);
}
row--; // 修改目标元素
i = row - 1;
cell.value = 0;
viewCell.value = 0;
} else if (cell.value !== 0 && cell.value !== topCell.value) { // 目标元素有值,且当前列有值
row--;
i = row - 1;
} else {
i--;
}
}
}
}
moveLeft(check) {
this.shouldCreateRandomValue = false;
for (let i = 0; i < this.size; i++) { // 每行
let row = 0;
let j = row + 1;
while (j < this.size) {
if (row === this.size - 1) break; // 目标元素为倒数第二行时终止
const topCell = this.cells[i][row]; // 目标元素
const cell = this.cells[i][j];
const viewCell = this.viewCells[i][j];
if (topCell.value === 0) { // 目标元素为0时将当前列数据赋值给目标元素
topCell.value = cell.value;
if (topCell.value !== 0) {
this.shouldCreateRandomValue = true;
// 创建动画并执行
this.createAnim(cell, topCell);
}
cell.value = 0;
viewCell.value = 0;
j++;
} else if (topCell.value === cell.value) { // 相等时顶个目标元素值 * 2
if (check) {
this.isGameOver = false;
return false;
} else {
this.scoreValue += topCell.value;
this.shouldCreateRandomValue = true;
topCell.value *= 2;
// 创建动画并执行
this.createAnim(cell, topCell);
}
row++; // 修改目标元素
j = row + 1;
cell.value = 0;
viewCell.value = 0;
} else if (cell.value !== 0 && cell.value !== topCell.value) { // 目标元素有值,且当前列有值
row++;
j = row + 1;
} else {
j++;
}
}
}
}
moveRight(check) {
this.shouldCreateRandomValue = false;
for (let i = this.size; i--;) { // 每列
let row = this.size - 1;
let j = row - 1;
while (j >= 0) {
if (row === 0) break; // 目标元素为最后行时终止
const topCell = this.cells[i][row]; // 目标元素
const cell = this.cells[i][j];
const viewCell = this.viewCells[i][j];
if (topCell.value === 0) { // 目标元素为0时将当前列数据赋值给目标元素
topCell.value += cell.value;
if (topCell.value !== 0) {
this.shouldCreateRandomValue = true;
// 创建动画并执行
this.createAnim(cell, topCell);
}
cell.value = 0;
viewCell.value = 0;
j--;
} else if (topCell.value === cell.value) { // 相等时顶个目标元素值 * 2
if (check) {
this.isGameOver = false;
return false;
} else {
this.scoreValue += topCell.value;
this.shouldCreateRandomValue = true;
topCell.value *= 2;
// 创建动画并执行
this.createAnim(cell, topCell);
}
row--; // 修改目标元素
j = row - 1;
cell.value = 0;
viewCell.value = 0;
} else if (cell.value !== 0 && cell.value !== topCell.value) { // 目标元素有值,且当前列有值
row--;
j = row - 1;
} else {
j--;
}
}
}
}
reset() {
this.cells = [[]];
this.viewCells = [[]];
// 可能有动画遗留问题 --- 暂不想解决
this.initCanvas();
this.initData();
this.gameStatus = 0;
this.scoreValue = 0;
}
start() {
if (this.gameStatus === 0) {
this.gameStatus = 1;
this.shouldCreateRandomValue = true;
this.randomEmptyCellValue();
}
}
updateData() {
if (!this.animSum && this.isChange) {
for (let i = 0; i < this.size; i++) {
for (let j = 0; j < this.size; j++) {
this.viewCells[i][j].value = this.cells[i][j].value;
}
}
this.isChange = false;
}
}
ani = () => {
this.clear();
this.updateData();
this.draw();
requestAnimationFrame(this.ani);
}
}
const game2048 = new Game2048(document.getElementById('main'));
</script>
</body>
</html>