PaperJS
Instancing/Scoping:
When working with PaperScript, each script is run in its own scope, a PaperScope object. The global paper object through which the library is exposed is also such a PaperScope object. It helps to think of these scopes as execution contexts.
Scopes were introduced as a way to have separate PaperScript contexts on a page with many examples, each having its own view and project, without seeing each other but still sharing the library code. They could be seen as sandboxed 'plugins' with shared code.
Pros:
- Don't need to worry about variable names so much
- Access to built in methods with no hassle
- No file conflicts
Cons:
- Hard to modularize your files
- Files get huge
- Splitting files from HTML leads to cross origin issues and requires you run them on localhost.
Alternatives:
You can run it with basic JS, though you do lose a little bit of functionality and you need to be a little more verbose with your commands.
For instance:
var path = new Path();
// Give the stroke a color
path.strokeColor = 'black';
var start = new Point(100, 100);
// Move to start and draw a line from there
path.moveTo(start);
// Note the plus operator on Point objects.
// PaperScript does that for us, and much more!
path.lineTo(start + [ 100, -50 ]);
In vanilla JS becomes:
window.onload = function(){
// Assign canvas
var canvas = document.querySelector('canvas');
paper.setup(canvas);
// Preceeding paper classes/functions with the `paper` object.
var path = new paper.Path();
path.strokeColor = 'black';
var start = new paper.Point(100, 100);
path.moveTo(start);
// unembedded version - .add instead of '+'.
path.lineTo( start.add([ 100, -50 ]) );
};
The tradeoff is, you don't need to be as explicit with your source files, and you don't need to run a server to cope with cross origins and you end up with more control -- but you end up typing and troubleshooting a bit more, and online examples aren't as simple to follow. More info about running pure JS.
In this instance, I'm going with a separate JS file, sourced to paper.js, which means I get:
- A JS file to work in, rather than a giant HTML file with my code run in
<script></script>tags - All the out of the box functionality But I will also need to run it on localhost to deal with cross origin issues.
python -m SimpleHTTPServer
Begin!
Points:
We'll start by adding a basic line.
// Define a new path, and give it a colour so we can see it.
var path = new Path({
strokeColor: 'black'
});
// Create a point on the canvas
var start = new Point( 100, 100 );
// Tell the 'path' variable (our new path) to go to our start point
path.moveTo( start );
// Draw a line from our start to this new point
path.lineTo( 200, 200 );

From here, we can start adding as many points as we want. All we need to do is make some new points, and add them to our path variable:
path.add( new Point( 200, 200 ) );
path.add( new Point( 100, 200 ) );
path.add( new Point( 100, 100 ) );
To actually see our points on a shape, we can add the line path.fullySelected = true; to our code, which will give us a sort of wireframe for our path variable.

Now we've made a basic shape, but it's not actually connected, our first and last points are overlapping, but they aren't actually connected. If we want to actually lock it together, we can say:
path.closed = true.
We can further customise our new shape by changing things like the strokeWidth (the lines that we drew), the fill colour, and a bunch of other stuff.
path.fillColor = "rgba( 155, 0, 155, 0.2 )";
path.strokeWidth = 2;

There we go, we've built a basic shape.
Shapes:
As you can imagine, drawing all your shapes out with every single line can get tedious. Luckily, there are helpers for some of the more common shapes you might want to draw.
To do the same shape as we did above, we can use the Rectangle helper.
var topLeft = new Point( 250, 100 );
var bottomRight = new Point( 350, 200 );
// Define our rectangle's corners
var rectangle = new Rectangle(
topLeft,
bottomRight
);
// Make a new path to put it on the page
var rect = new Path.Rectangle( rectangle );
// Give it a colour so we can see it:
rect.strokeColor = 'red';

In fact, we get quite a few premade shapes to play with. We can also make basic shapes like circles, which take slightly different parameters, a circle takes a center point (x, y) and a radius.
var circ = new Path.Circle( new Point( 300, 300 ) , 50 );
circ.strokeColor = 'blue';

This places a circle centered on 300/300 with a radius of 50px. We can also pass in dynamic values to position it relative to another element. For instance:
var topLeft = new Point( 250, 100 );
var width = 100;
var height = 100;
// Bottom right now calculates with given widths in addition to our top left's co-ordinates.
var bottomRight = new Point( topLeft.x + width , topLeft.y + height );
// Define our rectangle's corners
var rectangle = new Rectangle(
topLeft,
bottomRight
);
// Make a new path to put it on the page
var rect = new Path.Rectangle( rectangle );
// Give it a colour so we can see it:
rect.strokeColor = 'red';
rect.center = new Point( ( bottomRight.x - width / 2 ) , ( bottomRight.y - height / 2 ) );
var circ = new Path.Circle( rect.center, 50 );
circ.strokeColor = 'blue';

By setting a center point on our rectangle, we now have a circle that renders in the middle of the rectangle, no matter where we go.
Scales and transformations
Two super powerful little functions are the item.clone() and item.scale() functions. Combining just these 2 and a for loop, you can get some pretty cool effects.
// Clone a circle, scale each clone down, and add it to the page.
for( var i = 5; i > 0; i-- ){
var copy = circ.clone();
copy.scale( i / 5 );
}

In fact, from here on, combining basically any of these methods with loops can get you really cool results.
// Clone our original box, rotate it by 22 degree increments.
for( var i = 0; i < 4; i++ ){
var copy = path.clone();
copy.rotate( i * 22 );
}

Cloning and slightly rotating our original shape gets us some pretty bizarre effects, but we can go much further.
Every shape has a boundary box, or bounding box. It is the measurement of the outermost points on any given shape. We can also exploit these as, say, centres for a bunch of looped shapes -- then use a base shape like a circle, and place every clone along it's radius.
// Our shape to use as a guide
var circlePath = new Path.Circle( new Point( 250, 350 ), 25 );
circlePath.strokeColor = "rgba( 0, 255, 0, 0.5 )";
// The number of shapes we want to make
var numCircles = 50;
// The angle each shape needs to be placed at to get us the radius of a circle
var angle = 360 / numCircles;
for( var i = 1; i < numCircles; i++ ){
var copy = circlePath.clone();
// debugger
copy.rotate( angle * i , circlePath.bounds.topLeft );
// Our hue method lets us increment the colour very slightly every time the loop runs
copy.strokeColor.hue += i * 7;
}

Mixing and matching any, or all of these effects can get you some really mindblowing stuff.
Animations:
Animations are really straightforward. In fact, the examples above have gotten you most of the way there.
Instead of a loop, we can throw a function called onFrame inside our program, which will be called every time the canvas renders a new frame (typically 60fps).
This function is almost identical to a single frame of our for loop, we increment a property by a tiny amount - except this time we save it in the object.
// Our shape to use as a guide
// Create a path, but don't give it a stroke.
// We want to use its points, but not see it.
var animationRing = new Path.Circle( new Point( 250, 350 ), 25 );
// Create a new ring, following the same path as our loop.
var ringFrame = animationRing.clone();
// Set our starting point for the animation
var startPoint = animationRing.bounds.topLeft;
// Give it colour so we can see it.
ringFrame.fillColor = "rgba( 0, 255, 0, 0.2 )";
// Every time paper re-renders, call this function, and rotate our frame as in the for loop.
var onFrame = function( event ){
ringFrame.rotate( 5, startPoint );
ringFrame.fillColor.hue += 5;
};

Mouse interaction
Just like the onFrame function given to us by default, we also have various other event handlers, including mouse and keyboard events.
The most straightforward example here would be a simple line drawing function:
// Start a new path to add points to.
var mousePath = new Path();
mousePath.strokeColor = 'black';
var onMouseDown = function( event ){
// Add this click co-ordinate to our new path
mousePath.add( new Point( event.point ) );
};
