Canvas Rectangles werden mit 10 Pixel Verschiebung gerendert. Warum?

philishake

javascript enthusiast
Hey Leute!

Ich sitze gerade privat an einer kleinen HTML5 Graphic+Game-Engine. Bisher betreibe ich noch viel prototyping. Dabei bin ich allerdings auf ein merkwürdiges Problem gestoßen. Ich rendere farbige Vierecke in ein Canvas. Diese werden allerdings mit einer Verschiebung von etwa 10 Pixeln auf der Y Achse gezeichnet. Die Frage ist, ob ich was in meinem Code falsch habe, oder ob das ein generelles Problem ist. Hier mal mein Code:

(Ich habe den Code sehr simple gehalten, da es nur ums prototyping geht. Daher fehlen jedwede Kommentare)

index.html:
HTML:
<!DOCTYPE html>
<html>
    <head>
        <title>MinerJS Sandbox</title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <!-- Cascading Stylesheets -->
        <link type="text/css" href="style.css" rel="stylesheet" />
        <!-- JavaScript -->
        <script type="text/javascript" charset="utf-8" src="script.js"></script>
        <script type="text/javascript" charset="utf-8" src="../js/scripts/Namespace.js"></script>
        <script type="text/javascript" charset="utf-8" src="../js/scripts/helper.js"></script>
        <script type="text/javascript" charset="utf-8">
            window.onload = function() {
                var engine = new Engine();
                engine.initialize();

                var renderButton = document.getElementById('render');
                renderButton.onclick = function() {
                    var rect = new Rect();
                    var x = document.getElementById('x').value;
                    var y = document.getElementById('y').value;
                    var w = document.getElementById('w').value;
                    var h = document.getElementById('h').value;
                    var c = document.getElementById('c').value;
                    var z = document.getElementById('z').value;

                    if(w != 0 && h != 0 && c != 0) {
                        rect.setParameter(x, y, w, h, c, z);

                        engine.addObject(rect);
                        engine.render();
                    } else {
                        alert('Wrong render information!');
                    }
                }
                
                renderButton.onclick();
            }
        </script>
    </head>
    <body>
        <div id="controls">
            <input type="text" id="x" value="10" /> x <br />
            <input type="text" id="y" value="10" /> y <br />
            <input type="text" id="w" value="10" /> width <br />
            <input type="text" id="h" value="10" /> height <br />
            <input type="text" id="c" value="#cdfcdf" /> color <br />
            <input type="text" id="z" value="0" /> depth <br />
            <button id="render">render</button>
        </div>
        <div id="screen">
            <canvas id="gameScreen">HTML5 Only</canvas>
        </div>
    </body>
</html>

style.css
Code:
#controls {
    position: absolute;
    width: 200px;
    padding: 5px;
    border: solid 1px;
    top: 10px;
    left: 10px;
}

#screen {
    position: absolute;
    top: 10px;
    left: 234px;
    border: solid 1px;
}

#gameScreen {
    width: 500px;
    height: 500px;
}

Namespace.js
Code:
var Scripts = {}

helper.js
Code:
/* 
 * Helper function for often used js functionality
 */
Scripts.Helper = function() {
    /**
     * @description document.getElementById
     * @function
     * @public
     * @param {string} id Object ID
     * @return {Object}
     */
    this.getById = function(id) {
        return document.getElementById(id);
    }
    /**
     * @description document.getElementsByClass
     * @function
     * @public
     * @param {string} className Name of the class
     * @return {array]
     */
    this.getByClass = function(className) {
        return document.getElementsByClassName(className);
    }
    /**
     * @description Removes all classes from an object
     * @function
     * @public
     * @param {string} id Object id
     * @return {undefined}
     */
    this.removeClass = function(id) {
        this.getById(id).className = '';
    }
    /**
     * @description Bind a function to an object in the DOM
     * @function
     * @public
     * @param {string} type      Function type to bind
     * @param {string} id        ID of the object to bind to
     * @param {Object} callback  Function to call
     * @param {Object} parameter Parameter for the function to call
     * @return {undefined}
     */
    this.bind = function(type, id, callback, parameter) {
        var object = this.getById(id);
        parameter = (typeof parameter != 'undefined') ? parameter : {};
        switch(type) {
            case 'click':
                object.onclick = function(parameter) {
                    callback(parameter);
                }
                break;
        }
    }
    /**
     * @description Unbind a function from an object in the DOM
     * @function
     * @public
     * @param {string} id   ID of the object where to unbind from
     * @param {string} type Type of functionality to unbind
     * @return {undefined}
     */
    this.unbind = function(id, type) {
        var object = this.getById(id);
        switch(type) {
            case 'click':
                object.onclick = undefined;
        }
    }
    /**
     * @description Renders content into a screen
     * @function
     * @public
     * @param {string}  screenId  Id in the DOM
     * @param {string}  content   Content to render
     * @param {boolean} overwrite Overwrite existing content
     * @return {undefined}
     */
    this.renderToScreen = function(screenId, content, overwrite) {
        overwrite = (typeof overwrite == 'undefined') ? true : overwrite;
        var object = this.getById(screenId);
        if(overwrite) {
            object.innerHTML = content;
        } else {
            object.innerHTML += content;
        }
    }
    /**
     * @description Return coordinates for given id
     * @function
     * @public
     * @param {string} id ID of the object
     * @return {object}
     */
    this.getPositionForObjectWithId = function(id) {
        var el = this.getById(id);
        
        var _x = 0;
        var _y = 0;
        
        while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) ) {
            _x += el.offsetLeft - el.scrollLeft;
            _y += el.offsetTop - el.scrollTop;
            el = el.offsetParent;
        }
        return { top: _y, left: _x };
    }
}

scripts.js
Code:
var Engine = function() {

    var _rendered,
        _pipeline,
        _idCounter;

    this.initialize = function() {
        _rendered  = new Array();
        _pipeline  = new Array();
        _idCounter = 0;
        
        var canvas = _getCanvas();
        var helper = new Scripts.Helper();
        
        canvas.addEventListener('click', function(event) {
            var cp = helper.getPositionForObjectWithId('gameScreen');

            var click = {
                x : event.pageX - cp.left,
                y : event.pageY - cp.top
            }
            
            console.log('Clicked: ' + click.x + ' ' + click.y);
            
            for(var i=0; i<_rendered.length; i++) {
                var object = _rendered[i];
                
                var obj = {
                    pos : {
                        x : object.getPosition().x,
                        y : object.getPosition().y
                    },
                    size : {
                        w : object.getSize().w,
                        h : object.getSize().h
                    }
                }
                
                console.log('on x: ' + obj.pos.x + ' to ' + Number(obj.pos.x + obj.size.w));
                console.log('on y: ' + obj.pos.y + ' to ' + Number(obj.pos.y + obj.size.h));
                
                if(click.x >= obj.pos.x && click.x <= Number(obj.pos.x + obj.size.w) &&
                   click.y >= obj.pos.y && click.y <= Number(obj.pos.y + obj.size.h)) {
                    object.action();
                }
            }
        });
    };

    this.addObject = function(object) {
        object.id = _idCounter;
        _pipeline.push(object);
        _idCounter++;
        return _idCounter - 1;
    };
    this.render = function() {

        for(var i=0; i<_rendered.length; i++) {
            _pipeline.unshift(_rendered[i]);
        }

        _pipeline.sort(function(obj_a, obj_b){
            return obj_a.getIndex() - obj_b.getIndex()
        });

        _rendered = new Array();

        var context = _getContext();

        context.clearRect(0, 0, screen.width, screen.height);

        for(var i=0; i<_pipeline.length; i++) {
            _pipeline[i].draw(context);
            _rendered.push(_pipeline[i]);
        }

        _pipeline = new Array();
    };
    
    function _getCanvas() {
        return document.getElementById('gameScreen');
    }
    
    function _getContext() {
        return _getCanvas().getContext('2d');
    }
}

var Rect = function() {

    var _x, _y, _w, _h, _c, _z;

    this.setParameter = function(x, y, w, h, c, z) {
        _x = Number(x);
        _y = Number(y);
        _w = Number(w);
        _h = Number(h);
        _z = Number(z);
        
        _c = c;
    }

    this.draw = function(screen) {
        screen.beginPath();
        screen.rect(_x, _y, _w, _h);
        screen.fillStyle = _c;
        screen.fill();
    }
    
    this.action = function() {
        console.log('You clicked a rectangle with the color: ' + _c);
    }

    this.getIndex = function() {
        return _z;
    }
    
    this.getPosition = function() {
        return {
            x : _x,
            y : _y
        }
    }
    
    this.getSize = function() {
        return {
            w : _w,
            h : _h
        }
    }
}

Testen kann man das ganze unter folgender Adresse: http://minerjs.com/sandbox

Vielen Dank schonmal im vorraus!
 
In Firefox 14 wird alles richtig dargestellt.

Vielleicht irritiert dich auch, dass du das canvas Element mittels CSS verzerrst. Entferne mal

CSS:
#gameScreen {
    width: 500px;
    height: 500px;
}
 
Aber wie sage ich dem canvas dann seine Größe? Muss ich das altmodisch im html tag machen?

edit: leider ändert das entfernen des css Teils nicht. Das Rechteck wird noch immer 10 Pixel zu weit gerendert
 
Zuletzt bearbeitet:
Guck mal in den Konsole, wenn du klickst. Und bei mir sieht das Rechteck auch eher wie 10*20 aussieht und nicht wie 10*10
 
Sieht es bei dir möglicherweise so aus, als wäre die Regel

CSS:
#gameScreen {
    width: 500px;
    height: 500px;
}

noch immer da? Siehe mein erster Post.
 
Ich habe gerade mit folgenden Browsern getestet und bei allen war das Rechteck weder quadratisch, noch an der richtigen Position:
- Internet Explorer 8
- Internet Explorer 9
- Firefox 14
- Chrome 21
- Opera 12
- Safari 5.1.7

Wie kann dass denn bitte sein?

EDIT: Hier mal bei jsFiddle.net zum besseren angucken: http://jsfiddle.net/9WWqG/405/
 
Zuletzt bearbeitet:
Zurück