Comparing version 4.3.0 to 5.1.0
@@ -12,19 +12,27 @@ const canvideo = require("../../index"); | ||
.addKeyframe(new canvideo.Keyframe(0) | ||
.addShape(new canvideo.Square(0, 0, 800) | ||
.addShape(new canvideo.Square(0, 0, 400) | ||
.fill("white") | ||
) | ||
.addShape(new canvideo.Rectangle(0, 0, 200, 200) | ||
.addShape(new canvideo.Square(0, 0, 200) | ||
.fill("green") | ||
) | ||
) | ||
.addShape(new canvideo.Square(0, 0, 100) | ||
.fill("blue") | ||
) | ||
.addShape(new canvideo.Square(200, 200, 100) | ||
.fill("red") | ||
) | ||
.addShape(new canvideo.Square(200, 150, 50) | ||
.fill("purple") | ||
) | ||
); | ||
video.camera.animate(0, 2, new canvideo.Animation({ zoom: 1 }, { zoom: 0.5 })); | ||
video.camera.animate(0, 2, new canvideo.ZoomAnimation({ scaleX: 1, scaleY: 1 }, { scaleX: 2, scaleY: 2 }, 200, 200).reverse()); | ||
video.on("done", () => { | ||
console.log("done") | ||
}); | ||
video.on("error", () => { | ||
console.log("error!"); | ||
}); | ||
video.export(path.join(__dirname, "./res/output.mp4")); | ||
video.export(path.join(__dirname, "./res/output.mp4")) | ||
.then(() => { | ||
console.log("done"); | ||
}) | ||
.catch(err => { | ||
console.log("Couldn't generate video."); | ||
}); |
@@ -39,5 +39,6 @@ import fs = require('fs'); | ||
interface CameraAttributes { | ||
x: number; | ||
y: number; | ||
zoom: positiveNumber; | ||
scaleX: number; | ||
scaleY: number; | ||
refX: number; | ||
refY: number; | ||
} | ||
@@ -68,2 +69,6 @@ interface ShapeAttributes { | ||
} | ||
interface ZoomValue{ | ||
scaleX: number; | ||
scaleY: number; | ||
} | ||
@@ -100,2 +105,8 @@ export function setTempPath(path: fs.PathLike): void; | ||
export class ZoomAnimation<Attributes> extends Animation<Attributes>{ | ||
constructor(startZoom: ZoomValue, endZoom: ZoomValue, refX: number, refY: number); | ||
constructor(startZoom: ZoomValue, endZoom: ZoomValue, ref: Coordinate); | ||
constructor(startZoom: ZoomValue, endZoom: ZoomValue, ref: coordinate); | ||
} | ||
export class Animanager<Attributes extends Object>{ | ||
@@ -178,21 +189,4 @@ constructor(defaultValue: object, setVideo?: (video: Video) => void); | ||
addShape(shape: Shape<any>): this; | ||
render(shapes: Array<Shape<any>>): void; | ||
} | ||
export interface VideoAfterExport { | ||
on(event: "done", handler: () => void): this; | ||
on(event: "error", handler: () => void): this; | ||
} | ||
export abstract class VideoAfterExport { | ||
keyframes: Array<Keyframe>; | ||
tempPath: fs.PathLike; | ||
width: evenNumber; | ||
height: evenNumber; | ||
fps: number; | ||
get spf(): number; | ||
frameAtTime(time: number): number; | ||
} | ||
export interface Video { | ||
@@ -220,3 +214,3 @@ on(event: "done", handler: () => void): this; | ||
frameAtTime(time: number): number; | ||
export(filePath: fs.PathLike): VideoAfterExport; | ||
export(filePath: fs.PathLike, callback?: () => void): Promise<undefined>; | ||
} | ||
@@ -223,0 +217,0 @@ } |
278
index.js
@@ -243,9 +243,9 @@ //Main file | ||
this.currentAnimations = []; | ||
if(typeof setVideo === 'function'){ | ||
this.setVideo = setVideo; | ||
if (typeof setVideo === 'function') { | ||
this.setVideo = setVideo; | ||
} | ||
else if(typeof setVideo === 'undefined'){ | ||
this.setVideo = function(value){}; | ||
else if (typeof setVideo === 'undefined') { | ||
this.setVideo = function (value) { }; | ||
} | ||
else{ | ||
else { | ||
throw new TypeError("Invalid constructor."); | ||
@@ -309,11 +309,11 @@ } | ||
} | ||
set extendUntil(value){ | ||
if(typeof value === 'number'){ | ||
set extendUntil(value) { | ||
if (typeof value === 'number') { | ||
this._extendUntil = Math.max(this._extendUntil, value); | ||
} | ||
else{ | ||
else { | ||
throw new TypeError("extendUntil time must be a number (in seconds)."); | ||
} | ||
} | ||
get extendUntil(){ | ||
get extendUntil() { | ||
return this._extendUntil; | ||
@@ -478,2 +478,13 @@ } | ||
} | ||
set frameNumber(value) { | ||
if (Number.isSafeInteger(value) && value >= 0) { | ||
this._frameNumber = value; | ||
} | ||
else { | ||
throw new TypeError("frameNumber must be non negative integer."); | ||
} | ||
} | ||
get frameNumber() { | ||
return this._frameNumber; | ||
} | ||
@@ -786,7 +797,7 @@ draw(ctx, value) { | ||
var newPoints = [] | ||
for(var i = 0; i < value.length; i++){ | ||
if(value[i] instanceof canvideo.Point){ | ||
for (var i = 0; i < value.length; i++) { | ||
if (value[i] instanceof canvideo.Point) { | ||
newPoints.push(value[i]); | ||
} | ||
else{ | ||
else { | ||
newPoints.push(new canvideo.Point(value[i])); | ||
@@ -797,7 +808,7 @@ } | ||
} | ||
else{ | ||
else { | ||
throw new TypeError("points must be an array of points."); | ||
} | ||
}, | ||
get points(){ | ||
get points() { | ||
return this._points; | ||
@@ -810,6 +821,6 @@ } | ||
set points(value){ | ||
set points(value) { | ||
this.defaultValue.points = value; | ||
} | ||
get points(){ | ||
get points() { | ||
return this.defaultValue.points; | ||
@@ -860,2 +871,5 @@ } | ||
this._frameNumber = value; | ||
for (var i = 0; i < this.shapes.length; i++) { | ||
this.shapes.frameNumber = this.frameNumber; | ||
} | ||
} | ||
@@ -869,11 +883,11 @@ else { | ||
} | ||
set extendUntil(value){ | ||
if(typeof value === 'number'){ | ||
set extendUntil(value) { | ||
if (typeof value === 'number') { | ||
this._extendUntil = Math.max(this._extendUntil, value); | ||
} | ||
else{ | ||
else { | ||
throw new TypeError("extendUntil time must be a number (in seconds)."); | ||
} | ||
} | ||
get extendUntil(){ | ||
get extendUntil() { | ||
return this._extendUntil; | ||
@@ -895,4 +909,10 @@ } | ||
function sorter(a, b) { | ||
if (a.valueAt(this.frameNumber).layer === b.valueAt(this.frameNumber).layer) { | ||
return a.shapeIndex - b.shapeIndex; | ||
var aLayer = a.valueAt(this.frameNumber).layer, bLayer = b.valueAt(this.frameNumber).layer; | ||
if (aLayer === bLayer) { | ||
if (a.frameNumber === b.frameNumber) { | ||
return a.shapeIndex - b.shapeIndex; | ||
} | ||
else { | ||
return a.frameNumber - b.frameNumber; | ||
} | ||
} | ||
@@ -904,23 +924,4 @@ else { | ||
var shapesToRender = []; | ||
var shapesToRender = shapes.concat(this.shapes).sort(sorter.bind(this)); | ||
if (shapes instanceof Array) { | ||
for (var i = 0; i < shapes.length; i++) { | ||
if (shapes[i] instanceof canvideo.Shape) { | ||
if (shapes[i].deleteFrame > this.frameNumber) { | ||
shapesToRender.push(shapes[i]); | ||
} | ||
} | ||
else { | ||
throw new TypeError(`shapes[${i}] is not a Shape.`); | ||
} | ||
} | ||
} | ||
else { | ||
throw new TypeError("Shapes must be an array of shapes."); | ||
} | ||
shapesToRender = shapesToRender.concat(this.shapes).sort(sorter.bind(this)); | ||
this.video.loop.goal++; | ||
var canvas = createCanvas(this.video.width, this.video.height); | ||
@@ -930,4 +931,9 @@ var ctx = canvas.getContext('2d'); | ||
var camera = this.video.camera.valueAt(this.frameNumber); | ||
var csx = camera.scaleX, csy = camera.scaleY; | ||
var crx = camera.refX, cry = camera.refY; | ||
ctx.translate(-camera.x, -camera.y); | ||
ctx.scale(camera.zoom, camera.zoom); | ||
ctx.translate(-(crx * csx - crx), -(cry * csy - cry)); | ||
ctx.scale(csx, csy); | ||
//Loop through and draw all the shapes | ||
for (var i = 0; i < shapesToRender.length; i++) { | ||
@@ -937,25 +943,58 @@ shapesToRender[i].draw(ctx, shapesToRender[i].valueAt(this.frameNumber), camera); | ||
var framePath = this.video.tempPath + "/frame" + this.frameNumber + ".jpg"; | ||
canvas.createJPEGStream() | ||
.on("end", () => { | ||
this.video.loop.emit("result", false); | ||
}) | ||
.on("error", err => { | ||
this.video.loop.emit("result", err, this.frameNumber); | ||
}) | ||
.pipe(fs.createWriteStream(framePath)); | ||
//Render next keyframe | ||
if (this.frameNumber + 1 < this.video.keyframes.length) { | ||
this.video.keyframes[this.frameNumber + 1].render(shapesToRender); | ||
return { | ||
promise: new Promise((resolve, reject) => { | ||
canvas.createJPEGStream() | ||
.on("end", () => { | ||
resolve(); | ||
}) | ||
.on("error", err => { | ||
reject(err); | ||
}) | ||
.pipe(fs.createWriteStream(framePath)); | ||
}), | ||
shapes: shapesToRender | ||
}; | ||
} | ||
else { | ||
throw new TypeError("this.frameNumber is not a number"); | ||
} | ||
} | ||
} | ||
//Zoom Linear Animation | ||
canvideo.ZoomAnimation = class extends canvideo.Animation { | ||
constructor(startZoom, endZoom, arg1, arg2) { | ||
if (typeof startZoom === 'object' && typeof startZoom.scaleX === 'number' && typeof startZoom.scaleY === 'number' && typeof endZoom === 'object' && typeof endZoom.scaleX === 'number' && typeof endZoom.scaleY === 'number') { | ||
var refX, refY; | ||
if (typeof arg1 === 'number' && typeof arg2 === 'number') { | ||
refX = arg1, refY = arg2; | ||
} | ||
else if (typeof arg1 === 'object' && typeof arg1.x === 'number' && typeof arg1.y === 'number' && typeof arg2 === 'undefined') { | ||
refX = arg1.x, refY = arg1.y; | ||
} | ||
else if (arg1 instanceof Array && typeof arg1[0] === 'number' && typeof arg1[1] === 'number' && typeof arg2 === 'undefined') { | ||
[refX, refY] = arg1; | ||
} | ||
else { | ||
//This is the last frame | ||
throw new TypeError("Invalid syntax for reference point."); | ||
} | ||
return this; | ||
var startValue = { | ||
scaleX: startZoom.scaleX, | ||
scaleY: startZoom.scaleY, | ||
refX: refX, | ||
refY: refY | ||
}; | ||
var endValue = { | ||
scaleX: endZoom.scaleX, | ||
scaleY: endZoom.scaleY, | ||
refX: refX, | ||
refY: refY | ||
}; | ||
super(startValue, endValue); | ||
} | ||
else { | ||
throw new TypeError("this.frameNumber is not a number"); | ||
throw new TypeError("Invalid startZoom or endZoom."); | ||
} | ||
} | ||
}; | ||
} | ||
@@ -1033,3 +1072,2 @@ | ||
this.tempPath = config.tempPath; | ||
this.loop = new AsyncLoop(); | ||
this.width = width; | ||
@@ -1042,37 +1080,73 @@ this.height = height; | ||
_x: 0, | ||
set x(value){ | ||
if(typeof value === 'number'){ | ||
set x(value) { | ||
if (typeof value === 'number') { | ||
this._x = value; | ||
} | ||
else{ | ||
else { | ||
throw new TypeError("x must be a numbe"); | ||
} | ||
}, | ||
get x(){ | ||
get x() { | ||
return this._x; | ||
}, | ||
_y: 0, | ||
set y(value){ | ||
if(typeof value === 'number'){ | ||
set y(value) { | ||
if (typeof value === 'number') { | ||
this._y = value; | ||
} | ||
else{ | ||
else { | ||
throw new TypeError("y must be a numbe"); | ||
} | ||
}, | ||
get y(){ | ||
get y() { | ||
return this._y; | ||
}, | ||
_zoom: 1, | ||
set zoom(value){ | ||
if(typeof value === 'number' && value > 0){ | ||
this._zoom = value; | ||
_scaleX: 0, | ||
set scaleX(value) { | ||
if (typeof value === 'number') { | ||
this._scaleX = value; | ||
} | ||
else{ | ||
throw new TypeError("zoom must be a number greater than 0."); | ||
else { | ||
throw new TypeError("scaleX must be a numbe"); | ||
} | ||
}, | ||
get zoom(){ | ||
return this._zoom; | ||
} | ||
get scaleX() { | ||
return this._scaleX; | ||
}, | ||
_scaleY: 0, | ||
set scaleY(value) { | ||
if (typeof value === 'number') { | ||
this._scaleY = value; | ||
} | ||
else { | ||
throw new TypeError("scaleY must be a numbe"); | ||
} | ||
}, | ||
get scaleY() { | ||
return this._scaleY; | ||
}, | ||
_refX: 0, | ||
set refX(value) { | ||
if (typeof value === 'number') { | ||
this._refX = value; | ||
} | ||
else { | ||
throw new TypeError("refX must be a numbe"); | ||
} | ||
}, | ||
get refX() { | ||
return this._refX; | ||
}, | ||
_refY: 0, | ||
set refY(value) { | ||
if (typeof value === 'number') { | ||
this._refY = value; | ||
} | ||
else { | ||
throw new TypeError("refY must be a numbe"); | ||
} | ||
}, | ||
get refY() { | ||
return this._refY; | ||
}, | ||
}); | ||
@@ -1112,11 +1186,11 @@ | ||
} | ||
set extendUntil(value){ | ||
if(typeof value === 'number'){ | ||
set extendUntil(value) { | ||
if (typeof value === 'number') { | ||
this._extendUntil = Math.max(this._extendUntil, value); | ||
} | ||
else{ | ||
else { | ||
throw new TypeError("extendUntil time must be a number (in seconds)."); | ||
} | ||
} | ||
get extendUntil(){ | ||
get extendUntil() { | ||
return this._extendUntil; | ||
@@ -1149,3 +1223,3 @@ } | ||
} | ||
export(filePath) { | ||
export(filePath, callback) { | ||
if (this.exported) { | ||
@@ -1162,2 +1236,5 @@ throw new SyntaxError("Cannot export twice."); | ||
} | ||
if(!((typeof callback === 'function' && callback.length === 0) || (typeof callback === 'undefined'))){ | ||
throw new TypeError("If callback is specified it must be a function which takes 0 args."); | ||
} | ||
@@ -1180,24 +1257,25 @@ //Set camera's video | ||
//Extend video further than last keyframe to render animations | ||
while(this.keyframes.length < this.extendUntil * this.fps){ | ||
while (this.keyframes.length < this.extendUntil * this.fps) { | ||
this.addKeyframe(new canvideo.Keyframe(this.keyframes.length * this.spf)); | ||
} | ||
//Render the first frame | ||
this.keyframes[0].render(); | ||
this.loop.on("done", errors => { | ||
if (!errors) { | ||
this.command | ||
.input(config.tempPath + "/frame%1d.jpg") | ||
.inputFPS(this.fps) | ||
.save(filePath) | ||
.outputFPS(this.fps); | ||
} | ||
else { | ||
this.emit("error"); | ||
} | ||
}); | ||
//Render a keyframe | ||
this.exported = true; | ||
return this; | ||
var promises = []; | ||
var previousShapes = []; | ||
for (var i = 0; i < this.keyframes.length; i++) { | ||
var { shapes, promise } = this.keyframes[i].render(shapes); | ||
previousShapes = shapes; | ||
promises.push(promise); | ||
} | ||
return Promise.all(promises) | ||
.then(() => { | ||
this.emit("done"); | ||
if(callback){ | ||
callback(); | ||
} | ||
}) | ||
.catch(err => { | ||
this.emit("error", err); | ||
}); | ||
} | ||
@@ -1204,0 +1282,0 @@ } |
{ | ||
"name": "canvideo", | ||
"version": "4.3.0", | ||
"version": "5.1.0", | ||
"description": "\u0016\u0016An open-source tool for Node.js that can make animations and generate videos.", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
2671110
1821