mirror of
https://github.com/scratchfoundation/paper.js.git
synced 2025-01-05 20:32:00 -05:00
Merge pull request #46 from jonathanpuckey/master
Node.js integration for exporting images and frame sequences.
This commit is contained in:
commit
896afefa99
9 changed files with 543 additions and 5 deletions
49
examples/Node.js/AnimatedStar.js
Normal file
49
examples/Node.js/AnimatedStar.js
Normal file
|
@ -0,0 +1,49 @@
|
|||
var paper = require('paper');
|
||||
paper.setup(new paper.Canvas(1024, 768));
|
||||
|
||||
var layer = paper.project.activeLayer;
|
||||
|
||||
var values = {
|
||||
count: 34,
|
||||
points: 32
|
||||
};
|
||||
|
||||
initialize();
|
||||
|
||||
paper.view.exportFrames({
|
||||
amount: 100,
|
||||
directory: __dirname,
|
||||
onComplete: function() {
|
||||
console.log('Done exporting.');
|
||||
},
|
||||
onProgress: function(event) {
|
||||
console.log(event.percentage + '% complete, frame took: ' + event.delta);
|
||||
}
|
||||
});
|
||||
|
||||
function initialize() {
|
||||
for (var i = 0; i < values.count; i++) {
|
||||
var offset = new paper.Point(20 + 10 * i, 0);
|
||||
var path = new paper.Path();
|
||||
path.fillColor = i % 2 ? 'red' : 'black';
|
||||
path.closed = true;
|
||||
|
||||
var l = offset.length;
|
||||
for (var j = 0; j < values.points * 2; j++) {
|
||||
offset.angle += 360 / values.points;
|
||||
var vector = offset.normalize(l * (j % 2 ? 0.1 : -0.1));
|
||||
path.add(offset.add(vector));
|
||||
}
|
||||
path.smooth();
|
||||
layer.insertChild(0, path);
|
||||
}
|
||||
layer.fitBounds(paper.view.bounds);
|
||||
}
|
||||
|
||||
paper.view.onFrame = function(event) {
|
||||
for (var i = 0, l = layer.children.length; i < l; i++) {
|
||||
var item = layer.children[i];
|
||||
var angle = (values.count - i) * Math.sin(event.count / 128) / 10;
|
||||
item.rotate(angle);
|
||||
}
|
||||
}
|
280
examples/Node.js/Tadpoles.pjs
Normal file
280
examples/Node.js/Tadpoles.pjs
Normal file
|
@ -0,0 +1,280 @@
|
|||
paper.setup(new Canvas(1024, 768));
|
||||
|
||||
// Adapted from Flocking Processing example by Daniel Schiffman:
|
||||
// http://processing.org/learning/topics/flocking.html
|
||||
|
||||
project.currentStyle = {
|
||||
strokeColor: 'white',
|
||||
strokeWidth: 2,
|
||||
strokeCap: 'round'
|
||||
};
|
||||
|
||||
new Path.Rectangle(view.bounds).fillColor = 'black';
|
||||
|
||||
var head = new Path.Oval([0, 0], [13, 8]);
|
||||
head.fillColor = 'white';
|
||||
head.strokeColor = null;
|
||||
var headSymbol = new Symbol(head);
|
||||
|
||||
var size = view.size;
|
||||
|
||||
var Boid = Base.extend({
|
||||
initialize: function(position, maxSpeed, maxForce) {
|
||||
var strength = Math.random() * 0.5;
|
||||
this.acc = new Point(0, 0);
|
||||
this.vel = Point.random() * 2 - 1;
|
||||
this.loc = position.clone();
|
||||
this.r = 30;
|
||||
this.maxSpeed = maxSpeed + strength;
|
||||
this.maxForce = maxForce + strength;
|
||||
this.head = headSymbol.place();
|
||||
this.path = new Path();
|
||||
this.shortPath = new Path();
|
||||
this.shortPath.strokeWidth = 4;
|
||||
for (var i = 0, l = strength * 10 + 10; i < l; i++) {
|
||||
this.path.add(this.loc);
|
||||
if (i < 3)
|
||||
this.shortPath.add(this.loc);
|
||||
}
|
||||
|
||||
this.firstSegment = this.path.segments[0];
|
||||
this.count = 0;
|
||||
this.lastRot = 0;
|
||||
},
|
||||
|
||||
run: function(boids) {
|
||||
this.lastLoc = this.loc.clone();
|
||||
if (!groupTogether) {
|
||||
this.flock(boids);
|
||||
} else {
|
||||
this.align(boids);
|
||||
}
|
||||
this.borders();
|
||||
|
||||
this.update();
|
||||
this.firstSegment.point = this.loc;
|
||||
var lastPoint = this.firstSegment.point;
|
||||
var lastVector = this.loc - this.lastLoc;
|
||||
var segments = this.path.segments;
|
||||
for (var i = 1, l = segments.length; i < l; i++) {
|
||||
var segment = segments[i];
|
||||
var vector = lastPoint - segment.point;
|
||||
this.count += this.vel.length * 10;
|
||||
var rotLength = Math.sin((this.count + i * 3) / 300);
|
||||
var rotated = lastVector.rotate(90).normalize(rotLength);
|
||||
lastPoint += lastVector.normalize(-5 - this.vel.length / 3);
|
||||
segment.point = lastPoint;
|
||||
segment.point += rotated;
|
||||
lastVector = vector;
|
||||
}
|
||||
this.path.smooth();
|
||||
this.head.position = this.loc;
|
||||
var vector = this.loc - this.lastLoc;
|
||||
var rot = vector.angle;
|
||||
this.head.rotate(rot - this.lastRot);
|
||||
this.lastRot = rot;
|
||||
|
||||
var shortSegments = this.shortPath.segments;
|
||||
for (var i = 0; i < 3; i++)
|
||||
shortSegments[i] = segments[i].clone();
|
||||
},
|
||||
|
||||
// We accumulate a new acceleration each time based on three rules
|
||||
flock: function(boids) {
|
||||
var sep = this.separate(boids) * 3;
|
||||
var ali = this.align(boids);
|
||||
var coh = this.cohesion(boids);
|
||||
this.acc += sep + ali + coh;
|
||||
},
|
||||
|
||||
update: function() {
|
||||
// Update velocity
|
||||
this.vel += this.acc;
|
||||
// Limit speed (vector#limit?)
|
||||
this.vel.length = Math.min(this.maxSpeed, this.vel.length);
|
||||
this.loc += this.vel;
|
||||
// Reset acceleration to 0 each cycle
|
||||
this.acc.length = 0;
|
||||
},
|
||||
|
||||
seek: function(target) {
|
||||
this.acc += this.steer(target, false);
|
||||
},
|
||||
|
||||
arrive: function(target) {
|
||||
this.acc += this.steer(target, true);
|
||||
},
|
||||
|
||||
// A method that calculates a steering vector towards a target
|
||||
// Takes a second argument, if true, it slows down as it approaches
|
||||
// the target
|
||||
steer: function(target, slowdown) {
|
||||
var steer,
|
||||
desired = target - this.loc,
|
||||
d = desired.length;
|
||||
if (d > 0) {
|
||||
// Two options for desired vector magnitude
|
||||
// (1 -- based on distance, 2 -- maxSpeed)
|
||||
if (slowdown && d < 100) {
|
||||
// // This damping is somewhat arbitrary:
|
||||
desired.length = this.maxSpeed * (d / 100);
|
||||
} else {
|
||||
desired.length = this.maxSpeed;
|
||||
}
|
||||
steer = desired - this.vel;
|
||||
steer.length = Math.min(this.maxForce, steer.length);
|
||||
} else {
|
||||
steer = new Point(0, 0);
|
||||
}
|
||||
return steer;
|
||||
},
|
||||
|
||||
borders: function() {
|
||||
var loc = this.loc;
|
||||
var r = this.r;
|
||||
var oldLoc = this.loc.clone();
|
||||
var width = size.width;
|
||||
var height = size.height;
|
||||
if (loc.x < -r) loc.x = width + r;
|
||||
if (loc.y < -r) loc.y = height + r;
|
||||
if (loc.x > width + r) loc.x = -r;
|
||||
if (loc.y > height + r) loc.y = -r;
|
||||
var vector = this.loc - oldLoc;
|
||||
if (!vector.isZero())
|
||||
this.path.position += vector;
|
||||
},
|
||||
|
||||
separate: function(boids) {
|
||||
var desiredSeperation = 60;
|
||||
var steer = new Point(0, 0);
|
||||
var count = 0;
|
||||
// For every boid in the system, check if it's too close
|
||||
for (var i = 0, l = boids.length; i < l; i++) {
|
||||
var other = boids[i];
|
||||
var d = other.loc.getDistance(this.loc);
|
||||
if (d > 0 && d < desiredSeperation) {
|
||||
// Calculate vector pointing away from neighbor
|
||||
var diff = this.loc - other.loc;
|
||||
steer += diff.normalize(1 / d);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
// Average -- divide by how many
|
||||
if (count > 0)
|
||||
steer /= count;
|
||||
if (steer.length > 0) {
|
||||
// Implement Reynolds: Steering = Desired - Velocity
|
||||
steer.length = this.maxSpeed;
|
||||
steer -= this.vel;
|
||||
steer.length = Math.min(steer.length, this.maxForce);
|
||||
}
|
||||
return steer;
|
||||
},
|
||||
|
||||
// Alignment
|
||||
// For every nearby boid in the system, calculate the average velocity
|
||||
align: function(boids) {
|
||||
var neighborDist = 25;
|
||||
var steer = new Point(0, 0);
|
||||
var count = 0;
|
||||
var nearest = 999;
|
||||
var closestPoint;
|
||||
for (var i = 0, l = boids.length; i < l; i++) {
|
||||
var other = boids[i];
|
||||
var d = this.loc.getDistance(other.loc);
|
||||
if (d > 0 && d < nearest) {
|
||||
closestPoint = other.loc;
|
||||
nearest = d;
|
||||
}
|
||||
if (d > 0 && d < neighborDist) {
|
||||
steer += other.vel;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (count > 0)
|
||||
steer /= count;
|
||||
if (steer.length > 0) {
|
||||
// Implement Reynolds: Steering = Desired - Velocity
|
||||
steer.length = this.maxSpeed;
|
||||
steer -= this.vel;
|
||||
steer.length = Math.min(steer.length, this.maxForce);
|
||||
}
|
||||
return steer;
|
||||
},
|
||||
|
||||
// Cohesion
|
||||
// For the average location (i.e. center) of all nearby boids,
|
||||
// calculate steering vector towards that location
|
||||
cohesion: function(boids) {
|
||||
var neighborDist = 100;
|
||||
var sum = new Point(0, 0);
|
||||
var count = 0;
|
||||
for (var i = 0, l = boids.length; i < l; i++) {
|
||||
var other = boids[i];
|
||||
var d = this.loc.getDistance(other.loc);
|
||||
if (d > 0 && d < neighborDist) {
|
||||
sum += other.loc; // Add location
|
||||
count++;
|
||||
}
|
||||
}
|
||||
if (count > 0) {
|
||||
sum /= count;
|
||||
// Steer towards the location
|
||||
return this.steer(sum, false);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
});
|
||||
|
||||
var heartPath = new Path([
|
||||
[[514.6962890625, 624.703125], [7.0966796875, -26.3369140625], [-7.10205078125, -27.0244140625]],
|
||||
[[484.29052734375, 548.6025390625], [13.16845703125, 23.7060546875], [-13.173828125, -23.70703125]],
|
||||
[[407.84619140625, 438.14453125], [37.79296875, 49.935546875], [-27.71630859375, -36.6435546875]],
|
||||
[[356.654296875, 368.400390625], [6.41015625, 9.8505859375], [-10.53759765625, -16.02978515625]],
|
||||
[[333.80712890625, 324.25146484375], [4.69189453125, 13.3994140625], [-4.697265625, -13.39892578125]],
|
||||
[[326.76416015625, 283.53857421875], [0, 13.74267578125], [0, -25.42431640625]],
|
||||
[[352.18798828125, 219.634765625], [-16.95263671875, 17.17822265625], [16.94775390625, -17.1787109375]],
|
||||
[[415.0615234375, 193.8671875], [-24.96826171875, 0], [25.19287109375, 0]],
|
||||
[[480.68310546875, 220.66552734375], [-18.552734375, -17.86572265625], [13.96826171875, 13.28662109375]],
|
||||
[[514.6962890625, 280.10302734375], [-8.70703125, -26.3369140625], [7.55859375, -25.88037109375]],
|
||||
[[546.6484375, 221.0087890625], [-13.7431640625, 13.517578125], [19.0087890625, -18.32177734375]],
|
||||
[[612.61328125, 193.5234375], [-24.9677734375, 0], [24.7373046875, 0]],
|
||||
[[675.486328125, 219.119140625], [-17.177734375, -17.06005859375], [17.1787109375, 17.06591796875]],
|
||||
[[701.2548828125, 280.10302734375], [0, -23.58837890625], [0, 20.61376953125]],
|
||||
[[686.1376953125, 344.52197265625], [10.076171875, -22.33203125], [-10.08203125, 22.33203125]],
|
||||
[[627.73046875, 432.3046875], [28.8603515625, -36.1875], [-37.5673828125, 47.412109375]],
|
||||
[[545.6171875, 549.1171875], [17.1787109375, -30.458984375], [-13.517578125, 24.0498046875]]
|
||||
]);
|
||||
heartPath.closed = true;
|
||||
heartPath.position = view.center;
|
||||
heartPath.strokeColor = null;
|
||||
heartPath.scale(1.5);
|
||||
|
||||
var groupTogether = false;
|
||||
var pathLength = heartPath.length;
|
||||
var mouseDown = false;
|
||||
var boids = [];
|
||||
|
||||
// Add the boids:
|
||||
for (var i = 0; i < 300; i++) {
|
||||
var position = view.center;
|
||||
boids.push(new Boid(position, 10, 0.05));
|
||||
}
|
||||
|
||||
function onFrame(event) {
|
||||
for (var i = 0, l = boids.length; i < l; i++) {
|
||||
if (groupTogether) {
|
||||
var length = ((i + event.count / 30) % l) / l * pathLength;
|
||||
var point = heartPath.getPointAt(length);
|
||||
boids[i].arrive(point);
|
||||
}
|
||||
boids[i].run(boids);
|
||||
}
|
||||
}
|
||||
|
||||
// Reposition the heart path whenever the window is resized:
|
||||
function onResize(event) {
|
||||
size = view.size;
|
||||
heartPath.position = view.center;
|
||||
}
|
12
examples/Node.js/exportTadpoles.js
Normal file
12
examples/Node.js/exportTadpoles.js
Normal file
|
@ -0,0 +1,12 @@
|
|||
require('../../node.js/');
|
||||
var paper = require('./Tadpoles');
|
||||
paper.view.exportFrames({
|
||||
amount: 400,
|
||||
directory: __dirname,
|
||||
onComplete: function() {
|
||||
console.log('Done exporting.');
|
||||
},
|
||||
onProgress: function(event) {
|
||||
console.log(event.percentage + '% complete, frame took: ' + event.delta);
|
||||
}
|
||||
});
|
1
index.js
Normal file
1
index.js
Normal file
|
@ -0,0 +1 @@
|
|||
module.exports = require('./src/loadNode.js');
|
54
node.js/index.js
Normal file
54
node.js/index.js
Normal file
|
@ -0,0 +1,54 @@
|
|||
var fs = require('fs'),
|
||||
vm = require('vm'),
|
||||
path = require('path');
|
||||
|
||||
__dirname = path.resolve(__dirname, '../src/');
|
||||
|
||||
// Create the context within which we will run the source files:
|
||||
var context = vm.createContext({
|
||||
options: {
|
||||
server: true,
|
||||
version: 'dev'
|
||||
},
|
||||
// Node Canvas library: https://github.com/learnboost/node-canvas
|
||||
Canvas: require('canvas'),
|
||||
// Copy over global variables:
|
||||
console: console,
|
||||
require: require,
|
||||
__dirname: __dirname,
|
||||
__filename: __filename,
|
||||
// Used to load and run source files within the same context:
|
||||
include: function(uri) {
|
||||
var source = fs.readFileSync(path.resolve(__dirname, uri), 'utf8');
|
||||
// For relative includes, we save the current directory and then
|
||||
// add the uri directory to __dirname:
|
||||
var oldDirname = __dirname;
|
||||
__dirname = path.resolve(__dirname, path.dirname(uri));
|
||||
vm.runInContext(source, context, uri);
|
||||
__dirname = oldDirname;
|
||||
}
|
||||
});
|
||||
|
||||
// Load Paper.js library files:
|
||||
context.include('paper.js');
|
||||
|
||||
context.Base.each(context, function(val, key) {
|
||||
if (val && val.prototype instanceof context.Base) {
|
||||
val._name = key;
|
||||
// Export all classes through PaperScope:
|
||||
context.PaperScope.prototype[key] = val;
|
||||
}
|
||||
});
|
||||
context.PaperScope.prototype['Canvas'] = context.Canvas;
|
||||
|
||||
require.extensions['.pjs'] = function(module, uri) {
|
||||
var source = context.PaperScript.compile(fs.readFileSync(uri, 'utf8'));
|
||||
var envVars = 'var __dirname = \'' + path.dirname(uri) + '\';' +
|
||||
'var __filename = \'' + uri + '\';';
|
||||
vm.runInContext(envVars, context);
|
||||
var scope = new context.PaperScope();
|
||||
context.PaperScript.evaluate(source, scope);
|
||||
module.exports = scope;
|
||||
};
|
||||
|
||||
module.exports = new context.PaperScope();
|
20
package.json
Normal file
20
package.json
Normal file
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"name": "paper",
|
||||
"description": "Vector graphics scripting framework",
|
||||
"version": "0.2.1",
|
||||
"contributors": [{
|
||||
"name" : "Jürg Lehni",
|
||||
"url" : "http://lehni.org"
|
||||
}, {
|
||||
"name" : "Jonathan Puckey",
|
||||
"url" : "http://jonathanpuckey.com"
|
||||
}],
|
||||
"homepage": "http://paperjs.org",
|
||||
"keywords": ["canvas", "graphic", "graphics", "vector", "paper.js"],
|
||||
"repository": "git://github.com/paperjs/paper.js/",
|
||||
"dependencies": {
|
||||
"canvas": "0.7.0"
|
||||
},
|
||||
"engines": { "node": ">= 0.4.0 && < 0.6.0" },
|
||||
"main": "./node.js/index.js"
|
||||
}
|
|
@ -105,8 +105,11 @@ var paper = new function() {
|
|||
/*#*/ if (options.browser) {
|
||||
/*#*/ include('browser/DomElement.js');
|
||||
/*#*/ include('browser/DomEvent.js');
|
||||
/*#*/ } // options.browser
|
||||
|
||||
/*#*/ include('ui/View.js');
|
||||
|
||||
/*#*/ if (options.browser) {
|
||||
/*#*/ include('ui/Event.js');
|
||||
/*#*/ include('ui/KeyEvent.js');
|
||||
/*#*/ include('ui/Key.js');
|
||||
|
@ -121,7 +124,9 @@ var paper = new function() {
|
|||
|
||||
/*#*/ include('core/PaperScript.js');
|
||||
|
||||
/*#*/ if (options.browser) {
|
||||
/*#*/ include('core/initialize.js');
|
||||
/*#*/ } // options.browser
|
||||
|
||||
/*#*/ if (options.version != 'dev') {
|
||||
// Finally inject the classes set on 'this' into the PaperScope class and create
|
||||
|
@ -133,4 +138,4 @@ var paper = new function() {
|
|||
this.enumerable = true;
|
||||
return new (PaperScope.inject(this));
|
||||
/*#*/ } // options.version != 'dev'
|
||||
};
|
||||
};
|
122
src/ui/View.js
122
src/ui/View.js
|
@ -36,6 +36,26 @@ var View = this.View = PaperScopeItem.extend(/** @lends View# */{
|
|||
this.base();
|
||||
// Handle canvas argument
|
||||
var size;
|
||||
|
||||
/*#*/ if (options.server) {
|
||||
if (canvas && canvas instanceof Canvas) {
|
||||
this._canvas = canvas;
|
||||
size = Size.create(canvas.width, canvas.height);
|
||||
} else {
|
||||
// 2nd argument onwards could be view size, otherwise use default:
|
||||
size = Size.read(arguments, 1);
|
||||
if (size.isZero())
|
||||
size = new Size(1024, 768);
|
||||
this._canvas = CanvasProvider.getCanvas(size);
|
||||
}
|
||||
|
||||
// Generate an id for this view / canvas if it does not have one
|
||||
this._id = this._canvas.id;
|
||||
if (this._id == null)
|
||||
this._canvas.id = this._id = 'canvas-' + View._id++;
|
||||
/*#*/ } // options.server
|
||||
|
||||
/*#*/ if (options.browser) {
|
||||
if (typeof canvas === 'string')
|
||||
canvas = document.getElementById(canvas);
|
||||
if (canvas instanceof HTMLCanvasElement) {
|
||||
|
@ -92,6 +112,8 @@ var View = this.View = PaperScopeItem.extend(/** @lends View# */{
|
|||
this._id = this._canvas.getAttribute('id');
|
||||
if (this._id == null)
|
||||
this._canvas.setAttribute('id', this._id = 'canvas-' + View._id++);
|
||||
/*#*/ } // options.browser
|
||||
|
||||
// Link this id to our view
|
||||
View._views[this._id] = this;
|
||||
this._viewSize = LinkedSize.create(this, 'setViewSize',
|
||||
|
@ -99,11 +121,15 @@ var View = this.View = PaperScopeItem.extend(/** @lends View# */{
|
|||
this._context = this._canvas.getContext('2d');
|
||||
this._matrix = new Matrix();
|
||||
this._zoom = 1;
|
||||
|
||||
/*#*/ if (options.browser) {
|
||||
this._events = this._createEvents();
|
||||
DomEvent.add(this._canvas, this._events);
|
||||
// Make sure the first view is focused for keyboard input straight away
|
||||
if (!View._focused)
|
||||
View._focused = this;
|
||||
/*#*/ } // options.browser
|
||||
|
||||
// As soon as a new view is added we need to mark the redraw as not
|
||||
// motified, so the next call loops through all the views again.
|
||||
this._scope._redrawNotified = false;
|
||||
|
@ -355,6 +381,7 @@ var View = this.View = PaperScopeItem.extend(/** @lends View# */{
|
|||
delete this._onFrameCallback;
|
||||
return;
|
||||
}
|
||||
/*#*/ if (options.browser) {
|
||||
var that = this,
|
||||
requested = false,
|
||||
before,
|
||||
|
@ -388,8 +415,11 @@ var View = this.View = PaperScopeItem.extend(/** @lends View# */{
|
|||
// of onFrame calls.
|
||||
if (!requested)
|
||||
this._onFrameCallback();
|
||||
/*#*/ } // options.browser
|
||||
},
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Handler function that is called whenever a view is resized.
|
||||
*
|
||||
|
@ -408,7 +438,13 @@ var View = this.View = PaperScopeItem.extend(/** @lends View# */{
|
|||
* @type Function
|
||||
*/
|
||||
onResize: null
|
||||
}, {
|
||||
statics: {
|
||||
_views: {},
|
||||
_id: 0
|
||||
}
|
||||
}, new function() { // Injection scope for mouse handlers
|
||||
/*#*/ if (options.browser) {
|
||||
var tool,
|
||||
timer,
|
||||
curPoint,
|
||||
|
@ -535,8 +571,6 @@ var View = this.View = PaperScopeItem.extend(/** @lends View# */{
|
|||
},
|
||||
|
||||
statics: {
|
||||
_views: {},
|
||||
_id: 0,
|
||||
|
||||
/**
|
||||
* Loops through all scopes and their views and sets the focus on
|
||||
|
@ -545,4 +579,88 @@ var View = this.View = PaperScopeItem.extend(/** @lends View# */{
|
|||
updateFocus: updateFocus
|
||||
}
|
||||
};
|
||||
/*#*/ } // options.browser
|
||||
}, new function() {
|
||||
/*#*/ if (options.server) {
|
||||
var fs = require('fs'),
|
||||
path = require('path');
|
||||
// Utility function that converts a number to a string with
|
||||
// x amount of padded 0 digits:
|
||||
function toPaddedString(number, length) {
|
||||
var str = number.toString(10);
|
||||
for (var i = 0, l = length - str.length; i < l; i++) {
|
||||
str = '0' + str;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
return {
|
||||
// DOCS: View#exportFrames(param);
|
||||
exportFrames: function(param) {
|
||||
param = Base.merge({
|
||||
fps: 30,
|
||||
prefix: 'frame-',
|
||||
amount: 1
|
||||
}, param);
|
||||
if (!param.directory) {
|
||||
throw new Error('Missing param.directory');
|
||||
}
|
||||
var view = this,
|
||||
count = 0,
|
||||
frameDuration = 1 / param.fps,
|
||||
lastTime = startTime = Date.now();
|
||||
|
||||
// Start exporting frames by exporting the first frame:
|
||||
exportFrame(param);
|
||||
|
||||
function exportFrame(param) {
|
||||
count++;
|
||||
var filename = param.prefix + toPaddedString(count, 6) + '.png',
|
||||
uri = param.directory + '/' + filename;
|
||||
var out = view.exportImage(uri, function() {
|
||||
// When the file has been closed, export the next fame:
|
||||
var then = Date.now();
|
||||
if (param.onProgress) {
|
||||
param.onProgress({
|
||||
count: count,
|
||||
amount: param.amount,
|
||||
percentage: Math.round(count / param.amount
|
||||
* 10000) / 100,
|
||||
time: then - startTime,
|
||||
delta: then - lastTime
|
||||
});
|
||||
}
|
||||
lastTime = then;
|
||||
if (count < param.amount) {
|
||||
exportFrame(param);
|
||||
} else {
|
||||
// Call onComplete handler when finished:
|
||||
if (param.onComplete) {
|
||||
param.onComplete();
|
||||
}
|
||||
}
|
||||
});
|
||||
if (view.onFrame) {
|
||||
view.onFrame({
|
||||
delta: frameDuration,
|
||||
time: frameDuration * count,
|
||||
count: count
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
// DOCS: View#exportImage(uri, callback);
|
||||
exportImage: function(uri, callback) {
|
||||
this.draw();
|
||||
// TODO: is it necessary to resolve the path?
|
||||
var out = fs.createWriteStream(path.resolve(__dirname, uri)),
|
||||
stream = this._canvas.createPNGStream();
|
||||
// Pipe the png stream to the write stream:
|
||||
stream.pipe(out);
|
||||
if (callback) {
|
||||
out.on('close', callback);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
};
|
||||
/*#*/ } // options.server
|
||||
});
|
||||
|
|
|
@ -43,8 +43,7 @@ var CanvasProvider = {
|
|||
canvas.height = size.height;
|
||||
return canvas;
|
||||
/*#*/ } else { // !options.browser
|
||||
// Only rhino-canvas for now:
|
||||
return new Image(size.width, size.height);
|
||||
return new Canvas(size.width, size.height);
|
||||
/*#*/ } // !options.browser
|
||||
}
|
||||
},
|
||||
|
|
Loading…
Reference in a new issue