13 Star 65 Fork 146

OpenHarmony-SIG / knowledge_demo_temp

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
code_explain.md 10.28 KB
一键复制 编辑 原始数据 按行查看 历史

JS拼图游戏代码解读

1、新建工程

在DevEco Studio中点击File -> New Project -> [Standard]Empty Ability -> Next,Project type 选择Application,Language选择JS语言,最后点击Finish完成项目创建。

2、编写页面

2.1、游戏首页

拼图游戏首页

左边是游戏标题和游戏规则,右边是两种游戏模式入口按钮。

<div class="container" onswipe="touchMove">
    <div class="textleft">
        <text class="title">{{ title }}</text>
        <text class="rule">{{ rule }}</text>
    </div>
    <div class="btnright">
        <input class="btn" type="button" value="4 × 4" onclick="playgame(4)"></input>
        <input class="btn" type="button" value="5 × 5" onclick="playgame(5)"></input>
    </div>
</div>

2.2、游戏页面

游戏初始页面

效果图如上,可以分为三部分

2.2.1、 顶部计时器

用文本组件显示变量currentSeconds。

<text class="seconds">当前秒数:{{currentSeconds}} s</text>
2.2.2、 中部左边是参照用的原图,右边是打乱后的拼图及隐藏的弹窗。

分别用两个canvas组件显示原图和 n×n 的拼图。其中拼图部分使用stack布局,用于完成游戏时显示弹窗。

    <div>
        <stack class="stack">
            <canvas class="canvas" ref="canvas0" onswipe="swipeGrids"></canvas>
        </stack>
        <stack class="stack">
            <canvas class="canvas" ref="canvas" onswipe="swipeGrids"></canvas>
            <div class="subcontainer" show="{{isShow}}">
                <text class="gameover">
                    游戏成功
                </text>
            </div>
        </stack>
    </div>
  • 参照图绘制
    refer() {
        ctx0 = this.$refs.canvas0.getContext('2d');
        var init = new Image();
        init.src = this.img.src
        init.onload = function () {
            console.log('Image load success');
            ctx0.drawImage(init, 0, 0, 360, 360, 0, 0, 240, 240);
        };
    },
  • 每块拼图网格绘制
    drawGrids() {
        context = this.$refs.canvas.getContext('2d');
        for (let row = 0; row < this.block; row++) {
            for (let column = 0; column < this.block; column++) {
                let gridStr = grids[row][column].toString();
                context.fillStyle = "#BBADA0";
                let leftTopX = column * (MARGIN + SIDELEN) + MARGIN;
                let leftTopY = row * (MARGIN + SIDELEN) + MARGIN;
                context.fillRect(leftTopX, leftTopY, SIDELEN, SIDELEN);
                context.font = "28px HYQiHei-65S";
                if (gridStr != "0") {
                    context.fillStyle = "#000000";
                    let offsetX = (3 - gridStr.length) * (SIDELEN / 8);
                    let offsetY = (SIDELEN - 14);
                    context.drawImage(this.img,
                        (CUT_SID + MARGIN) * ((grids[row][column] - 1) % this.block) + MARGIN, //原图img的X轴裁剪起点
                        (CUT_SID + MARGIN) * (Math.floor((grids[row][column] - 1) / this.block)) + MARGIN, //原图img的Y轴裁剪起点
                        CUT_SID, CUT_SID, //原图X轴,Y轴方向的裁剪长度
                        leftTopX, leftTopY, //画布X轴,Y轴画图的起点
                        SIDELEN, SIDELEN); //画布X轴,Y轴画图的长度
                    if(true == this.tip) {
                        context.fillText(gridStr, leftTopX + offsetX, leftTopY + offsetY);
                    }
                    else {
                        context.fillText("", leftTopX + offsetX, leftTopY + offsetY);
                    }
                }
                else {
                    if(true == this.isShow) {
                        context.drawImage(this.img,
                            (CUT_SID + MARGIN) * ((Math.pow(this.block, 2) - 1) % this.block) + MARGIN, //原图img的X轴裁剪起点
                            (CUT_SID + MARGIN) * (Math.floor((Math.pow(this.block, 2) - 1) / this.block)) + MARGIN, //原图img的Y轴裁剪起点
                            CUT_SID, CUT_SID, //原图X轴,Y轴方向的裁剪长度
                            leftTopX, leftTopY, //画布X轴,Y轴画图的起点
                            SIDELEN, SIDELEN); //画布X轴,Y轴画图的长度
                    }
                    else {
                        context.drawImage(this.img, 0, 0, 0, 0, 0, 0, SIDELEN, SIDELEN);
                    }
                }
            }
        }
    },
2.2.3、 底部是几个功能按钮

4个input组件横向排列,分别绑定对应的功能方法。

    <div>
        <input type="button" value="返回首页" class="btn" onclick="quit"></input>
        <input type="button" value="换张图" class="btn" onclick="changeimage"></input>
        <input type="button" value="重新开始" class="btn" onclick="restartGame"></input>
        <input type="button" value="提示" class="btn" onclick="gethelp"></input>
    </div>

3、编写游戏逻辑

3.1、首页游戏模式传值跳转

选择游戏模式4×4或5×5。

    playgame(num) {
        router.replace({
            uri: "pages/jigsaw/jigsaw",
            params: {
                block: num,
            },
        })
    }
3.2、游戏页面拼图网格绘制

根据页面路由的传值,计算拼图网格的大小及间隙,再进行绘制。

    onInit() {
        CUT_SID = 360 / this.block - (this.block + 1);
        SIDELEN = 240 / this.block - (this.block + 1);
        MARGIN = this.block;
        this.img.src = images[this.index % images.length].src;
    },

    onShow() {
        if(4 == this.block) {
            grids=[[1, 2, 3, 4],
            [5, 6, 7, 8],
            [9, 10, 11, 12],
            [13, 14, 15, 0]];
        }
        else {
            grids=[[1, 2, 3, 4, 5],
            [6, 7, 8, 9, 10],
            [11, 12, 13, 14, 15],
            [16, 17, 18, 19, 20],
            [21, 22, 23, 24, 0]];
        }
        timer = null;
        this.initGrids();
        this.drawGrids();
        timer = setInterval(this.run, 1000);
        this.refer();       //参照画布,可替换
    },
3.3、随机打乱拼图

进行一千次上、下、左、右随机滑动,打乱拼图。

    initGrids() {
        let array=["left","up","right","down"];
        for (let i = 0; i < 1000; i++){
            let randomIndex = Math.floor(Math.random() * this.block);
            let direction = array[randomIndex];
            this.changeGrids(direction);
        }
    },
3.4、监听滑动事件
    swipeGrids(event) {
        this.changeGrids(event.direction);
        if(this.gameover()){
            clearInterval(timer);
            this.isShow = true;
            this.tip = false;
        }
        this.drawGrids();
    },

    changeGrids(direction) {
        let x;
        let y;
        for (let row = 0; row < this.block; row++) {
            for (let column = 0; column < this.block; column++) {
                if (grids[row][column] == 0) {
                    x = row;
                    y = column;
                    break;
                }
            }
        }
        let temp;
        if(this.isShow==false){
            if (direction == 'left' && (y + 1) < this.block) {
                temp = grids[x][y + 1];
                grids[x][y + 1] = grids[x][y];
                grids[x][y] = temp;
            } else if (direction == 'right' && (y - 1) > -1) {
                temp = grids[x][y - 1];
                grids[x][y - 1] = grids[x][y];
                grids[x][y] = temp;
            } else if (direction == 'up' && (x + 1) < this.block) {
                temp = grids[x + 1][y];
                grids[x + 1][y] = grids[x][y];
                grids[x][y] = temp;
            } else if (direction == 'down' && (x - 1) > -1) {
                temp = grids[x - 1][y];
                grids[x - 1][y] = grids[x][y];
                grids[x][y] = temp;
            }
        }
    },
3.5、拼图数字提示

设置提示标识符变量,为true时开启数字提示。

    gethelp() {
        this.tip = !this.tip;
        this.drawGrids();
    },
3.6、拼图完成判断

初始化时给每个拼图网格编号,每次滑动操作结束后进行编号比对。

    gameover(){
        let originalgrids;
        if(4 == this.block) {
            originalgrids=[[1, 2, 3, 4],
            [5, 6, 7, 8],
            [9, 10, 11, 12],
            [13, 14, 15, 0]];
        }
        else {
            originalgrids=[[1, 2, 3, 4, 5],
            [6, 7, 8, 9, 10],
            [11, 12, 13, 14, 15],
            [16, 17, 18, 19, 20],
            [21, 22, 23, 24, 0]];
        }
        for (let row = 0; row < this.block; row++) {
            for (let column = 0; column < this.block; column++) {
                if (grids[row][column] != originalgrids[row][column]){
                    return false;
                }
            }
        }
        return true;
    },
3.7、图片切换

从图片列表中获取图片地址赋值。

    changeimage() {
        this.index += 1;
        this.img.src = images[this.index % images.length].src;
        this.restartGame();
    },

4、效果展示

游戏效果展示

5、项目下载和导入

项目仓库地址: https://gitee.com/openharmony-sig/knowledge_demo_temp/tree/master/FA/Entertainment/Jigsaw

1)git下载

git clone https://gitee.com/openharmony-sig/knowledge_demo_temp.git

2)项目导入

打开DevEco Studio,点击File->Open->下载路径/FA/Entertainment/Jigsaw

6、约束与限制

1. 设备编译约束

2. 应用编译约束

1
https://gitee.com/openharmony-sig/knowledge_demo_temp.git
git@gitee.com:openharmony-sig/knowledge_demo_temp.git
openharmony-sig
knowledge_demo_temp
knowledge_demo_temp
master

搜索帮助

53164aa7 5694891 3bd8fe86 5694891