0

In the following code the drawFunc is not executed during the loop (I can see that with a step-by-step-debugging, the code just jumps to the end of the function).

The function gets executed at the line "stage.add(layer);", causing a bug.

Any help on how to fix this?

<html>
    <head>
        <style>
            body {
                margin: 0px;
                padding: 0px;
            }
            canvas {
                border: 1px solid #9C9898;
            }
        </style>
        <script type="text/javascript" src="http://www.html5canvastutorials.com/libraries/kinetic-v3.9.7.js"></script>
        <script>
            window.onload = function() {

                // global parameters
                var curvatureX = 10;
                var curvatureY = 10;
                var nodeRadius = 10;

                var stage = new Kinetic.Stage({
                    container: "container",
                    width: 578,
                    height: 300
                });
                var layer = new Kinetic.Layer();


                var nodes = [];

                for (var i = 0;i<10;i++){
                    nodes[i] = new Kinetic.Circle({
                        x: i*20,
                        y: i*5,
                        z:1,
                        radius: nodeRadius,
                        fill: "blue",
                        stroke: "black",
                        strokeWidth: 4
                    });
                    layer.add(nodes[i]);


                }


                var edges = [];
                for (var i = 0;i<9;i++){
                    console.log("just after the for: "+i);
                    edges[i]= new Kinetic.Shape({


// *** PROBLEMS IS HERE
// *** FOR LOOP SKIPS THE FOLLOWING LINES

                        drawFunc: function() {
                            var context = this.getContext();
                            context.beginPath();
                            console.log("in the drawing function: "+i);
                            context.moveTo(nodes[i].getX(), nodes[i].getY());
                            if ((nodes[i].getY()-nodes[i+1].getY())<0){
                                context.quadraticCurveTo((nodes[i].getX()+nodes[i+1].getX()+curvatureX)/2, (nodes[i].getY()+nodes[i+1].getY()-curvatureY)/2, nodes[i+1].getX(), nodes[i+1].getY());
                            }
                            else{
                                context.quadraticCurveTo((nodes[i].getX()+nodes[i+1].getX()+curvatureX)/2, (nodes[i].getY()+nodes[i+1].getY()+curvatureY)/2, nodes[i+1].getX(), nodes[i+1].getY());

                            }
                            context.lineWidth = 10;
                            // line color
                            context.strokeStyle = "black";
                            context.stroke();
                        },

// *** LOOP RESUMES HERE


                        fill: "#00D2FF",
                        stroke: "black",
                        strokeWidth: 4
                    });
                    layer.add(edges[i]);
                }


                //*** FUNCTION IS EXECUTED HERE, CAUSING BUG.
                stage.add(layer);

                var amplitude_1 = 100;
                var amplitude_2 = 30;
                var period = 2000;
                // in ms
                var centerX = stage.getWidth() / 2;
                var centerY = stage.getHeight() / 2;

                stage.onFrame(function(frame) {
                    for (var i=0;i<nodes.length;i++){
                        nodes[i].setX(amplitude_1 * i * Math.sin(frame.time * 2 * Math.PI / period) + centerX);
                        nodes[i].setY(amplitude_2 * i+ 20);
                    }
                    layer.draw();

                });

                stage.start();
            };

        </script>
    </head>
    <body>
        <div id="container"></div>
    </body>
</html>
Alnitak
  • 334,560
  • 70
  • 407
  • 495
seinecle
  • 10,118
  • 14
  • 61
  • 120
  • You're defining a function as part of an object, not calling it explicitly, AFAICT, so there's no reason it would execute in the loop--so the framework is calling it. Which means something in the framework isn't happy about your draw function. I'd be a little nervous about the "this" reference because of JS's "this" semantics, if that isn't the issue, then it's something else framework-specific. You don't include any error info, so can't help there. – Dave Newton May 30 '12 at 09:54
  • yes, when arrived at "stage.add(layer);" an error "nodes[i + 1] is undefined".This is thrown because at this moment the indice i has a value of 9. Trying to execute the function throws an error because it tries to fetch nodes[i+1], which is nodes[10] => does not exist. – seinecle May 30 '12 at 09:58
  • Then your draw function needs to enclose the loops "i" value, there are a number of ways to do this, including building the function instead of simply defining it. – Dave Newton May 30 '12 at 10:00
  • ok, I don't know how to do that... ? – seinecle May 30 '12 at 10:04
  • Search SO or google for javascript function closure or javascript loop value closure etc, it's a very common problem. Also, if you don't use a @ tag with a username, like @seinecle, ppl may never know that you've replied to them. – Dave Newton May 30 '12 at 10:15
  • We need some sort of FAQ for this - I can't remember how many times I've answered this question. – Alnitak May 30 '12 at 10:16

1 Answers1

1

You cannot use a variable declared outside a function and assume that its value when used inside that function is what it was at the time the function was declared.

In the particular case of a loop variable, that variable will have the last value assigned to it by the time the inner function is called.

To fix this, you have to "close" the outer variable's value inside a new scope, in your case that would look like this:

for (var i = 0; i < 9; ++i) {
    ...
    drawFunc: (function(i) {  //  <------------+
        return function() {   //               |
            alert(i);         // you can use this "i" here
        }
    })(i)
}

The unusual looking construct now there is an immediately invoked function expression which has been passed your loop variable i, but now has a locally bound copy of that variable in its scope.

Alnitak
  • 334,560
  • 70
  • 407
  • 495