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>
6
  • 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. Commented May 30, 2012 at 9: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. Commented May 30, 2012 at 9: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. Commented May 30, 2012 at 10:00
  • ok, I don't know how to do that... ? Commented May 30, 2012 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. Commented May 30, 2012 at 10:15

1 Answer 1

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.

Sign up to request clarification or add additional context in comments.

3 Comments

thx @Alnitak this is very clear. Just: why the last (i) bit at the end?
@seinecle because that's how the current value of the "outer" i is passed to the IIFE.
@Altinak great I learned a lot + I got the code to work. Thanks a lot.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.