Paper.js Tips
PinePaper uses Paper.js for canvas graphics. Here are key concepts.
Coordinate System
Paper.js uses a standard canvas coordinate system:
- Origin (0, 0) at top-left
- X increases rightward
- Y increases downward
// Center of canvas
const center = view.center;
// Canvas dimensions
const width = view.size.width;
const height = view.size.height;
Points
// Create points
const p1 = new paper.Point(100, 200);
const p2 = new paper.Point({ x: 100, y: 200 });
// Point operations
const sum = p1.add(p2);
const diff = p1.subtract(p2);
const scaled = p1.multiply(2);
const distance = p1.getDistance(p2);
const angle = p1.getAngle(p2);
Paths
// Create path
const path = new paper.Path();
path.add(new paper.Point(100, 100));
path.add(new paper.Point(200, 150));
path.strokeColor = 'black';
// Path shapes
const circle = new paper.Path.Circle({ center: [200, 200], radius: 50 });
const rect = new paper.Path.Rectangle({ point: [100, 100], size: [200, 150] });
const star = new paper.Path.Star({ center: [300, 300], points: 5, radius1: 25, radius2: 50 });
Styling
item.fillColor = '#3b82f6';
item.strokeColor = '#1e40af';
item.strokeWidth = 2;
item.opacity = 0.8;
item.shadowColor = 'rgba(0,0,0,0.3)';
item.shadowBlur = 10;
item.shadowOffset = new paper.Point(5, 5);
Transformations
// Position
item.position = new paper.Point(400, 300);
item.position.x += 50;
// Rotation (degrees)
item.rotation = 45;
item.rotate(30); // Add 30 degrees
// Scale
item.scale(1.5); // Uniform
item.scale(2, 0.5); // Non-uniform
Layers and Groups
// Layers
const bgLayer = new paper.Layer({ name: 'background' });
const fgLayer = new paper.Layer({ name: 'foreground' });
fgLayer.activate(); // New items go here
// Groups
const group = new paper.Group();
group.addChild(circle);
group.addChild(text);
group.position = [400, 300]; // Move all children
Hit Testing
// Find item at point
const hitResult = paper.project.hitTest(event.point);
if (hitResult && hitResult.item) {
console.log('Hit:', hitResult.item);
}
// Hit test options
const options = {
fill: true,
stroke: true,
tolerance: 5
};
Events
// Mouse events on items
circle.onMouseDown = (event) => {
console.log('Clicked at', event.point);
};
// Frame events (animation)
view.onFrame = (event) => {
// event.delta - time since last frame
// event.time - total elapsed time
// event.count - frame count
item.rotate(event.delta * 30); // 30 deg/sec
};
Text
const text = new paper.PointText({
point: [400, 300],
content: 'Hello World',
fontSize: 24,
fontFamily: 'Arial',
fillColor: '#000000',
justification: 'center'
});
Rasters (Images)
const raster = new paper.Raster('image.png');
raster.position = view.center;
raster.onLoad = () => {
raster.scale(0.5);
};
Serialization
// Export to JSON
const json = item.exportJSON();
// Import from JSON
const restored = new paper.Path();
restored.importJSON(json);
// Export project
const projectJSON = paper.project.exportJSON();
Performance Tips
- Use
item.visible = falseinstead of removing items temporarily - Group related items to transform together
- Use
path.simplify()to reduce complex paths - Avoid creating items in
onFrame- reuse existing items - Use
raster.smoothing = falsefor pixel art