paperJS (paper.js) JavaScript Library walk through by Brett Paufler - 9-10-13

JavaScript Tutorial & paper.js primer

Pendulum Animation (review)

See the Compound Pendulum Tutorial if interested in how this graphic was made.  Or the below, should be enough for many.


// All this was pulled line for line from the Pendulum Tutorial
var myStar = [];
var hSS = halfSquareScreen;
for (i = 1; i <= 13; i++){
    myStar[i] = Path.Star(CP, 5 + i, hSS/(4 * i), 1);
    myStar[i].strokeColor = 'black';
}
myStar[13].strokeColor = 'blue';
//myStar[10].strokeWidth = 3;
for (i = 2; i <= 13; i++){
    myStar[i].position = myStar[i - 1].firstSegment.point;
}
function animateObjects(){
    //testCircle.position.y += 1;
    for (i = 1; i <= 13; i++){
        myStar[i] = myStar[i].rotate(i * Math.pow(-1, i));
    }
    for (i = 2; i <= 13; i++){
        myStar[i].position = myStar[i - 1].firstSegment.point;
    }
}

Since, I pulled this in wholesale, the only real problem was in remembering to comment out the default animateObject() function.  But luckily, since that ran, and the circle dropped, it jogged my memory that it was there.  Do you really need an example of commenting out an outdated function?  Eh, it's as easy to include as not:
// This is the default animateObjects function that I had to comment out
//function animateObjects(){
//    testCircle.position.y += 1;
//}

paper.js contains method = Path.contains(point)

Counter intuitively, as some point, I tend to code from the bottom up.

I code this first:
function onMouseDown(event){
    testMouseClick(event);
    checkClickBox(event);
    updateFeedbackPointText(event);
}

But, the checkClickBox(event); will only throw an error on my debugger (JSLint in Notepad++) until I create that function, so while I am coding the previous, I insert an empty function call into my code:
function checkClickBox(event){}

Also, note: I pulled the updateFeedbackPointText(event); function into the onMouseDown(event) function, so that event (rather than the onFrame(event) is the event the PointText's will track).  Got that?  This rather than that.


Of course, a few minutes later, I came back and filled out the checkClickBox() function:, which I'll use over and over again for these sandbox tutorials, so at some point I'm going to have to update my default page to include this.
function checkClickBox(event){
    for (i = 1; i <= 6; i++){
        if (clickBox[i].contains(event.point)){
            switch (i){
            case 1:
                addStar();
                break;
            case 2:
                deleteStar();
                break;
            case 3:
                addStar();
                break;
            case 4:
                deleteStar();
                break;
            case 5:
                addStar();
                break;
            case 6:
                deleteStar();
                break;
            } // end switch
        } // end contains if
    }  // end for
}  // end checkClickBox function

This function includes two new function calls, so I create those at the same time, as well:
function addStar(){}
function deleteStar(){}
I insert these functions above the checkClickBox() function.  Order is important.

Once the switch statement looks like its up and running (no errors, program doesn't crash, still works as I want it to), I filled out the two new functions.  First, the deleteStar() function, as that one is easier:
function deleteStar(){
    if (visibleStars >= 2){
        myStar[visibleStars].visible = false;
        visibleStars -= 1;
        myStar[visibleStars].strokeColor = 'blue';
    }
}

And then the addStar() function,
function addStar(){
    if (visibleStars <= 12){
        myStar[visibleStars].strokeColor = 'black';
        visibleStars += 1;
        myStar[visibleStars].visible = true;
        myStar[visibleStars].strokeColor = 'blue';
    }
}

For both, I use a new variable to track the number of visible objects:
var visibleStars = 13;

I would point out:
  1. The checkClickBox(event) function is nice and long with plenty of nested structures, so as each layer is closed out, I comment which layer is closing.
  2. All I'm doing with the deleteStar() and addStar() functions is changing one of the myStar[] Objects from visible to invisible (true or false, or vice a versa) and altering which myStar[] is to appear blue.
  3. I am not adding or deleting any objects; that's more complicated and something to be avoided.
And since this is supposed to be a lesson on contains, I should go back and review that code.  This being the start of the appropriate function:
function checkClickBox(event){
    for (i = 1; i <= 6; i++){
        if (clickBox[i].contains(event.point)){
            switch (i){
            case 1:
                addStar();
                break; 
(etc)...


The for loop cycles through the six clickBox[]'s, checks to see if they contain the event.point, which since this function is being called by:
function onMouseDown(event){
    testMouseClick(event);
    checkClickBox(event);
    updateFeedbackPointText(event);
}
The event in question is the mouseClick, onMouseDown(event).
If this event.point is located within one of the checkClickBox's, the code searches through a switch/case structure to determine what to do next.  And for ease of readability, modularity, personal sanity, debugging, and what not, I recommend instructing the program to run a subroutine function (unless what is to be done is so simple and basic that calling a function would seem sort of OCD'ish, if you know what I mean).   And then again, if you  suffer from OCD, it's probably a good idea to get into this right habit now (before something else takes hold), and always call call your switch/case actions as external functions.  It's just easier to write, read, and debug.

event.point

And from there, it's time for me to plot values into the PointText's and see what there is to see.
As previously stated, all of these events are derived from the onMouseDown(event).

And because of this, this is where I finally discovered what I was doing wrong in regards to events.  You see, to 'initialize' the values of the PointText graphics, I was calling the updateFeedbackPointText(); imediately after I had created that function, but before an onMouseDown(event) was ever called.  No onMouseDown(event), meant no event for the updateFeedbackPointText() function, and so it was a blind call and crashing the program.

So, now, when the page loads, all of the FBText[] PointText objects have the default text from when they were initialized from within the PointText creation for loop, namely: 'FBText[i] nada yet'.

Interestingly, after the first onMouseDown(event), the program hangs at event.midPoint.  We know it hangs at this point in the code as the PointText's do not resolve past this line.  This will be something to keep in mind later.  And perhaps a master level programmer would wrap all events in a try/catch block for this very reason as a way of avoiding unforeseen difficulties.  But for now, I'm just going to make a mental note of the peculiarity:
That is to say until one of the pre-created functions (onFrame(), onMouseDown(), etc.) kicks off an event, event has little meaning.  And since event.middlePoint identifies the midPoint between two events, that property isn't set (not even as a Null, undefined, or anything -- hence why the program is crashing) until two events have been triggered.

And that, I do believe, is the piece of the puzzle I was missing to understand events.

Next up, the remainder of the onMouseFunctions: onMouseUp, onMouseDrag, etc.



previous (CompoundPendulum)        paper.js tutorial index       next - onMouseFunctions()



Back to BrettCode Home



Brett Words
my writing site (Home to the writing of Celli the Happy Go Lucky Celaphopod, Eddie Takosori, Fritz Heinmillerstein, Morgan Feldstone, Kevin Stillwater, and of course, me, your host, Brett Paufler)

paper.js official site = http://www.paperJS.org


© Copyright 2013 Brett Paufler