diff --git a/index.html b/index.html
index b0cb5181..d4cc28f7 100644
--- a/index.html
+++ b/index.html
@@ -96,6 +96,7 @@
+
diff --git a/js/controller/AnimatedPreviewController.js b/js/controller/AnimatedPreviewController.js
index c8b7f51a..ee1e7f1c 100644
--- a/js/controller/AnimatedPreviewController.js
+++ b/js/controller/AnimatedPreviewController.js
@@ -3,10 +3,13 @@
ns.AnimatedPreviewController = function (framesheet, container, dpi) {
this.framesheet = framesheet;
this.container = container;
- this.animIndex = 0;
+
+ this.elapsedTime = 0;
+ this.currentIndex = 0;
this.fps = parseInt($("#preview-fps")[0].value, 10);
-
+ this.deltaTime = 0;
+ this.previousTime = 0;
var renderingOptions = {
"dpi": dpi
};
@@ -14,40 +17,24 @@
};
ns.AnimatedPreviewController.prototype.init = function () {
- this.initDom();
-
- this.renderer.init(this.framesheet.getFrameByIndex(this.animIndex));
-
- this.startAnimationTimer();
- };
-
- ns.AnimatedPreviewController.prototype.initDom = function () {
$("#preview-fps")[0].addEventListener('change', this.onFPSSliderChange.bind(this));
};
- ns.AnimatedPreviewController.prototype.startAnimationTimer = function () {
- this.stopAnimationTimer();
- this.animationTimer = window.setTimeout(this.refreshAnimatedPreview.bind(this), 1000/this.fps);
- };
-
- ns.AnimatedPreviewController.prototype.stopAnimationTimer = function () {
- if (this.animationTimer) {
- window.clearInterval(this.animationTimer);
- this.animationTimer = null;
- }
- };
-
ns.AnimatedPreviewController.prototype.onFPSSliderChange = function(evt) {
this.fps = parseInt($("#preview-fps")[0].value, 10);
};
- ns.AnimatedPreviewController.prototype.refreshAnimatedPreview = function () {
- if (!this.framesheet.hasFrameAtIndex(this.animIndex)) {
- this.animIndex = 0;
+ ns.AnimatedPreviewController.prototype.render = function (delta) {
+ this.elapsedTime += delta;
+ var index = Math.floor(this.elapsedTime / (1000/this.fps));
+ if (index != this.currentIndex) {
+ this.currentIndex = index;
+ if (!this.framesheet.hasFrameAtIndex(this.currentIndex)) {
+ this.currentIndex = 0;
+ this.elapsedTime = 0;
+ }
+ this.renderer.render(this.framesheet.getFrameByIndex(this.currentIndex));
}
- this.renderer.render(this.framesheet.getFrameByIndex(this.animIndex));
- this.animIndex++;
- this.startAnimationTimer();
};
})();
\ No newline at end of file
diff --git a/js/controller/DrawingController.js b/js/controller/DrawingController.js
index 97997a9c..a9a32e3e 100644
--- a/js/controller/DrawingController.js
+++ b/js/controller/DrawingController.js
@@ -210,8 +210,22 @@
this.overlayRenderer.render(this.overlayFrame);
};
+ ns.DrawingController.prototype.render = function () {
+ try {
+
+ this.renderFrame();
+ this.renderOverlay();
+ } catch (e) {
+ // TODO : temporary t/c for integration
+ }
+ };
+
ns.DrawingController.prototype.renderFrame = function () {
- this.renderer.render(this.frame);
+ var serializedFrame = this.frame.serialize();
+ if (this.serializedFrame != serializedFrame) {
+ this.serializedFrame = serializedFrame
+ this.renderer.render(this.frame);
+ }
};
ns.DrawingController.prototype.renderFramePixel = function (col, row) {
@@ -219,7 +233,11 @@
};
ns.DrawingController.prototype.renderOverlay = function () {
- this.overlayRenderer.render(this.overlayFrame);
+ var serializedOverlay = this.overlayFrame.serialize();
+ if (this.serializedOverlay != serializedOverlay) {
+ this.serializedOverlay = serializedOverlay
+ this.renderer.render(this.overlayFrame);
+ }
};
ns.DrawingController.prototype.clearOverlay = function () {
diff --git a/js/controller/PreviewFilmController.js b/js/controller/PreviewFilmController.js
index 7db43364..5ae04e03 100644
--- a/js/controller/PreviewFilmController.js
+++ b/js/controller/PreviewFilmController.js
@@ -6,40 +6,41 @@
this.framesheet = framesheet;
this.container = container;
+ this.dirty = false;
+
$.subscribe(Events.REDRAW_PREVIEWFILM, $.proxy(function(evt) {
- this.createPreviews()
+ this.dirty = true;
}, this));
};
ns.PreviewFilmController.prototype.init = function() {
- var addFrameButton = $('#add-frame-button')[0];
- addFrameButton.addEventListener('mousedown', this.addFrame.bind(this));
- this.createPreviews();
-
-
+ var addFrameButton = $('#add-frame-button')[0];
+ addFrameButton.addEventListener('mousedown', this.addFrame.bind(this));
};
ns.PreviewFilmController.prototype.addFrame = function () {
this.framesheet.addEmptyFrame();
- piskel.setActiveFrameAndRedraw(this.framesheet.getFrameCount() - 1);
+ piskel.setActiveFrame(this.framesheet.getFrameCount() - 1);
};
- ns.PreviewFilmController.prototype.createPreviews = function () {
- // TODO(vincz): Full redraw on any drawing modification, optimize.
- this.container.html("");
+ ns.PreviewFilmController.prototype.render = function () {
+ if (!this.dirty) return
+ // TODO(vincz): Full redraw on any drawing modification, optimize.
+ this.container.html("");
- var frameCount = this.framesheet.getFrameCount();
-
- for (var i = 0, l = frameCount; i < l ; i++) {
- this.container.append(this.createInterstitialTile_(i));
- this.container.append(this.createPreviewTile_(i, this.framesheet));
- }
- this.container.append(this.createInterstitialTile_(frameCount));
+ var frameCount = this.framesheet.getFrameCount();
- var needDragndropBehavior = !!(frameCount > 1);
- if(needDragndropBehavior) {
- this.initDragndropBehavior_();
- }
+ for (var i = 0, l = frameCount; i < l ; i++) {
+ this.container.append(this.createInterstitialTile_(i));
+ this.container.append(this.createPreviewTile_(i, this.framesheet));
+ }
+ this.container.append(this.createInterstitialTile_(frameCount));
+
+ var needDragndropBehavior = !!(frameCount > 1);
+ if(needDragndropBehavior) {
+ this.initDragndropBehavior_();
+ }
+ this.dirty = false;
};
/**
@@ -135,7 +136,7 @@
$('#preview-list').removeClass("show-interstitial-tiles");
// TODO(vincz): deprecate.
- piskel.setActiveFrameAndRedraw(activeFrame);
+ piskel.setActiveFrame(activeFrame);
// TODO(vincz): move localstorage request to the model layer?
$.publish(Events.LOCALSTORAGE_REQUEST);
@@ -168,7 +169,7 @@
previewTileRoot.addEventListener('click', function(evt) {
// has not class tile-action:
if(!evt.target.classList.contains('tile-action')) {
- piskel.setActiveFrameAndRedraw(tileNumber);
+ piskel.setActiveFrame(tileNumber);
}
});
diff --git a/js/drawingtools/Move.js b/js/drawingtools/Move.js
index 2ff9ca73..23b0f58f 100644
--- a/js/drawingtools/Move.js
+++ b/js/drawingtools/Move.js
@@ -29,7 +29,6 @@
var colDiff = col - this.startCol, rowDiff = row - this.startRow;
if (colDiff != 0 || rowDiff != 0) {
this.shiftFrame(colDiff, rowDiff, drawer.frame, this.frameClone);
- drawer.renderFrame();
}
};
diff --git a/js/drawingtools/PaintBucket.js b/js/drawingtools/PaintBucket.js
index a4523bd7..f3c3ccfe 100644
--- a/js/drawingtools/PaintBucket.js
+++ b/js/drawingtools/PaintBucket.js
@@ -21,10 +21,6 @@
var targetColor = drawer.frame.getPixel(col, row);
//this.recursiveFloodFill_(frame, col, row, targetColor, color);
this.queueLinearFloodFill_(drawer.frame, col, row, targetColor, color);
-
- // Draw in canvas:
- // TODO: Remove that when we have the centralized redraw loop
- drawer.renderFrame();
};
/**
diff --git a/js/drawingtools/Rectangle.js b/js/drawingtools/Rectangle.js
index 36967878..5f2fa7c7 100644
--- a/js/drawingtools/Rectangle.js
+++ b/js/drawingtools/Rectangle.js
@@ -25,7 +25,6 @@
// Drawing the first point of the rectangle in the fake overlay canvas:
drawer.overlayFrame.setPixel(col, row, color);
- drawer.renderOverlay();
};
ns.Rectangle.prototype.moveToolAt = function(col, row, color, drawer) {
@@ -44,13 +43,13 @@
}
drawer.overlayFrame.setPixel(strokePoints[i].col, strokePoints[i].row, color);
}
- drawer.renderOverlay();
};
/**
* @override
*/
ns.Rectangle.prototype.releaseToolAt = function(col, row, color, drawer) {
+ drawer.clearOverlay();
// If the stroke tool is released outside of the canvas, we cancel the stroke:
if(drawer.frame.containsPixel(col, row)) {
var strokePoints = this.getRectanglePixels_(this.startCol, col, this.startRow, row);
@@ -59,12 +58,8 @@
drawer.frame.setPixel(strokePoints[i].col, strokePoints[i].row, color);
}
// The user released the tool to draw a line. We will compute the pixel coordinate, impact
- // the model and draw them in the drawing canvas (not the fake overlay anymore)
- // Draw in canvas:
- // TODO: Remove that when we have the centralized redraw loop
- drawer.renderFrame();
+ // the model and draw them in the drawing canvas (not the fake overlay anymore)
}
- drawer.clearOverlay();
};
/**
diff --git a/js/drawingtools/SimplePen.js b/js/drawingtools/SimplePen.js
index 39a27363..86065d63 100644
--- a/js/drawingtools/SimplePen.js
+++ b/js/drawingtools/SimplePen.js
@@ -23,10 +23,6 @@
this.previousCol = col;
this.previousRow = row;
drawer.frame.setPixel(col, row, color);
-
- // Draw on canvas:
- // TODO: Remove that when we have the centralized redraw loop
- drawer.renderFramePixel(col, row);
}
};
diff --git a/js/piskel.js b/js/piskel.js
index 9e8d11c6..2aee3ebb 100644
--- a/js/piskel.js
+++ b/js/piskel.js
@@ -36,6 +36,7 @@ $.namespace("pskl");
piskel.initDPIs_();
+
frameSheet = new pskl.model.FrameSheet(framePixelWidth, framePixelHeight);
frameSheet.addEmptyFrame();
@@ -78,12 +79,21 @@ $.namespace("pskl");
}
$.subscribe('SET_ACTIVE_FRAME', function(evt, frameId) {
- piskel.setActiveFrameAndRedraw(frameId);
+ piskel.setActiveFrame(frameId);
});
$.subscribe('FRAMESHEET_RESET', function(evt, frameId) {
piskel.redraw();
});
+ var drawingLoop = new pskl.rendering.DrawingLoop();
+ drawingLoop.addCallback(this.render, this);
+ drawingLoop.start();
+ },
+
+ render : function (delta) {
+ this.drawingController.render(delta);
+ this.animationController.render(delta);
+ this.previewsController.render(delta);
},
/**
@@ -138,7 +148,7 @@ $.namespace("pskl");
$.subscribe(Events.REFRESH, function() {
- piskel.setActiveFrameAndRedraw(0);
+ piskel.setActiveFrame(0);
});
pskl.ToolSelector.init();
@@ -164,13 +174,12 @@ $.namespace("pskl");
piskel.setActiveFrame(0);
$.publish(Events.HIDE_NOTIFICATION);
piskel.finishInit();
- piskel.setActiveFrameAndRedraw(0);
};
xhr.onerror = function () {
$.publish(Events.HIDE_NOTIFICATION);
piskel.finishInit();
- piskel.setActiveFrameAndRedraw(0);
+ piskel.setActiveFrame(0);
};
xhr.send();
@@ -181,18 +190,6 @@ $.namespace("pskl");
this.drawingController.frame = this.getCurrentFrame();
},
- setActiveFrameAndRedraw: function(index) {
- this.setActiveFrame(index);
- this.redraw();
- },
-
- redraw : function () {
- // Update drawing canvas:
- this.drawingController.renderFrame();
- // Update slideshow:
- this.previewsController.createPreviews();
- },
-
getActiveFrameIndex: function() {
if(-1 == activeFrameIndex) {
throw "Bad active frame initialization."
@@ -203,7 +200,6 @@ $.namespace("pskl");
getCurrentFrame : function () {
return frameSheet.getFrameByIndex(activeFrameIndex);
},
-
// TODO(julz): Create package ?
storeSheet : function (event) {
// TODO Refactor using jquery ?
diff --git a/js/rendering/DrawingLoop.js b/js/rendering/DrawingLoop.js
new file mode 100644
index 00000000..9d13a239
--- /dev/null
+++ b/js/rendering/DrawingLoop.js
@@ -0,0 +1,58 @@
+(function () {
+ var ns = $.namespace("pskl.rendering");
+
+ ns.DrawingLoop = function () {
+ this.requestAnimationFrame = this.getRequestAnimationFrameShim_();
+ this.isRunning = false;
+ this.previousTime = 0;
+ this.callbacks = [];
+ };
+
+ ns.DrawingLoop.prototype.addCallback = function (callback, scope, args) {
+ var callbackObj = {
+ fn : callback,
+ scope : scope,
+ args : args
+ };
+ this.callbacks.push(callbackObj);
+ return callbackObj;
+ };
+
+ ns.DrawingLoop.prototype.removeCallback = function (callbackObj) {
+ var index = this.callbacks.indexOf(callbackObj);
+ if (index != -1) {
+ this.callbacks.splice(index, 1);
+ }
+ };
+
+ ns.DrawingLoop.prototype.start = function () {
+ this.isRunning = true;
+ this.loop_();
+ };
+
+ ns.DrawingLoop.prototype.loop_ = function () {
+ var currentTime = Date.now();
+ var delta = currentTime - this.previousTime;
+ this.executeCallbacks_(delta);
+ this.previousTime = currentTime;
+ this.requestAnimationFrame.call(window, this.loop_.bind(this));
+ };
+
+ ns.DrawingLoop.prototype.executeCallbacks_ = function (deltaTime) {
+ for (var i = 0 ; i < this.callbacks.length ; i++) {
+ var cb = this.callbacks[i];
+ cb.fn.call(cb.scope, deltaTime, cb.args);
+ }
+ };
+
+ ns.DrawingLoop.prototype.stop = function () {
+ this.isRunning = false;
+ };
+
+ ns.DrawingLoop.prototype.getRequestAnimationFrameShim_ = function () {
+ var requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame ||
+ window.webkitRequestAnimationFrame || window.msRequestAnimationFrame || function (callback) {window.setTimeout(callback, 1000/60)};
+
+ return requestAnimationFrame;
+ }
+})()
\ No newline at end of file
diff --git a/js/rendering/FrameRenderer.js b/js/rendering/FrameRenderer.js
index 14f6ae42..a2f97e44 100644
--- a/js/rendering/FrameRenderer.js
+++ b/js/rendering/FrameRenderer.js
@@ -1,7 +1,6 @@
(function () {
var ns = $.namespace("pskl.rendering");
-
ns.FrameRenderer = function (container, renderingOptions, className) {
this.defaultRenderingOptions = {
@@ -169,4 +168,18 @@
}
return this.canvas;
};
+
+ /**
+ * @private
+ */
+ ns.FrameRenderer.prototype.createCanvasForFrame_ = function (frame) {
+ var canvas = document.createElement("canvas");
+ canvas.setAttribute("width", frame.getWidth() * this.dpi);
+ canvas.setAttribute("height", frame.getHeight() * this.dpi);
+
+ canvas.classList.add("canvas");
+ if(this.className) canvas.classList.add(this.className);
+
+ return canvas;
+ };
})();
\ No newline at end of file