halfpastfour.am
halfpastfour.am

Reputation: 5933

HTML 5 Canvas seems to redraw removed parts

I have created the following code on jsfiddle. The goal is to remove a box from the canvas after it has been clicked. What actually happens is that the grid is cleared and completely redrawn WITH the removed box in it's old spot. The grid will only appear empty when all of the given objects have been removed... I am puzzled! What am I doing wrong?

jQuery(function(){

    GridBox = new GridBox();

    GridBox.init();

    var canvas    = GridBox.canvas;

    canvas.on( 'click', GridBox.clickHandler );

});

function GridBox()
{

    this.target        = { x: 0, y: 0 };
    this.current       = { x: 0, y: 0 };
    this.boxHeight     = 50;
    this.boxWidth      = 50;
    this.width         = 500;
    this.height        = 500;
    this.context       = null;
    this.canvas        = null;

    var self = this,
        init = false,
        bw   = this.width,
        bh   = this.height,
        p    = 0,
        cw   = bw + ( p * 2 ) + 1,
        ch   = bh + ( p * 2 ) + 1;

    /**
     * Array of boxes that are painted on the grid.
     * Each box has its own x and y coordinates.
     */
    this.boxesOnGrid    = [
        { x: 2, y: 2 },
        { x: 9, y: 2 },
        { x: 5, y: 5 }
    ];

    /**
     * Initiate this object
     * @constructor 
     */
    this.init    = function()
    {
        if( !init ) {
            var canvas    = jQuery( '<canvas/>' ).attr({ width: cw, height: ch }).appendTo( 'body' );

            this.canvas     = canvas;
            this.context    = this.canvas.get( 0 ).getContext( '2d' );

            this.createGrid();

            init    = true;

        }
    };

    this.clearGrid        = function()
    {
        alert( 'clearing grid' );
        this.context.clearRect( 0, 0, 500, 500 );
    };

    /**
     * Create the grid 
     */
    this.createGrid        = function()
    {
        for( var x = 0; x <= bw; x += this.boxWidth ) {
            this.context.moveTo( 0.5 + x + p, p );
            this.context.lineTo( 0.5 + x + p, bh + p );
        }

        for( var x = 0; x <= bh; x += this.boxHeight ) {
            this.context.moveTo( p, 0.5 + x + p );
            this.context.lineTo( bw + p, 0.5 + x + p );
        }

        this.context.strokeStyle    = "#aaa";
        this.context.stroke();

        var boxes    = this.boxesOnGrid;

        this.boxesOnGrid    = [];

        for( key in boxes ) {
            var currentBox    = boxes[ key ];
            alert( 'i want to create box ' + currentBox.x + 'x' + currentBox.y );
            this.createBoxAt( currentBox.x, currentBox.y );
        }
    };

    /**
     * Find a suitable path between two boxes
     */
    this.findPath        = function()
    {

    };

    this.clickHandler    = function( event )
    {
        var clickOffset        = {
                x:    event.offsetX,
                y:    event.offsetY
            }, clickedBox    = {
                x:    Math.ceil( clickOffset.x / 50 ),
                y:    Math.ceil( clickOffset.y / 50 )
            };

        for( key in GridBox.boxesOnGrid ) {
            if( GridBox.boxesOnGrid[ key ].x === clickedBox.x && GridBox.boxesOnGrid[ key ].y === clickedBox.y ) {
                GridBox.clearGrid();
                GridBox.removeBox( key );
                GridBox.createGrid();
            }
        }

    };

    /**
     * Remove a box from the grid by removing it from the boxes array
     * and re-drawing the grid.
     */
    this.removeBox        = function( key )
    {
        alert( 'removing box ' + key );
        this.boxesOnGrid.splice( key, 1 );
    };

    /**
     * Create a box at a given coordinate on the grid
     * @param    {int} x
     * @param    {int} y 
     */
    this.createBoxAt    = function( x, y )
    {
        var box    = {
                x:    x * this.boxWidth - this.boxWidth,
                y:    y * this.boxHeight - this.boxHeight
            };

        this.createBox( box.x, box.y );
        this.saveBox( x, y );
    };

    this.createBox    = function( xpos, ypos )
    {
        this.context.rect( xpos, ypos, this.boxWidth, this.boxHeight );
        this.context.fillStyle    = '#444';
        this.context.fill();
    };

    this.saveBox    = function( x, y )
    {
        this.boxesOnGrid.push( { x: x, y: y } );
    };
}​

Upvotes: 1

Views: 197

Answers (1)

Loktar
Loktar

Reputation: 35309

Working Fiddle

Change createBox to the following.

  this.createBox    = function( xpos, ypos )
    {
        this.context.beginPath();
        this.context.rect( xpos, ypos, this.boxWidth, this.boxHeight );
        this.context.fillStyle    = '#444';
        this.context.fill();
        this.context.closePath();
    };

Your not properly starting/ending paths, so the previous path isnt cleared when you redraw thus filling them all in again. Another way around it is to just use fillRect instead.

The first step to create a path is calling the beginPath method. Internally, paths are stored as a list of sub-paths (lines, arcs, etc) which together form a shape. Every time this method is called, the list is reset and we can start drawing new shapes.

Further Reading

Upvotes: 4

Related Questions