资讯专栏INFORMATION COLUMN

js模块化例子

Hanks10100 / 2050人阅读

摘要:另外一个与变量函数有关的约定是变量名函数名前加下横杠,表示这是私有变量函数,表达为没有赢也没有平局刚刚着棋的玩家赢了棋盘满了,且平局刚刚着棋的玩家,走棋无效,再走一次把球放到指定列。

最近在看一本书,里面提到js的模块化,觉得很有必要,所以记录下来
Game.js

/**
 * This is the main class that handles the game life cycle. It initializes
 * other components like Board and BoardModel, listens to the DOM events and
 * translates clicks to coordinates.
 * @param canvas the canvas object to use for drawing
 */
function Game(canvas) {
    this._boardRect = null;
    this._canvas = canvas;
    this._ctx = canvas.getContext("2d");
    this._boardModel = new BoardModel();//实例化类

    this._boardRenderer = new boardRenderer(this._ctx, this._boardModel);
    this.handleResize();
}

_h = Game.prototype;

/**
 * Handles the click (or tap) on the Canvas. Translates the canvas coordinates
 * into the column of the game board and makes the next turn in that column
 * @param x the x coordinate of the click or tap
 * @param y the y coordinate of the click or tap
 */
_h.handleClick = function(x, y) {
    // 获取列的索引
    var column = Math.floor((x - this._boardRect.x)/this._boardRect.cellSize);

    //生成回合并检查结果
    var turn = this._boardModel.makeTurn(column);

    // 如果回合有效,更新游戏盘并绘制新球
    if (turn.status != BoardModel.ILLEGAL_TURN) {
        this._boardRenderer.drawToken(turn.x, turn.y);
    }

    // Do we have a winner after the last turn?
    if (turn.status == BoardModel.WIN) {
        // Tell the world about it and reset the board for the next game
        alert((turn.piece == BoardModel.RED ? "red" : "green") + " won the match!");
        this._reset();
    }

    // If we have the draw, do the same
    if (turn.status == BoardModel.DRAW) {
        alert("It is a draw!");
        this._reset();
    }
};

/**
 * Reset the _boardModel and redraw the board.
 */
_h._reset = function() {
    this._clearCanvas();
    this._boardModel.reset();
    this._boardRenderer.repaint();
};

/**
 * Called when the screen has resized. In this case we need to calculate
 * new size and position for the game board and repaint it.
 */
_h.handleResize = function() {
    this._clearCanvas();
    this._boardRect = this._getBoardRect();//拿到画棋盘的3个重要参数
    this._boardRenderer.setSize(this._boardRect.x, this._boardRect.y, this._boardRect.cellSize);
    this._boardRenderer.repaint();
};

/**
 * Get the optimal position and the size of the board
 * @return the object that describes the optimal position and
 * size for the board:
 * {
 *      x: the x of the top-left corner of the board
 *      y: the y of the top-left corner of the board
 *      cellSize: the optimal size of the cell (in pixels)
 * }
 */
_h._getBoardRect = function() {
    var cols = this._boardModel.getCols();//这个游戏的列数
    var rows = this._boardModel.getRows();//这个游戏的行数
    var cellSize = Math.floor(Math.min(this._canvas.width/cols, this._canvas.height/rows));//每个单元格的长

    var boardWidth = cellSize*cols;
    var boardHeight = cellSize*rows;

    return {
        x: Math.floor((this._canvas.width - boardWidth)/2),
        y: Math.floor((this._canvas.height - boardHeight)/2),
        cellSize: cellSize
    }
};

/**
 * 清除画布的方法居然是直接在画布上面画一个白色的矩形!不需要使用clearRect()...
 * 但是如果背景是一张图片,直接就可以用这个方法
 */
_h._clearCanvas = function() {
    this._ctx.fillStyle = "white";
    this._ctx.fillRect(0, 0, this._canvas.width, this._canvas.height);
};

boardRenderer.js

/**
 * 这个类负责绘制,棋盘,球
 * @param context the 2d context to draw at
 * @param model the BoardModel to take data from
 */
function boardRenderer(context,model){
    this._ctx = context;
    this._model = model;

    //为方便保存
    this._cols = model.getCols();
    this._rows = model.getRows();

    //游戏盘左上角的位置
    this._x = 0;
    this._y = 0;

    //游戏盘矩形的宽度和高度
    this.width = 0;
    this.height = 0;

    //游戏盘单元格的最佳大小
    this._cellSize = 0;
}

_h = boardRenderer.prototype;

/**
 * 重新绘制整个画板Repaints the whole board.
 */
_h.repaint = function() {
    this._ctx.save();
    this._ctx.translate(this._x, this._y);
    this._drawBackground();
    this._drawGrid();
    this._ctx.restore();

    for (var i = 0; i < this._cols; i++) {
        for (var j = 0; j < this._rows; j++) {
            this.drawToken(i, j);
        }
    }
};

/**
 * 画背景
 *
 */
_h._drawBackground = function(){
    var ctx = this._ctx;

    //背景
    var gradient = ctx.createLinearGradient(0,0,0,this._height);
    gradient.addColorStop(0,"#fffbb3");
    gradient.addColorStop(1,"#f6f6b2");
    ctx.fillStyle = gradient;
    ctx.fillRect(0,0,this._width,this._height);

    //绘制曲线
    var co = this.width/6;
    ctx.strokeStyle = "#dad7ac";
    ctx.fillStyle = "#f6f6b2";

    //第一条曲线
    ctx.beginPath();
    ctx.moveTo(co, this._height);
    ctx.bezierCurveTo(this._width + co*3, -co, -co*3, -co, this._width - co, this._height);
    ctx.fill();

    //第二条曲线
    ctx.beginPath();
    ctx.moveTo(co, 0);
    ctx.bezierCurveTo(this._width + co*3, this._height + co, -co*3, this._height + co, this._width - co, 0);
    ctx.fill();
}

/**
 * 画网格.
 */
_h._drawGrid = function() {
    var ctx = this._ctx;
    ctx.beginPath();
    //画横线 Drawing horizontal lines
    for (var i = 0; i <= this._cols; i++) {
        ctx.moveTo(i*this._cellSize + 0.5, 0.5);
        ctx.lineTo(i*this._cellSize + 0.5, this._height + 0.5)
    }

    //画竖线 Drawing vertical lines
    for (var j = 0; j <= this._rows; j++) {
        ctx.moveTo(0.5, j*this._cellSize + 0.5);
        ctx.lineTo(this._width + 0.5, j*this._cellSize + 0.5);
    }

    //给这些线描边
    ctx.strokeStyle = "#CCC";
    ctx.stroke();
};

/**
 * 在指定的地方画上指定颜色的标记
 * @param cellX 单元格的x坐标
 * @param cellY 单元格的y坐标
 */
_h.drawToken = function(cellX, cellY) {
    var ctx = this._ctx;
    var cellSize = this._cellSize;
    var tokenType = this._model.getPiece(cellX, cellY);

    //如果单元格为空
    if (!tokenType)
        return;


    var colorCode = "black";
    switch(tokenType) {
        case BoardModel.RED:
            colorCode = "red";
        break;
        case BoardModel.GREEN:
            colorCode = "green";
        break;
    }

    //标记的圆心位置
    var x = this._x + (cellX + 0.5)*cellSize;
    var y = this._y + (cellY + 0.5)*cellSize;
    ctx.save();
    ctx.translate(x, y);

    //标记的半径
    var radius = cellSize*0.4;

    //渐变的中心
    var gradientX = cellSize*0.1;
    var gradientY = -cellSize*0.1;

    var gradient = ctx.createRadialGradient(
        gradientX, gradientY, cellSize*0.1, // 内圆 (炫光)
        gradientX, gradientY, radius*1.2); // 外圆

    gradient.addColorStop(0, "yellow"); // “光线”的颜色
    gradient.addColorStop(1, colorCode); // 标记的颜色
    ctx.fillStyle = gradient;

    ctx.beginPath();
    ctx.arc(0, 0, radius, 0, 2*Math.PI, true);
    ctx.fill();
    ctx.restore();
};

/**
 * Sets the new position and size for the board. Should call repaint to
 * see the changes
 * @param x the x coordinate of the top-left corner
 * @param y the y coordinate of the top-left corner
 * @param cellSize optimal size of the cell in pixels
 */
_h.setSize = function(x, y, cellSize)  {
    this._x = x;
    this._y = y;
    this._cellSize = cellSize;
    this._width = this._cellSize*this._cols;
    this._height = this._cellSize*this._rows;
};

boardModel.js

/**
 * 这个类是负责保存/验证/返回当前游戏的状态
 * 如当前的玩家是谁、每个单元格放的是什么球、
 * 是不是谁赢了
 * @param cols number of columns in the board
 * @param rows number of rows in the board
 */
function BoardModel(cols, rows) {
    this._cols = cols || 7;
    this._rows = rows || 6;
    this._data = [];//用于记录游戏当前的游戏状态——每个格子有什么球
    this._currentPlayer = BoardModel.RED;
    this._totalTokens = 0;

    this.reset();
}

/**
 * 0代表单元格为空,1代表单元格有红色球,2代表单元格有绿色球
 * 因为怕以后忘记这些数字代表什么,干脆把数字存到常量里,代码看起来易懂,
 * 但是这么多字,前端的js不是应该越短越好吗!?

 * ps.变量名全大写表示这是常量,这是一个js程序员之间的约定,表达为 CAPITAL_CASED。
 * 另外一个与变量(函数)有关的约定是:变量名(函数名)前加"_"下横杠,表示这是私有变量(函数),表达为 _underlinePrivateVariables
 */
BoardModel.EMPTY = 0;
BoardModel.RED = 1;
BoardModel.GREEN = 2;

/**
 * Game state after the turn
 */
BoardModel.NONE = 0; // 没有赢也没有平局
BoardModel.WIN = 1; //刚刚着棋的玩家赢了
BoardModel.DRAW = 2; // 棋盘满了,且平局
BoardModel.ILLEGAL_TURN = 3; // 刚刚着棋的玩家,走棋无效,再走一次

_h = BoardModel.prototype;

/**
 * Resets the game board into the initial state: the
 * board is empty and the starting player is RED.
 */
_h.reset = function() {
    this._data = [];
    for (var i = 0; i < this._rows; i++) {
        this._data[i] = [];
        for (var j = 0; j < this._cols; j++) {
            this._data[i][j] = BoardModel.EMPTY;
        }
    }

    this._currentPlayer = BoardModel.RED;
    this._totalTokens = 0;
};

/**
 * 把球放到指定列。 Board model itself takes care of
 * tracking the current player.
 * @param column the index of the column
 * @param piece the ID of the piece (RED or YELLOW)
 * @return the object {
 *      status: win condition
 *      x: the x coordinate of the new turn (undefined if turn was illegal)
 *      y: the y coordinate of the new turn (undefined if turn was illegal)
 *      piece: piece id (RED or GREEN)
 * }
 */
_h.makeTurn = function(column) {

    //正在放的球的颜色 The color of the piece that we"re dropping
    var piece = this._currentPlayer;

    //检查这一列是否可以放球
    if (column < 0 || column >= this._cols) {
        return {
            status: BoardModel.ILLEGAL_TURN
        }
    }

    //检查这一列上有空行没,如果没有,则这回合无效
    var row = this._getEmptyRow(column);
    if (row == -1) {
        return {
            status: BoardModel.ILLEGAL_TURN
        }
    }

    //放置球
    this._totalTokens++;
    this._data[row][column] = piece;

    // 更换玩家
    this._toggleCurrentPlayer();

    // 返回回合成功的消息,包括新的游戏状态:none——可以继续游戏、win、draw
    return {
        status: this._getGameState(column, row),
        x: column,
        y: row,
        piece: piece
    }
};

_h.getPiece = function(col, row) {
    return this._data[row][col];
};

/**
 * 返回这个游戏有多少列
 */
_h.getCols = function() {
    return this._cols;
};

/**
 * 返回这个游戏有多少行
 */
_h.getRows = function() {
    return this._rows;
};

/**
 * 返回指定列是否有空行,如果没有 return -1.
 * @param column the column index
 */
_h._getEmptyRow = function(column) {
    for (var i = this._rows - 1; i >= 0; i--) {
        if (!this.getPiece(column, i)) {
            return i;
        }
    }
    return -1;
};


/**
 * Checks for the win condition, checks how many pieces of the same color are in each
 * possible row: horizontal, vertical or both diagonals.
 * @param column the column of the last move
 * @param row the row of the last move
 * @return the game state after this turn:
 *  NONE if the state wasn"t affected
 *  WIN if current player won the game with the last turn
 *  DRAW if there"s no emty cells in the board left
 */
_h._getGameState = function(column, row) {
    if (this._totalTokens == this._cols*this._rows)
        return BoardModel.DRAW;

    for (var deltaX = -1; deltaX < 2; deltaX++) {
        for (var deltaY = -1; deltaY < 2; deltaY++) {
            if (deltaX == 0 && deltaY == 0)
                continue;
            var count = this._checkWinDirection(column, row, deltaX, deltaY)
                    + this._checkWinDirection(column, row, -deltaX, -deltaY) + 1;
            if (count >= 4) {
                return BoardModel.WIN;
            }
        }
    }
    return BoardModel.NONE;
};

/**
 * Calculates the number of pieces of the same color in the given direction, starting
 * fromt the given point (last turn)
 * @param column starting column
 * @param row starting row
 * @param deltaX the x direction of the check
 * @param deltaY the y direction of the check
 */
_h._checkWinDirection = function(column, row, deltaX, deltaY) {
    var pieceColor = this.getPiece(column, row);
    var tokenCounter = 0;
    var c = column + deltaX;
    var r = row + deltaY;
    while(c >= 0 && r >= 0 && c < this._cols && r < this._rows &&
            this.getPiece(c, r) == pieceColor) {
        c += deltaX;
        r += deltaY;
        tokenCounter++;
    }
    return tokenCounter;
};

/**
 * 切换当前玩家 - from red to green and back.
 */
_h._toggleCurrentPlayer = function() {
    this._currentPlayer = (this._currentPlayer==BoardModel.RED)?BoardModel.GREEN:BoardModel.RED;
    /*if (this._currentPlayer == BoardModel.RED)
        this._currentPlayer = BoardModel.GREEN;
    else
        this._currentPlayer = BoardModel.RED;*/
};

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/87677.html

相关文章

  • JavaScript块化发展

    摘要:所有依赖这个模块的语句,都定义在一个回调函数中,等到所有依赖加载完成之后前置依赖,这个回调函数才会运行。如果将前面的代码改写成形式,就是下面这样定义了一个文件,该文件依赖模块,当模块加载完毕之后执行回调函数,这里并没有暴露任何变量。 模块化是我们日常开发都要用到的基本技能,使用简单且方便,但是很少人能说出来但是的原因及发展过程。现在通过对比不同时期的js的发展,将JavaScript模...

    mengbo 评论0 收藏0
  • JavaScript块化开发的演进历程

    摘要:参考精读模块化发展模块化七日谈前端模块化开发那点历史本文先发布于我的个人博客模块化开发的演进历程,后续如有更新,可以查看原文。 Brendan Eich用了10天就创造了JavaScript,因为当时的需求定位,导致了在设计之初,在语言层就不包含很多高级语言的特性,其中就包括模块这个特性,但是经过了这么多年的发展,如今对JavaScript的需求已经远远超出了Brendan Eich的...

    anonymoussf 评论0 收藏0
  • Require.js实现js块化管理教程

    摘要:再者,引入一大堆的文件也不美观,而使用即可实现的模块化异步加载。通过定义模块的方式可分为以下两类。当和这两个模块加载完成之后将会执行回调函数。插件可以使回调函数在结构加载完成之后再执行。最好的方式是使用字符串但这很难管理尤其实在多行的时候。 什么是Require.js Require.js是一个AMD规范的轻量级js模块化管理框架,最新版本require.js 2.1.11压缩后只有1...

    fox_soyoung 评论0 收藏0
  • webpack中imports-loader,exports-loader,expose-loade

    摘要:有几个和模块化相关的,,比较容易混淆。注意,我们并没有引入。所以运行的结果是。同时,我们指定了变量。等于是在文件的最顶上,加上了。实际情况大致如此。把一个模块导出并付给一个全局变量。假如中有代码,那么后的值就为我们这里只讨论浏览器环境。 Webpack有几个和模块化相关的loader,imports-loader,exports-loader,expose-loader,比较容易混淆。...

    why_rookie 评论0 收藏0
  • ES6 系列之模块加载方案

    摘要:感谢感谢和在推动模块化发展方面做出的贡献。与引用阮一峰老师的标准参考教程规范加载模块是同步的,也就是说,只有加载完成,才能执行后面的操作。规定了新的模块加载方案。与引用阮一峰老师的入门它们有两个重大差异。 前言 本篇我们重点介绍以下四种模块加载规范: AMD CMD CommonJS ES6 模块 最后再延伸讲下 Babel 的编译和 webpack 的打包原理。 require....

    pinecone 评论0 收藏0
  • 使用CommonJS,AMD以及CMD编写块化JavaScripts

    摘要:模块化编程首先,我想说说模块化编程这个概念当我不清楚这个概念的时候,其实说什么模块化编程多好多好都是懵逼的而我一直不觉得有多好,其实也是因为我从开始写,就一直都在模块化编程啊我们写一个文件然后我们在文件中引入然后调用方法哈哈这样已经是模块化 模块化编程 首先,我想说说模块化编程这个概念当我不清楚这个概念的时候,其实说什么模块化编程多好多好都是懵逼的而我一直不觉得有多好,其实也是因为我从...

    nifhlheimr 评论0 收藏0

发表评论

0条评论

Hanks10100

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<