Xion Dark
Xion Dark

Reputation: 3434

Html 5 canvas trouble

I'm trying to practice with HTML5 canvas drawing so I came up with this. I should result in a yellow star that is overlapped by a green silhouette of a bust (the head and shoulders of a person).

However if the drawStar() line is un-commented, the green silhouette is not drawn. Is this Working code is not as important as an explanation of what is happening.

http://jsfiddle.net/xiondark2008/LYfHJ/2/

HTML:

<!DOCTYPE html>
<html>
<head>
    <style>
    h6{
        -webkit-margin-before: .5em;
        -webkit-margin-after: 0;
    }
    label{
        margin-left: 1em;
    }
    </style>

</head>
<body>

    <canvas id="myCanvas" width="19" height="19" 
        style="border:1px solid d3d3d3;">
        Your browser does not support the HTML5 canvas tag.</canvas>
</body>
</html>

javascript:

function drawProfile(ctx,x,y,width,height) {
    this.flp = false;
    this.x = function(r){ return Math.floor(width*(this.flp?1-r:r)); }
    this.y = function(r){ return Math.floor(height*r); }

    ctx.save();

    ctx.translate( x, y );
    ctx.fillStyle="#40FF00";

    ctx.beginPath();
    ctx.moveTo( this.x(0), this.y(1) );
    ctx.bezierCurveTo(
        this.x(0),      this.y(125/190),
        this.x(70/190), this.y(170/190),
        this.x(75/190), this.y(120/190)
    );
    ctx.lineTo( this.x(95/190), this.y(130/190) );
    ctx.bezierCurveTo(
        this.x(40/190), this.y(130/190),
        this.x(30/190), this.y(0), 
        this.x(95/190), this.y(0)
    );

    this.flp = true;

    ctx.bezierCurveTo(
        this.x(30/190), this.y(0),
        this.x(40/190), this.y(130/190), 
        this.x(95/190), this.y(130/190)
    );
    ctx.lineTo( this.x(75/190), this.y(120/190) );
    ctx.bezierCurveTo(
        this.x(70/190), this.y(170/190),
        this.x(0), this.y(125/190),
        this.x(0), this.y(1)
    );
    ctx.fill();

    this.flp = false;

    ctx.restore();
}

function drawStar(ctx,x,y,height) {

    var pnts = 5,
        rad = height/(1-Math.cos(0.8*Math.PI));

    this.x = function(p,dst){
        return dst * Math.sin( (p/pnts)*2*Math.PI );
    }
    this.y = function(p,dst){
        return dst * Math.cos( (p/pnts)*2*Math.PI ) * -1;
    }
    this.movePct = function(a,b,pct){
        var f = (function(x){
                var m = (b.y-a.y)/(b.x-a.x);
                return m*x+(a.y-(a.x*m));
            }).bind(),
            r = b.x - a.x,
            point = {};

        point.x = a.x+(r*pct);
        point.y = f(point.x);

        return point;
    }
    this.transPoints = function(s,p,e,pct){
        var sp = this.movePct(s,p,pct),
            ep = this.movePct(e,p,pct);

        return [sp,ep];
    }

    ctx.save();

    ctx.translate( x+rad, y+rad );
    ctx.fillStyle = "#ffff00";

    ctx.beginPath();

    for(var i=0;i<pnts;i++){
        var dst = rad/2,
            s = { x: this.x( i+0.5, dst ),
                  y: this.y( i+0.5, dst ) },
            p = { x: this.x( i+1, rad ),
                  y: this.y( i+1, rad ) },
            e = { x: this.x( i+1.5, dst ),
                  y: this.y( i+1.5, dst ) },
            t = this.transPoints(s,p,e,.75);

        if(i==0){ ctx.moveTo( s.x, s.y ); }

        ctx.lineTo(t[0].x, t[0].y);
        ctx.quadraticCurveTo(p.x, p.y, t[1].x, t[1].y);
        ctx.lineTo(e.x, e.y);
    }
    ctx.fill();

    ctx.restore();

}

function draw(c) {

    var ctx = c.getContext("2d");

    this.x = function(r){ return c.width*r; }
    this.y = function(r){ return c.height*r; }

    ctx.shadowBlur=this.y(1/19);
    ctx.shadowColor="black";

    //drawStar( ctx, this.x(-3/19), this.y(-1/19), this.y(20/19) );
    drawProfile( ctx, this.x(6/19), this.y(1/19), this.x(18/19), this.y(18/19) );


    if(0){
        ctx.clearRect( this.x(1), this.y(0), this.x(1), this.y(1) );
        ctx.clearRect( this.x(0), this.y(1), this.x(1), this.y(1) );
    }

}

draw( document.getElementById("myCanvas") );

Upvotes: 1

Views: 190

Answers (2)

JanUlrich
JanUlrich

Reputation: 125

Joe is right. You are overwriting the this.x and this.y functions. I don't know why you are doing this. I made a workaround by resetting the this.x and this.y functions to their old value at the end of the functions drawProfile and drawStar:

function drawProfile(ctx,x,y,width,height) {
    var oldX = this.x, //storing the old values for this.x ...
        oldY = this.y; // ... and this.y
    this.flp = false;
    this.x = function(r){ return Math.floor(width*(this.flp?1-r:r)); }
    this.y = function(r){ return Math.floor(height*r); }

    ctx.save();

    ctx.translate( x, y );
    ctx.fillStyle="#40FF00";

    ctx.beginPath();
    ctx.moveTo( this.x(0), this.y(1) );
    ctx.bezierCurveTo( this.x(0), this.y(125/190),
                       this.x(70/190), this.y(170/190),
                       this.x(75/190), this.y(120/190)
                      );
    ctx.lineTo( this.x(95/190), this.y(130/190) );
    ctx.bezierCurveTo( this.x(40/190), this.y(130/190),
                       this.x(30/190), this.y(0), 
                       this.x(95/190), this.y(0)
                      );

    this.flp = true;

    ctx.bezierCurveTo( this.x(30/190), this.y(0),
                       this.x(40/190), this.y(130/190), 
                       this.x(95/190), this.y(130/190)
                      );
    ctx.lineTo( this.x(75/190), this.y(120/190) );
    ctx.bezierCurveTo( this.x(70/190), this.y(170/190),
                       this.x(0), this.y(125/190),
                       this.x(0), this.y(1)
                      );
    ctx.fill();

    this.flp = false;

    ctx.restore();

    this.x = oldX; //Restoring old values
    this.y = oldY;
}

function drawStar(ctx,x,y,height)
{
    var pnts = 5,
        rad = height/(1-Math.cos(0.8*Math.PI)),
        oldX = this.x, //storing the old values for this.x ...
        oldY = this.y; // ... and this.y

    this.x = function(p,dst){
        return dst * Math.sin( (p/pnts)*2*Math.PI );
    }
    this.y = function(p,dst){
        return dst * Math.cos( (p/pnts)*2*Math.PI ) * -1;
    }
    this.movePct = function(a,b,pct){
        var f = (function(x){
            var m = (b.y-a.y)/(b.x-a.x);
            return m*x+(a.y-(a.x*m));
        }).bind(),
            r = b.x - a.x,
            point = {};

        point.x = a.x+(r*pct);
        point.y = f(point.x);

        return point;
    }
    this.transPoints = function(s,p,e,pct){
        var sp = this.movePct(s,p,pct),
            ep = this.movePct(e,p,pct);

        return [sp,ep];
    }

    ctx.save();

    ctx.translate( x+rad, y+rad );
    ctx.fillStyle = "#ffff00";

    ctx.beginPath();

    for(var i=0;i<pnts;i++){
        var dst = rad/2,
            s = { x: this.x( i+0.5, dst ),
                 y: this.y( i+0.5, dst ) },
            p = { x: this.x( i+1, rad ),
                 y: this.y( i+1, rad ) },
            e = { x: this.x( i+1.5, dst ),
                 y: this.y( i+1.5, dst ) },
            t = this.transPoints(s,p,e,.75);

        if(i==0){ ctx.moveTo( s.x, s.y ); }

        ctx.lineTo(t[0].x, t[0].y);
        ctx.quadraticCurveTo(p.x, p.y, t[1].x, t[1].y);
        ctx.lineTo(e.x, e.y);
    }
    ctx.fill();

    ctx.restore();

    this.x = oldX; //Resetting the old values
    this.y = oldY;
}

function draw(c)
{

    var ctx = c.getContext("2d");

    this.x = function(r){ return c.width*r; }
    this.y = function(r){ return c.height*r; }

    ctx.shadowBlur=this.y(1/19);
    ctx.shadowColor="black";

    drawStar( ctx, this.x(-3/19), this.y(-1/19), this.y(20/19) );
    drawProfile( ctx, this.x(6/19), this.y(1/19), this.x(18/19), this.y(18/19) );



    if(0){
        ctx.clearRect( this.x(1), this.y(0), this.x(1), this.y(1) );
        ctx.clearRect( this.x(0), this.y(1), this.x(1), this.y(1) );
    }

}

draw( document.getElementById("myCanvas") );

http://jsfiddle.net/hhaXM/

Upvotes: 0

Joe
Joe

Reputation: 47609

You're using this in your functions. In the context here, this is bound to window (there are lots of resources out there about how to use this, and you're doing it incorrectly).

In each function that you use this, is is bound to the same thing (window) so they over-write each other's variables. (This is because of the way you're using the functions. If you newed them it would be a different story).

So

  1. your draw() method sets this.x, which is actually window.x,
  2. then passes it to drawStar() as an argument
  3. the drawStar() function method changes the value of this.x (which is window.x) and returns back to draw()
  4. then draw() calls drawProfile() with this.x (which is window.x), except this time its value is a function rather than the float value you wanted it to be.
  5. drawProfile expected the argument to be a float, but now it's a function

(for x read x and y)

You can do this all with closures and variables. Remove the thises and your program will work. Probably.

Upvotes: 2

Related Questions