River Tam
River Tam

Reputation: 3216

Is drawing circles really this inefficient compared to rectangles?

I'm working on a very small project in JavaScript just to help me understand the canvas from the bottom up. I'm trying to avoid using frameworks and the like for now, just so I can understand the basic functionality of HTML5 games.

It's a fairly basic "cursor mover" with a fading tail. That's it. Just a shape in one color, moving around based on player input, with a fading tail. I suggest you try it out; it's actually quite pretty.

Anyways, obviously I'd prefer the cursor to be a circle because it looks smoother. However, whenever I do so, the browser pretty much completely locks up on me. It clearly works, at least to a good extent, but it's slower than a turtle running through frozen peanut butter.

I know I'm not supposed to include just JSFiddle, but it's kind of a lot of code and the whole thing is running slowly.

The problem is most likely in the draw function:

Game.draw = function() {
    for (var sn = 0; sn < this.strokes.length; sn++) {
        var s = this.strokes[sn];
/*1*/   this.context.arc(s.x, s.y, this.cursorRadius, 0, 2 * Math.PI);
        this.context.fillStyle = this.bgColor;
/*2*/   //this.context.fillRect(s.x, s.y, this.cursorWidth, this.cursorHeight);
/*1*/   //this.context.fill();
        this.context.fillStyle = s.getColor();
/*2*/   //this.context.fillRect(s.x, s.y, this.cursorWidth, this.cursorHeight);
/*1*/   this.context.fill();
    }
};

The lines are labeled by approaches. 1 corresponds to circles, while 2 corresponds to rectangles.

Here's the whole project: http://jsfiddle.net/w4Rg3/3/

I just find it kind of hard to believe that it's so incredibly much slower to have circles (after seeing all of the projects that JS can do), and that I might be doing something wrong.

Upvotes: 0

Views: 180

Answers (2)

enhzflep
enhzflep

Reputation: 13089

Just a couple of observations (oh, and No! drawing circles is not 'inefficient' compared to squares, slower certainly - but still quite quick) Your problems unfortunately run much deeper that that..

It would appear that you need to have a re-think of the way that you plan to implement the game. It runs as you so succinctly put it, slower than a turtle through frozen peanut-butter. (hehe, niice!)

I suspect it's something to do with the way you've setup the Request animation frame, but really couldn't get that far into debugging without fear my laptop would melt and emit that magical blue smoke.

I trimmed all of the js from a copy, with the exception of the Key variable and the 2 calls to add event listeners for keyup and keydown. This left me with the following:

<!DOCTYPE HTML>
<html>
<head>
<title>flowing</title>
<script>

var Key = 
{
    _pressed: {},

    SPACE: 32,
    LEFT: 37,
    UP: 38,
    RIGHT: 39,
    DOWN: 40,

    isDown: function(keyCode) {
        return this._pressed[keyCode];
    },

    onKeydown: function(event) {
        this._pressed[event.keyCode] = true;
        console.log(this._pressed[event.keyCode]);
    },

    onKeyup: function(event) {
        delete this._pressed[event.keyCode];
        console.log(this._pressed[event.keyCode]);
    }
};
window.addEventListener('keyup', Key.onKeyup, false);
window.addEventListener('keydown', Key.onKeydown, false);

</script>
<style>
</style>
</head>
 <!-- canvas gets inserted here by js -->
<body >
</body>    
</html>

It bombs, big-time!

You see, the thing is - when you attach an event listener like that, whenever you're inside the handler, the 'this' keyword actually corresponds to the HTML element that triggered the event. So, what does that mean exactly?

The 3 functions inside it can be expanded to:

  1. return window._pressed[keyCode];
  2. window._pressed[event.keyCode] = true;
  3. delete window._pressed[event.keyCode];

Only problem is, the window object doesn't have a _pressed variable.. The variable, Key does.

You should open up the Developer Tools console of whichever browser you're using. In Chrome, FF and Opera it's Ctrl-Shift-I, in IE it's F12.

This will instantly highlight errors such as this one.

I'll see if I can't find an iceberg so I can keep testing without fear of having to shop for a new lappy.

[EDIT:] New code for Game.draw

Game.draw = function() 
{
    for (var sn = 0; sn < this.strokes.length; sn++) 
    {
        var s = this.strokes[sn]; /*1*/
        this.context.beginPath();
        this.context.arc(s.x, s.y, this.cursorRadius, 0, 2 * Math.PI);
        this.context.fillStyle = s.getColor(); //.bgColor; /*2*/
        this.context.closePath();
        this.context.fill();
    }
};

Drawing an arc involves a path. To use paths properly, you need to tell the canvas element when you start defining a path and again when you've finished defining one. I guess the browser was just ending up with 'the mother of all paths' which naturally enough took forever to draw. Of course, your paths are dead-simple, just simple circles. With the canvas's state machine properly updated with our intentions, the code seems no different in responsiveness whether we draw circles or squares. Hope that's the root of your problem. :)

Upvotes: 1

River Tam
River Tam

Reputation: 3216

everyone! I found the issue! It has nothing to do with the structure of my game or anything particularly finicky. Just an API issue!

The problem is indeed in the code sample in my question in the first post.

Game.draw = function() {
    for (var sn = 0; sn < this.strokes.length; sn++) {
        var s = this.strokes[sn];
/*1*/   this.context.beginPath();  /*THIS IS FIXED*/
/*1*/   this.context.arc(s.x, s.y, this.cursorRadius, 0, 2 * Math.PI);
/*1*/   this.context.closePath();  /*THIS IS FIXED*/
        this.context.fillStyle = this.bgColor;
/*2*/   //this.context.fillRect(s.x, s.y, this.cursorWidth, this.cursorHeight);
/*1*/   this.context.fill();
        this.context.fillStyle = s.getColor();
/*2*/   //this.context.fillRect(s.x, s.y, this.cursorWidth, this.cursorHeight);
/*1*/   this.context.fill();
    }
};

I made sure to mark the lines that I added with a "THIS IS FIXED" comment. I assume that it was previously just creating a really, really long arc and drawing it over and over, but who knows? Anyways, problem solved, and it looks beautiful.

Upvotes: 1

Related Questions