简体   繁体   English

在多个画布上绘制的HTML5图像没有显示在其中一个上

[英]HTML5 Drawing on multiple canvases images don't show up on one of them

Working on a sort of proof-of-concept object-oriented javascript project that emulates a chessboard. 研究一种模拟棋盘的概念验证的面向对象的javascript项目。 Currently I've got four canvases set up, each set to two different boards and two "sidebar" canvases which display the current turn and the list of any pieces taken for the associated game. 目前,我已经设置了四个画布,每个画布设置为两个不同的面板和两个“侧栏”画布,这些画布显示了当前的回合以及相关游戏的所有棋子列表。 Here's a screenshot of what it looks like currently: 这是当前情况的屏幕截图:

http://i.imgur.com/GPoVkK2.png http://i.imgur.com/GPoVkK2.png

The problem is, the elements within the second sidebar are for whatever reason drawing in the first sidebar, and I haven't been able to figure out why. 问题是,无论出于何种原因,第二个侧边栏中的元素都是在第一个侧边栏中绘制的,而我一直无法弄清原因。

Here's all the code files for the project broken out and explained to the best of my ability: 以下是该项目的所有代码文件,它们已尽我所能进行解释:

index.html index.html

<!DOCTYPE html>
<html>
<head>
    <script type="text/javascript" src="scripts/Marker.js"></script>
    <script type="text/javascript" src="scripts/TurnMarker.js"></script>
    <script type="text/javascript" src="scripts/GameToken.js"></script>
    <script type="text/javascript" src="scripts/GameBoard.js"></script>
    <script type="text/javascript" src="scripts/Validation.js"></script>
    <script type="text/javascript" src="scripts/Main.js"></script>
</head>
<body onLoad=initialize()>

<canvas id="gameBoard" width="400" height="400" style="border:1px solid #000000; position=relative;">
Your browser doesn't support HTML5 canvas.
</canvas>
<canvas id="sideBar" width="50" height="400" style="border:1px solid #000000; position=relative;">
</canvas>

<canvas id="gameBoardWizard" width="400" height="400" style="border:1px solid #000000; position=relative;">
Your browser doesn't support HTML5 canvas.
</canvas>
<canvas id="sideBarWizard" width="50" height="400" style="border:1px solid #000000; position=relative;">
</canvas>
</body>
</html>

Main.js Main.js

/*** Chess Board Program
* Author: Alex Jensen
* CS 3160: Concepts of Programming Languages
* 12/5/14
*/

var gameBoards = [];

/** Initialize is called when the page loads (set up in the HTML below)
*  
*/
function initialize()
{
    createBoard("gameBoard", "sideBar" ,8 , 8);
    createBoard("gameBoardWizard", "sideBarWizard" ,8, 8);
    // setInterval is a super awesome javascript function and I love it.
    setInterval(function() {draw()}, 100);
}

/** CreateBoard is a helper function for Initialize that is responsible for loading a game board into the list of game boards.
*   boardID= String title of the HTML5 table within the HTML index file.
*/
function createBoard(boardID, sideBarID, numSquaresRows, numSquaresColumns)
{
    var initBoardValidator = [];
    for (var i=0;i<numSquaresColumns;i++) 
    {
        initBoardValidator[i] = [];
        for (var j=0;j<numSquaresRows;j++) 
        {
            initBoardValidator[i][j] = null;
        }
    }

    var gameBoard = new GameBoard(boardID, sideBarID, numSquaresRows, numSquaresColumns, initBoardValidator, [], []);   
    gameBoard.tokens = createDefaultTokens(gameBoard);
    gameBoard.takenTokens = createDefaultTakeMarkers(gameBoard);
    gameBoards[gameBoards.length] = gameBoard;
}

/** Helper function for initialize which creates all the tokens and stores them in appropriate locations.
*
*/
function createDefaultTokens(game)
{
    tokens = [];
    // Create Pawns
    for (var i = 0; i < 8; i++)
    {
        tokens[tokens.length] = new GameToken(game, "WP", 1, 'images/wp.png', i * game.squareWidth, game.squareHeight, pawnValidator);
        tokens[tokens.length] = new GameToken(game, "BP", 2, 'images/bp.png', i * game.squareWidth, game.squareHeight * 6, pawnValidator);
    }
    // Create other pieces
    tokens[tokens.length] = new GameToken(game, "WR", 1, 'images/wr.png', 0, 0, rookValidator);
    tokens[tokens.length] = new GameToken(game, "WN", 1, 'images/wn.png', game.squareWidth, 0, knightValidator);
    tokens[tokens.length] = new GameToken(game, "WB", 1, 'images/wb.png', game.squareWidth * 2, 0, bishopValidator);
    tokens[tokens.length] = new GameToken(game, "WQ", 1, 'images/wq.png', game.squareWidth * 3, 0, queenValidator);
    tokens[tokens.length] = new GameToken(game, "WK", 1, 'images/wk.png', game.squareWidth * 4, 0, kingValidator);
    tokens[tokens.length] = new GameToken(game, "WB", 1, 'images/wb.png', game.squareWidth * 5, 0, bishopValidator);
    tokens[tokens.length] = new GameToken(game, "WN", 1, 'images/wn.png', game.squareWidth * 6, 0, knightValidator);
    tokens[tokens.length] = new GameToken(game, "WR", 1, 'images/wr.png', game.squareWidth * 7, 0, rookValidator);
    tokens[tokens.length] = new GameToken(game, "BR", 2, 'images/br.png', 0, game.squareWidth * 7, rookValidator);
    tokens[tokens.length] = new GameToken(game, "BN", 2, 'images/bn.png', game.squareWidth, game.squareHeight * 7, knightValidator);
    tokens[tokens.length] = new GameToken(game, "BB", 2, 'images/bb.png', game.squareWidth * 2, game.squareHeight * 7, bishopValidator);
    tokens[tokens.length] = new GameToken(game, "BQ", 2, 'images/bq.png', game.squareWidth * 3, game.squareHeight * 7, queenValidator);
    tokens[tokens.length] = new GameToken(game, "BK", 2, 'images/bk.png', game.squareWidth * 4, game.squareHeight * 7, kingValidator);
    tokens[tokens.length] = new GameToken(game, "BB", 2, 'images/bb.png', game.squareWidth * 5, game.squareHeight * 7, bishopValidator);
    tokens[tokens.length] = new GameToken(game, "BN", 2, 'images/bn.png', game.squareWidth * 6, game.squareHeight * 7, knightValidator);
    tokens[tokens.length] = new GameToken(game, "BR", 2, 'images/br.png', game.squareWidth * 7, game.squareHeight * 7, rookValidator);
    return tokens;
}

function createDefaultTakeMarkers(game)
{
    var takenTokens = [];
    // Create Pawns
    for (var i = 0; i < 8; i++)
    {
        takenTokens[takenTokens.length] = new Marker("WP", 1, 'images/wp.png', 5, (i * 20) + 5);
        takenTokens[takenTokens.length] = new Marker("BP", 1, 'images/bp.png', 5, game.sideBar.height - ((i * 20) + 25));
    }

    // Create other pieces
    takenTokens[takenTokens.length] = new Marker("WR", 1, 'images/wr.png', 25, 5);
    takenTokens[takenTokens.length] = new Marker("WN", 1, 'images/wn.png', 25, 25);
    takenTokens[takenTokens.length] = new Marker("WB", 1, 'images/wb.png', 25, 45);
    takenTokens[takenTokens.length] = new Marker("WQ", 1, 'images/wq.png', 25, 65);
    takenTokens[takenTokens.length] = new Marker("WK", 1, 'images/wk.png', 25, 85);
    takenTokens[takenTokens.length] = new Marker("WB", 1, 'images/wb.png', 25, 105);
    takenTokens[takenTokens.length] = new Marker("WN", 1, 'images/wn.png', 25, 125);
    takenTokens[takenTokens.length] = new Marker("WR", 1, 'images/wr.png', 25, 145);
    takenTokens[takenTokens.length] = new Marker("BR", 1, 'images/br.png', 25, game.sideBar.height - 25);
    takenTokens[takenTokens.length] = new Marker("BN", 1, 'images/bn.png', 25, game.sideBar.height - 45);
    takenTokens[takenTokens.length] = new Marker("BB", 1, 'images/bb.png', 25, game.sideBar.height - 65);
    takenTokens[takenTokens.length] = new Marker("BQ", 1, 'images/bq.png', 25, game.sideBar.height - 85);
    takenTokens[takenTokens.length] = new Marker("BK", 1, 'images/bk.png', 25, game.sideBar.height - 105);
    takenTokens[takenTokens.length] = new Marker("BB", 1, 'images/bb.png', 25, game.sideBar.height - 125);
    takenTokens[takenTokens.length] = new Marker("BN", 1, 'images/bn.png', 25, game.sideBar.height - 145);
    takenTokens[takenTokens.length] = new Marker("BR", 1, 'images/br.png', 25, game.sideBar.height - 165);

    console.log(takenTokens);
    return takenTokens;
}

/** Helper function for draw responsible for drawing each gameBoard
 *
 */
function draw()
{
    for (var i = 0; i < gameBoards.length; i++)
    {
        gameBoards[i].draw();
    }
}

GameBoard.js GameBoard.js

function bind(scope, fn) {
   return function() {
      return fn.apply(scope, arguments);
   }
}

function GameBoard(boardID, sideBarID, numSquareRows, numSquareColumns, validator, tokens, takenTokens)
{   
    this.game = document.getElementById(boardID);
    this.gameContext = this.game.getContext("2d");
    var gamerect = this.game.getBoundingClientRect();
    //this.gameContext.translate(gamerect.left, gamerect.top);
    this.sideBar = document.getElementById(sideBarID);
    this.sideBarContext = sideBar.getContext("2d");
    var siderect = this.sideBar.getBoundingClientRect();
    //this.sideBarContext.translate(siderect.left, siderect.top);
    this.boardWidth = this.game.width;
    this.boardHeight = this.game.height;
    this.squareWidth = this.boardWidth / numSquareColumns;
    this.squareHeight = this.boardHeight / numSquareRows;
    if (this.squareHeight % 1 != 0) alert("WARNING: squareHeight is not a solid number, the program might not work correctly! Always ensure that the board height divided by the number of rows comes out as a whole number.");
    if (this.squareWidth % 1 != 0) alert("WARNING: squareWidth is not a solid number, the program might not work correctly! Always ensure that the board width divided by the number of columns comes out as a whole number.");

    this.validator = validator;
    this.tokens = tokens;
    this.takenTokens = takenTokens;


    this.turnOrderToken = new TurnMarker('images/wturn.png', 'images/bturn.png', siderect.width / 2 - 20, siderect.height / 2 - 20, 40, 40);

    this.activePlayer = 1; // Whose turn is it?
    this.selectedToken = null; // What token is currently being dragged around?
    this.takePiece = null; 

    // Event listeners function nearly identically to how they are handled in C#.
    this.game.addEventListener("mousedown", bind(this, this.onMouseDown), false);
    this.game.addEventListener("mousemove", bind(this, this.onMouseMove), false);
    this.game.addEventListener("mouseup", bind(this, this.onMouseUp), false);

    /** Helper function for drawBoard responsible for swapping between two colors whenever it is called.
    *
    */
    this.swapColor = function swapColor()
    {
        if (this.gameContext.fillStyle != '#0000ff')
        {
            this.gameContext.fillStyle = '#0000ff';
        } else {
            this.gameContext.fillStyle = '#ffffff';
        }
    }

    /** Responsible for drawing all the tokens
    *
    */
    this.drawTokens = function drawTokens()
    {
        for (var i = 0; i < this.tokens.length; i++)
        {
            var token = this.tokens[i];
            this.gameContext.drawImage(token.image, token.x, token.y, this.squareWidth, this.squareHeight);
        }
    }

    /** Responsible for drawing the checkerboard.
    *
    */
    this.drawBoard = function drawBoard()
    {
        this.gameContext.clearRect(0, 0, this.boardWidth, this.boardHeight);
        for (var i = 0; i < this.boardWidth; i += this.squareWidth) 
        {
            for (var j = 0; j < this.boardHeight; j += this.squareHeight) 
            {
            this.swapColor();
            this.gameContext.fillRect(i,j,this.squareWidth,this.squareHeight);
            }
        this.swapColor();
        }
    }

    this.drawMarkers = function drawMarkers()
    {
        for (var i = 0; i < this.takenTokens.length; i++)
        {
            var marker = this.takenTokens[i];
            console.log(marker.image + " " + marker.x + " " +  marker.y + " " +  marker.width + " " +  marker.height);
            if (marker.visible)
            {
                this.sideBarContext.drawImage(marker.image, marker.x, marker.y, marker.width, marker.height);
            }
        }
    }

    this.drawTurnMarker = function drawTurnMarker()
    {
        if (this.activePlayer == 1)
        {
            console.log(this.turnOrderToken.player1Image + " " + this.turnOrderToken.x + " " +  this.turnOrderToken.y + " " +  this.turnOrderToken.width + " " +  this.turnOrderToken.height);
            this.sideBarContext.drawImage(this.turnOrderToken.player1Image, this.turnOrderToken.x, this.turnOrderToken.y, this.turnOrderToken.width, this.turnOrderToken.height);
        }
        else
        {
            this.sideBarContext.drawImage(this.turnOrderToken.player2Image, this.turnOrderToken.x, this.turnOrderToken.y, this.turnOrderToken.width, this.turnOrderToken.height);
        }
    }

    /** Container method which runs all draw functions on the board.
     *
     */
    this.draw = function draw()
    {
        this.drawBoard();
        this.drawTokens();
        this.drawMarkers();
        this.drawTurnMarker();
    }

    /** Removes tokens from the board and adds them to the list of captured pieces in the sidebar
    *
    */
    this.capture = function capture(token)
    {
        for (var i  = 0; i < this.tokens.length; i++)
        {
            var takenToken = this.tokens[i];
            if (takenToken.x == token.x && takenToken.y == token.y)
            {
                this.tokens.splice(i, 1);
                break;
            }
        }
        for (var i  = 0; i < this.takenTokens.length; i++)
        {
            var takenToken = this.takenTokens[i];
            if (takenToken.name == token.name && takenToken.visible == false)
            {
                console.log(takenToken);
                takenToken.visible = true;
                break;
            }
        }
    }
}

/** Event that fires when the mouse button is released
*  Listeners in gameBoard
*/
GameBoard.prototype.onMouseUp = function (event)
{
    if (this.selectedToken != null)
    {
        var gridx = Math.round(this.selectedToken.x / this.squareWidth);
        var gridy = Math.round(this.selectedToken.y / this.squareHeight);

        // Snap to the nearest tile
        this.selectedToken.x = (gridx * this.squareWidth);
        this.selectedToken.y = (gridy * this.squareHeight);

        // Check to see if the move that was made is legal
        this.takePiece = this.validator[gridx][gridy];
        if (this.selectedToken.movementValidator())
        {
            // If it was, then advance the turn
            if (this.activePlayer == 1)
            {
                this.activePlayer = 2;
            } 
            else 
            {
                this.activePlayer = 1;
            }
        } 
        else 
        {
            // Otherwise move the token back to where it was
            this.selectedToken.x = this.selectedToken.initX;
            this.selectedToken.y = this.selectedToken.initY;
        }

        // Wherever the token ends up, update the grid to reflect that.
        this.validator[this.selectedToken.initX / this.squareWidth][this.selectedToken.initY / this.squareHeight] = null;
        this.validator[this.selectedToken.x / this.squareWidth][this.selectedToken.y / this.squareHeight] = this.selectedToken;
        this.selectedToken = null;
    }
}

/**
 *
 */
GameBoard.prototype.onMouseDown = function(event)
{
    var rect = this.game.getBoundingClientRect();
    var mousePos = {x:event.clientX - rect.left, y:event.clientY - rect.top};
    for (var i = 0; i < this.tokens.length; i++)
    {
        token = this.tokens[i];
        // if you clicked this token and it's your turn
        if (mousePos.x > token.x && mousePos.y > token.y && mousePos.x < token.x + this.squareWidth && mousePos.y < token.y + this.squareHeight && token.player == this.activePlayer)
        {
            this.selectedToken = token;

            // Store where the token was before we picked it up. That way if we make an illegal move we can restore it to its initial location
            this.selectedToken.initX = token.x;
            this.selectedToken.initY = token.y;
        }
    }
}

/** Event that fires when the mouse position is updated
*  Listeners in gameBoard
*/
GameBoard.prototype.onMouseMove = function(event)
{
    if (this.selectedToken != null)
    {
        var rect = this.game.getBoundingClientRect();
        var mousePos = {x:event.clientX - rect.left, y:event.clientY - rect.top};
        this.selectedToken.x = mousePos.x - (this.squareWidth / 2);
        this.selectedToken.y = mousePos.y - (this.squareHeight / 2);
    }
}

Marker.js Marker.js

/** Marker is a visual widget used to show taken pieces. 
*  player= The player associated with the marker
*  tokenImagePath= The valid path to the location of the marker texture
*  x,y= location of marker on the sidebar
*/
function Marker(name, player, markerPath, x, y)
{
    this.name = name;
    this.image = new Image();
    this.x = x;
    this.y = y;
    this.width = 20;
    this.height = 20;
    this.player = player;
    this.image.src = markerPath;
    this.visible = true;
}

GameToken.js GameToken.js

/** GameToken represents a chess piece. 
*  player= The player the chess piece belongs to
*  tokenImagePath= The valid path to the location of the chess piece texture
*  x,y= location of token on the board
*  movementValidator= Function to determine whether a move made by this token is legal or not. 
*  Validators are different for different types of tokens.
*/
function GameToken(game, name, player, tokenImagePath, x, y, movementValidator)
{
    this.game = game;
    this.name = name;
    this.image = new Image();
    this.x = x;
    this.y = y;
    game.validator[x / game.squareWidth][y / game.squareHeight] = this;
    this.player = player;
    this.image.src = tokenImagePath;
    this.movementValidator = movementValidator;
}

And finally a WIP Validation script to check for legal moves 最后是一个WIP验证脚本,以检查合法行为

/** Specific validation code for Pawns.
*
*/
function pawnValidator()
{
    // Pawns are tricky to validate because they can move one square directly forward, but can't take the square directly
    // in front of them, and can only move diagonally when they can capture. In addition, they can move two squares 
    // forward as long as they're in starting position.
    if (this.takePiece != null)
    {
        // If the square we moved to has an enemy in it and we've made a legal move with the pawn to take that piece
        if ((this.takePiece.player != this.player && 
            (this.x == this.initX + this.squareWidth || this.x == this.initX - this.squareWidth) &&
            ((this.player == 1 && this.y == this.initY + this.squareHeight) ||
            (this.player == 2 && this.y == this.initY - this.squareHeight))))
        {
            // We're allowed to remove the token here because we've validated that the pawn has made the correct movement to take the piece.
            capture(takePiece);
            takePiece = null;
            return true;
        }
        else
        {
            takePiece = null;
            return false;
        }
    }
    // The pawn is not capturing, so check to see that the move it is making is legal
    else if (this.x == this.initX)
    {
        if (this.player == 1)
        {
            if ((this.y == this.initY + this.game.squareHeight) || (this.y == this.initY + (2*this.game.squareHeight)) && this.initY == (this.game.squareHeight))
            {
                return true;
            }
        }
        else if (this.y == this.initY - this.game.squareHeight || (this.y == this.initY - (2*this.game.squareHeight)) && this.initY == (6*this.game.squareHeight))
        {
            {
                return true;
            }
        }
    }
    return false;
}

/** Specific validation code for Rooks.
*
*/
function rookValidator()
{
    // First check if the movement made was legal for a rook (straight line)
    if (this.x == this.initX || this.y == this.initY)
    {
    // Next check if the movement made went through any other pieces
        if (lineValidation(this.initX, this.initY, this.x, this.y))
        {
            if (this.game.takePiece != null) 
            {
                this.game.capture(this.game.takePiece);
            }
            this.game.takePiece = null;
            return true;
        }
    }
    return false;
}

/** Specific validation code for Knights.
*
*/
function knightValidator()
{
    // First check if the movement made was legal for a knight using relative positioning
    var relativeX = Math.abs(this.x - this.initX) / this.game.squareWidth;
    var relativeY = Math.abs(this.y - this.initY) / this.game.squareHeight;
    if ((relativeX == 1 && relativeY == 2) || (relativeX == 2 && relativeY == 1))
    {
        // Knights can jump, so we don't need to validate the movement further
        if (this.game.takePiece != null) 
        {
            this.game.capture(this.game.takePiece);
        }
        takePiece = null;
        return true;
    }
}

/** Specific validation code for Bishops.
*
*/
function bishopValidator()
{
    // First check if the movement made was legal for a bishop (diagonal line)
    if (Math.abs(this.x - this.initX) == Math.abs(this.y - this.initY))
    {
        // Next check if the movement made went through any other pieces
        if (lineValidation(this.initX, this.initY, this.x, this.y))
        {
            if (takePiece != null) 
            {
                capture(takePiece);
            }
            takePiece = null;
            return true;
        }
    }
}

/** Specific validation code for Kings.
*
*/
function kingValidator()
{
    // First check if the movement made was legal for a king using relative positioning
    var relativeX = Math.abs(this.x - this.initX) / squareSize;
    var relativeY = Math.abs(this.y - this.initY) / squareSize;
    if ((relativeX == 1 && relativeY == 1) || (relativeX == 1 && relativeY == 0) || (relativeX == 0 && relativeY == 1))
    {
        // TODO: Check to see if the move puts the king in check. That's a little past the scope of this project but would make for a nice addition.
        if (takePiece != null) 
        {
            capture(takePiece);
        }
        takePiece = null;
        return true;
    }
}

/** Specific validation code for Queens.
*
*/
function queenValidator()
{
    // First check if the movement made was legal for a queen (diagonal line or straight line)
    if ((Math.abs(this.x - this.initX) == Math.abs(this.y - this.initY)) ||
    (this.x == this.initX || this.y == this.initY))
    {
        // Next check if the movement made went through any other pieces
        if (lineValidation(this.initX, this.initY, this.x, this.y))
        {
            if (takePiece != null) 
            {
                capture(takePiece);
            }
            takePiece = null;
            return true;
        }
    }
}

/** Checks each square traveled over a line to see if it has traveled through another piece
*  IMPORTANT: This function only works if the move is legal! If the move made is impossible in chess this function
*  will not work correctly!
*/
function lineValidation(startX, startY, endX, endY)
{
    while (startX != endX || startY != endY)
    {
        if (startX < endX) startX += squareSize;
        if (startY < endY) startY += squareSize;
        if (startX > endX) startX -= squareSize;
        if (startY > endY) startY -= squareSize;
        var checkTake = gameBoardValidator[startX / squareSize][startY / squareSize];
        if (checkTake != null && (startX != endX || startY != endY))
        {
            return false;
        }
    }
    return true;
}

Both games work but the UI elements in that second side canvas always seem to be drawing in the first canvas spot. 两种游戏都可以工作,但是第二个侧面画布中的UI元素似乎总是在第一个画布中绘制。 Anybody see what I goofed up? 有人看到我发疯了吗?

In your GameBoard object you are trying to get context from window.sideBar instead of this.sideBar (it should have produced an error in console): 在您的GameBoard对象中,您尝试从window.sideBar而不是this.sideBar获取上下文(它应该在控制台中产生错误):

this.sideBarContext = sideBar.getContext("2d");

Change that line to: 将该行更改为:

this.sideBarContext = this.sideBar.getContext("2d");
                      ^^^^

and it should work. 它应该工作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM