123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460 |
- import {STextAlign} from "@persagy-web/draw/lib";
- <template>
- <div>
- <canvas id="canvas" width="410" height="410" style="background: #bbada0;border-radius: 10px" tabindex="0"></canvas>
- <el-button @click="reset" type="primary">重新开始</el-button>
- </div>
- </template>
- <script lang="ts">
- import {Component, Vue} from "vue-property-decorator"
- import {SCanvasView, SColor, SPainter, STextAlign, STextBaseLine} from "@persagy-web/draw/lib";
- /**
- * 2048
- *
- * @author 郝洁 <haojie@persagy.com>
- */
- class GameView extends SCanvasView {
- /** 格子宽度 */
- box_width: number = this.width * 0.8 * 0.25;
- margin_width: number = this.width * 0.2 * 0.20;
- /** */
- gridMap: number[][] = [];
- /** */
- score: number = 0;
- /** 游戏结束 */
- isGameOver: boolean = false;
- /** 颜色 */
- color = {
- 0:{
- font: "#ffffffff",
- background: "#eee4da59"
- },
- 2: {
- font: "#776e65",
- background: "#eee4da"
- },
- 4: {
- font: "#776e65",
- background: "#ede0c8"
- },
- 8: {
- font: "#f9f6f2",
- background: "#f2b179"
- },
- 16: {
- font: "#f9f6f2",
- background: "#f59563"
- },
- 32: {
- font: "#f9f6f2",
- background: "#f67c5f"
- },
- 64: {
- font: "#f9f6f2",
- background: "#f65e3b"
- },
- 128: {
- font: "#f9f6f2",
- background: "#edcf72"
- },
- 256: {
- font: "#f9f6f2",
- background: "#edcc61"
- },
- 512: {
- font: "#f9f6f2",
- background: "#edc850"
- },
- 1024: {
- font: "#f9f6f2",
- background: "#edc53f"
- },
- 2048: {
- font: "#f9f6f2",
- background: "#edc22e"
- },
- };
- /**
- * 构造函数
- *
- * @param id canvas DOM id
- */
- constructor(id: string) {
- super(id);
- this.init()
- }
- /**
- * 初始化游戏
- */
- init(): void {
- this.gridMap = [];
- for (let row = 0; row < 4; row++) { // 循环行数
- const m1: number[] = [];
- for (let col = 0; col < 4; col++) { // 循环列数
- m1.push(0);
- }
- this.gridMap.push(m1);
- }
- // 初始化分数
- this.score = 0;
- // 游戏结束标记为 false
- this.isGameOver = false;
- // 生成随机位置的数字
- this.generateOneNumber('left');
- }
- /**
- * 键盘按下事件
- *
- * @param event 事件对象
- */
- onKeyDown(event: KeyboardEvent): void {
- if (this.isGameOver) {
- return;
- }
- let moved, merged;
- // 按键判断
- switch (event.code) {
- case "KeyW": // 上移动作
- case "ArrowUp":
- moved = this.moveToFill('top');
- merged = this.mergeGrid('top');
- (moved || merged) && this.generateOneNumber('top');
- break;
- case "KeyA": // 左移动作
- case "ArrowLeft":
- moved = this.moveToFill('left');
- merged = this.mergeGrid('left');
- (moved || merged) && this.generateOneNumber('left');
- // this.update()
- break;
- case "KeyD": // 右移动作
- case "ArrowRight":
- // 判断游戏是否继续,左部产生随机位置随机数
- moved = this.moveToFill('right');
- merged = this.mergeGrid('right');
- (moved || merged) && this.generateOneNumber('right');
- break;
- // 按键 下
- case "KeyS": // 下移动作
- case "ArrowDown":
- // 判断游戏是否继续,上部产生随机位置随机数
- moved = this.moveToFill('bottom');
- merged = this.mergeGrid('bottom');
- (moved || merged) && this.generateOneNumber('bottom');
- break;
- default :
- // do nothing
- }
- this.isGameOver = this.checkIsOver();
- }
- /**
- * 生成随机的格子
- */
- generateOneNumber(direction: string): void {
- let temp = [];
- if (direction == "left") {
- for (let i = 0; i < 4; i++) {
- temp.push(this.gridMap[i][3])
- }
- } else if (direction == "top") {
- temp = this.gridMap[3];
- } else if (direction == "bottom") {
- temp = this.gridMap[0];
- } else {
- // (direction == "right")
- for (let i = 0; i < 4; i++) {
- temp.push(this.gridMap[i][0])
- }
- }
- let indexArr = [];
- for(let i = 0; i < temp.length; i++) {
- if(temp[i] == 0) {
- indexArr.push(i)
- }
- }
- const len = indexArr.length;
- if (len) {
- const random = Math.floor(len * Math.random());
- const index = indexArr[random];
- if (direction == "left") {
- this.gridMap[index][3] = 2
- } else if (direction == "top") {
- this.gridMap[3][index] = 2;
- } else if (direction == "bottom") {
- this.gridMap[0][index] = 2;
- } else {
- // (direction == "right")
- this.gridMap[index][0] = 2
- }
- this.update()
- } else {
- // this.isGameOver = true;
- this.update();
- }
- //随机一个位置,取新方块出现的位置
- //随机一个数字 2 或 4 ,在随机位置显示随机数字
- }
- /**
- * 移动数字,使移动方向的数字填满
- */
- moveToFill(direction: string): boolean{
- let flag = false;
- if (direction == "left") {
- for (let i = 0; i < 4; i++) {
- const tempJrr = new Array(4).fill(0);
- let index = 0;
- for (let j = 0; j < 4; j++) {
- if (this.gridMap[i][j]!=0) {
- tempJrr[index] = this.gridMap[i][j];
- index++;
- } else {
- flag = true;
- }
- }
- this.gridMap[i] = tempJrr;
- }
- } else if (direction == "top") {
- for (let j = 0; j < 4; j++) {
- const tempJrr = new Array(4).fill(0);
- let index = 0;
- for (let i = 0; i < 4; i++) {
- if (this.gridMap[i][j]!=0) {
- tempJrr[index] = this.gridMap[i][j];
- index++;
- } else {
- flag = true;
- }
- }
- for(let u = 0; u < 4; u++) {
- this.gridMap[u][j] = tempJrr[u];
- }
- }
- } else if (direction == "bottom") {
- for (let j = 0; j < 4; j++) {
- const tempJrr = new Array(4).fill(0);
- let index = 3;
- for (let i = 3; i >= 0; i--) {
- if (this.gridMap[i][j]!=0) {
- tempJrr[index] = this.gridMap[i][j];
- index--;
- } else {
- flag = true;
- }
- }
- for(let u = 3; u >= 0; u--) {
- this.gridMap[u][j] = tempJrr[u];
- }
- }
- } else {
- for (let i = 0; i < 4; i++) {
- const tempJrr = new Array(4).fill(0);
- let index = 3;
- for (let j = 3; j >= 0; j--) {
- if (this.gridMap[i][j]!=0) {
- tempJrr[index] = this.gridMap[i][j];
- index--;
- } else {
- flag = true;
- }
- }
- this.gridMap[i] = tempJrr;
- }
- }
- return flag;
- }
- /**
- * 合并数字
- *
- * @param direction 方向
- */
- mergeGrid(direction: string): boolean{
- let flag = false;
- if (direction == "left") {
- for (let i = 0; i < 4; i++) {
- for (let j = 0; j < 3; j++) {
- if (this.gridMap[i][j] == this.gridMap[i][j + 1]) {
- this.gridMap[i][j] = 2*this.gridMap[i][j];
- this.gridMap[i][j + 1] = 0;
- flag = true;
- }
- }
- }
- } else if (direction == "top") {
- for (let i = 0; i < 3; i++) {
- for (let j = 0; j < 4; j++) {
- if (this.gridMap[i][j] == this.gridMap[i + 1][j]) {
- this.gridMap[i][j] = 2*this.gridMap[i][j];
- this.gridMap[i + 1][j] = 0;
- flag = true;
- }
- }
- }
- } else if (direction == "bottom") {
- for (let i = 3; i >= 1; i--) {
- for (let j = 0; j < 4; j++) {
- if (this.gridMap[i][j] == this.gridMap[i - 1][j]) {
- this.gridMap[i][j] = 2*this.gridMap[i][j];
- this.gridMap[i - 1][j] = 0;
- flag = true;
- }
- }
- }
- } else {
- // (direction == "right")
- for (let i = 0; i < 4; i++) {
- for (let j = 3; j >= 1; j--) {
- if (this.gridMap[i][j] == this.gridMap[i][j - 1]) {
- this.gridMap[i][j] = 2*this.gridMap[i][j];
- this.gridMap[i][j - 1] = 0;
- flag = true;
- }
- }
- }
- }
- if (flag) {
- this.moveToFill(direction)
- }
- return flag;
- }
- /**
- * 绘制游戏画面
- *
- * @param painter 绘制对象
- */
- onDraw(painter: SPainter): void {
- painter.clearRect(0, 0, this.width, this.height);
- painter.pen.color = SColor.Transparent;
- this.drawMap(painter);
- this.drawNum(painter);
- // 如果游戏结束,绘制文字
- if (this.isGameOver) {
- painter.brush.color = SColor.Blue;
- painter.font.size = 20;
- painter.font.textAlign = STextAlign.Center;
- painter.drawText("GAME OVER", 180, 30);
- // return;
- }
- }
- /**
- * 绘制数字
- */
- drawNum(painter: SPainter): void {
- // 遍历行
- for (let row = 0; row < 4; row++) {
- // 遍历列
- for (let col = 0; col < 4; col++) {
- this.drawCellNum(painter, row, col, this.gridMap[row][col]);
- }
- }
- }
- /**
- * 绘制格子中的数字
- */
- drawCellNum(painter: SPainter, row: number, col: number, type: number): void {
- const x = col * 100 + 10;
- const y = row * 100 + 10;
- if (type != 0) {
- painter.font.textAlign = STextAlign.Center;
- painter.font.size = 40;
- // @ts-ignore
- painter.font.textBaseLine = STextBaseLine.Middle;
- painter.brush.color = new SColor(this.color[type].font);
- painter.drawText(type+'', x+45, y+45);
- }
- }
- /**
- * 绘制背景图
- */
- drawMap(painter: SPainter) {
- // 遍历行
- for (let row = 0; row < 4; row++) {
- // 遍历列
- for (let col = 0; col < 4; col++) {
- this.drawCell(painter, row, col, this.gridMap[row][col]);
- }
- }
- }
- /**
- * 绘制具体格子
- *
- * @param painter 绘制对象
- * @param row 行
- * @param col 列
- * @param type 图类型
- */
- private drawCell(painter: SPainter, row: number, col: number, type: number): void {
- const x = col * 100 + 10;
- const y = row * 100 + 10;
- painter.brush.color = new SColor(this.color[type].background);
- painter.drawRoundRect(x, y, 90, 90, 10);
- }
- /**
- * 检测结束
- */
- checkIsOver():boolean{
- for(let i = 0; i < 4; i++) {
- if (this.gridMap[i].indexOf(0) > -1) {
- return false;
- }
- for(let j = 0; j < 3; j++) {
- if (this.gridMap[i][j] == this.gridMap[i][j+1]) {
- return false
- }
- if (i < 3 && this.gridMap[i][j] == this.gridMap[i+1][j]){
- return false
- }
- }
- }
- return this.gridMap[3][3] != this.gridMap[2][3];
- }
- }
- @Component
- export default class Gcanvas extends Vue {
- /** 实例化 */
- view: GameView | null = null;
- /**
- * 页面挂载完成
- */
- mounted(): void {
- this.view = new GameView('canvas');
- document.getElementById('canvas')!!.focus()
- }
- /**
- * 重置操作
- */
- reset(): void {
- this.view!!.init()
- document.getElementById("canvas")!!.focus()
- }
- }
- </script>
- <style scoped>
- </style>
|