diff --git a/index.html b/index.html index 499e9821..256c5709 100644 --- a/index.html +++ b/index.html @@ -76,6 +76,8 @@ + + diff --git a/js/controller/AnimatedPreviewController.js b/js/controller/AnimatedPreviewController.js new file mode 100644 index 00000000..1a1462bf --- /dev/null +++ b/js/controller/AnimatedPreviewController.js @@ -0,0 +1,62 @@ +(function () { + var ns = $.namespace("pskl.controller"); + ns.AnimatedPreviewController = function (framesheet, container, dpi) { + this.dpi = dpi; + this.framesheet = framesheet; + this.container = container; + this.animIndex = 0; + + this.fps = parseInt($("#preview-fps")[0].value, 10); + + this.renderer = new pskl.rendering.FrameRenderer(); + }; + + ns.AnimatedPreviewController.prototype.init = function () { + this.initDom(); + this.startAnimationTimer(); + }; + + ns.AnimatedPreviewController.prototype.initDom = function () { + var frame = this.framesheet.getFrameByIndex(0); + var height = frame.getHeight() * this.dpi, + width = frame.getWidth() * this.dpi; + + previewCanvas = document.createElement('canvas'); + previewCanvas.className = 'canvas'; + + this.container.setAttribute('style', 'width:' + width + 'px; height:' + height + 'px;'); + previewCanvas.setAttribute('width', width); + previewCanvas.setAttribute('height', height); + + this.container.appendChild(previewCanvas); + this.previewCanvas = previewCanvas; + + $("#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 () { + this.renderer.render(this.framesheet.getFrameByIndex(this.animIndex), this.previewCanvas, this.dpi); + this.animIndex++; + if (this.animIndex == this.framesheet.getFrameCount()) { + this.animIndex = 0; + } + this.startAnimationTimer(); + }; + +})(); \ No newline at end of file diff --git a/js/controller/PreviewFilmController.js b/js/controller/PreviewFilmController.js new file mode 100644 index 00000000..9b5c2244 --- /dev/null +++ b/js/controller/PreviewFilmController.js @@ -0,0 +1,89 @@ +(function () { + var ns = $.namespace("pskl.controller"); + ns.PreviewFilmController = function (framesheet, container, dpi) { + + this.dpi = dpi; + this.framesheet = framesheet; + this.container = container; + + this.renderer = new pskl.rendering.FrameRenderer(); + }; + + ns.PreviewFilmController.prototype.init = function() { + var addFrameButton = $('#add-frame-button')[0]; + addFrameButton.addEventListener('mousedown', this.addFrame.bind(this)); + this.createPreviews(); + }; + + ns.PreviewFilmController.prototype.addFrame = function () { + this.framesheet.addEmptyFrame(); + piskel.setActiveFrameAndRedraw(this.framesheet.getFrameCount() - 1); + }; + + ns.PreviewFilmController.prototype.createPreviews = function () { + this.container.innerHTML = ""; + for (var i = 0, l = this.framesheet.getFrameCount(); i < l ; i++) { + this.container.appendChild(this.createPreviewTile(i)); + } + }; + + ns.PreviewFilmController.prototype.createPreviewTile = function(tileNumber) { + var frame = this.framesheet.getFrameByIndex(tileNumber); + var width = frame.getWidth() * this.dpi, + height = frame.getHeight() * this.dpi; + + var previewTileRoot = document.createElement("li"); + var classname = "preview-tile"; + + if (piskel.getActiveFrameIndex() == tileNumber) { + classname += " selected"; + } + previewTileRoot.className = classname; + + var canvasContainer = document.createElement("div"); + canvasContainer.className = "canvas-container"; + canvasContainer.setAttribute('style', 'width:' + width + 'px; height:' + height + 'px;'); + + var canvasBackground = document.createElement("div"); + canvasBackground.className = "canvas-background"; + canvasContainer.appendChild(canvasBackground); + + var canvasPreview = document.createElement("canvas"); + canvasPreview.className = "canvas tile-view" + + canvasPreview.setAttribute('width', width); + canvasPreview.setAttribute('height', height); + + previewTileRoot.addEventListener('click', function(evt) { + // has not class tile-action: + if(!evt.target.classList.contains('tile-action')) { + piskel.setActiveFrameAndRedraw(tileNumber); + } + }); + + var canvasPreviewDuplicateAction = document.createElement("button"); + canvasPreviewDuplicateAction.className = "tile-action" + canvasPreviewDuplicateAction.innerHTML = "dup" + + canvasPreviewDuplicateAction.addEventListener('click', function(evt) { + piskel.duplicateFrame(tileNumber); + }); + + this.renderer.render(this.framesheet.getFrameByIndex(tileNumber), canvasPreview, this.dpi); + canvasContainer.appendChild(canvasPreview); + previewTileRoot.appendChild(canvasContainer); + previewTileRoot.appendChild(canvasPreviewDuplicateAction); + + if(tileNumber > 0 || this.framesheet.getFrameCount() > 1) { + var canvasPreviewDeleteAction = document.createElement("button"); + canvasPreviewDeleteAction.className = "tile-action" + canvasPreviewDeleteAction.innerHTML = "del" + canvasPreviewDeleteAction.addEventListener('click', function(evt) { + piskel.removeFrame(tileNumber); + }); + previewTileRoot.appendChild(canvasPreviewDeleteAction); + } + + return previewTileRoot; + }; +})(); \ No newline at end of file diff --git a/js/model/Frame.js b/js/model/Frame.js index 2302c43d..552f6b93 100644 --- a/js/model/Frame.js +++ b/js/model/Frame.js @@ -1,5 +1,6 @@ (function () { var ns = $.namespace("pskl.model"); + ns.Frame = function (pixels) { this.pixels = pixels; }; @@ -30,6 +31,10 @@ return clone; }; + ns.Frame.prototype.serialize = function () { + return JSON.stringify(this.pixels); + }; + ns.Frame.prototype.setPixel = function (col, row, color) { this.pixels[col][row] = color; }; diff --git a/js/model/FrameSheet.js b/js/model/FrameSheet.js index 9e1bed9a..bb66a547 100644 --- a/js/model/FrameSheet.js +++ b/js/model/FrameSheet.js @@ -38,7 +38,11 @@ // Could be used to pass around model using long GET param (good enough for simple models) and // do some temporary locastorage ns.FrameSheet.prototype.serialize = function() { - throw "FrameSheet.prototype.serialize not implemented" + var serializedFrames = []; + for (var i = 0 ; i < this.frames.length ; i++) { + serializedFrames.push(this.frames[i].serialize()); + } + return '[' + serializedFrames.join(",") + ']'; //return JSON.stringify(frames); }; @@ -48,13 +52,17 @@ * @param {String} serialized */ ns.FrameSheet.prototype.deserialize = function (serialized) { - throw "FrameSheet.prototype.deserialize not implemented" - // try { - // frames = JSON.parse(serialized); - // $.publish(Events.FRAMESHEET_RESET); - // } catch (e) { - // throw "Could not load serialized framesheet." + e.message - // } + try { + var frameConfigurations = JSON.parse(serialized); + this.frames = []; + for (var i = 0 ; i < frameConfigurations.length ; i++) { + var frameCfg = frameConfigurations[i]; + this.addFrame(new ns.Frame(frameCfg)); + } + $.publish(Events.FRAMESHEET_RESET); + } catch (e) { + throw "Could not load serialized framesheet : " + e.message + } }; ns.FrameSheet.prototype.getFrameByIndex = function(index) { diff --git a/js/piskel.js b/js/piskel.js index dcdc62b7..f8ea4b53 100644 --- a/js/piskel.js +++ b/js/piskel.js @@ -9,7 +9,7 @@ $.namespace("pskl"); /** * FrameSheetModel instance. */ - var frameSheet, renderer = null, + var frameSheet, // Temporary zoom implementation to easily get bigger canvases to // see how good perform critical algorithms on big canvas. @@ -52,17 +52,32 @@ $.namespace("pskl"); init : function () { var emptyFrame = pskl.model.Frame.createEmpty(framePixelWidth, framePixelHeight); + frameSheet = new pskl.model.FrameSheet(); + frameSheet.addFrame(emptyFrame); + this.drawingController = new pskl.controller.DrawingController( emptyFrame, $('#drawing-canvas-container')[0], drawingCanvasDpi ); - renderer = new pskl.rendering.FrameRenderer(); - - frameSheet = new pskl.model.FrameSheet(); - frameSheet.addFrame(emptyFrame); this.setActiveFrame(0); + + this.animationController = new pskl.controller.AnimatedPreviewController( + frameSheet, + $('#preview-canvas-container')[0], + previewAnimationCanvasDpi + ); + + + this.previewsController = new pskl.controller.PreviewFilmController( + frameSheet, + $('#preview-list')[0], + previewTileCanvasDpi + ); + + this.animationController.init(); + this.previewsController.init(); pskl.NotificationService.init(); pskl.LocalStorageService.init(frameSheet); @@ -96,9 +111,6 @@ $.namespace("pskl"); // TODO: Move this into their service or behavior files: this.initDrawingArea(); - this.initPreviewSlideshow(); - this.initAnimationPreview(); - this.startAnimation(); pskl.ToolSelector.init(); pskl.Palette.init(frameSheet); @@ -142,21 +154,19 @@ $.namespace("pskl"); setActiveFrameAndRedraw: function(index) { this.setActiveFrame(index); - - // When redraw engine is refactored, remove the following crap and - // trigger an event instead: + this.redraw(); + }, + redraw : function () { // Update drawing canvas: this.drawingController.renderFrame(); // Update slideshow: - this.createPreviews(); - // Update animation preview: - animIndex = 0; + this.previewsController.createPreviews(); }, getActiveFrameIndex: function() { if(-1 == activeFrameIndex) { - throw "Bad active frane initialization." + throw "Bad active frame initialization." } return activeFrameIndex; }, @@ -171,128 +181,13 @@ $.namespace("pskl"); drawingAreaContainer.addEventListener('contextmenu', this.onCanvasContextMenu); }, - initPreviewSlideshow: function() { - var addFrameButton = $('#add-frame-button')[0]; - addFrameButton.addEventListener('mousedown', function() { - frameSheet.addEmptyFrame(); - piskel.setActiveFrameAndRedraw(frameSheet.getFrameCount() - 1); - }); - this.createPreviews(); - }, - - initAnimationPreview : function() { - - var previewAnimationContainer = $('#preview-canvas-container')[0]; - previewCanvas = document.createElement('canvas'); - previewCanvas.className = 'canvas'; - previewAnimationContainer.setAttribute('style', - 'width:' + framePixelWidth * previewAnimationCanvasDpi + 'px; height:' + framePixelHeight * previewAnimationCanvasDpi + 'px;'); - previewAnimationContainer.appendChild(previewCanvas); - previewCanvas.setAttribute('width', framePixelWidth * previewAnimationCanvasDpi); - previewCanvas.setAttribute('height', framePixelHeight * previewAnimationCanvasDpi); - }, - - startAnimation : function () { - var scope = this; - var animFPSTuner = $("#preview-fps")[0]; - var animPreviewFPS = parseInt(animFPSTuner.value, 10); - var startPreviewRefresh = function() { - return setInterval(scope.refreshAnimatedPreview, 1000/animPreviewFPS); - }; - var refreshUpdater = startPreviewRefresh(); - - animFPSTuner.addEventListener('change', function(evt) { - window.clearInterval(refreshUpdater); - animPreviewFPS = parseInt(animFPSTuner.value, 10); - $("#display-fps").html(animPreviewFPS + " fps"); - refreshUpdater = startPreviewRefresh(); - }); - }, - - createPreviews : function () { - console.log("createPreviews"); - var container = $('#preview-list')[0], previewTile; - container.innerHTML = ""; - for (var i = 0, l = frameSheet.getFrameCount(); i < l ; i++) { - previewTile = this.createPreviewTile(i); - container.appendChild(previewTile); - } - }, - - createPreviewTile: function(tileNumber) { - var previewTileRoot = document.createElement("li"); - var classname = "preview-tile"; - - if (this.getActiveFrameIndex() == tileNumber) { - classname += " selected"; - } - previewTileRoot.className = classname; - - var canvasContainer = document.createElement("div"); - canvasContainer.className = "canvas-container"; - canvasContainer.setAttribute('style', - 'width:' + framePixelWidth * previewTileCanvasDpi + 'px; height:' + framePixelHeight * previewTileCanvasDpi + 'px;'); - - var canvasBackground = document.createElement("div"); - canvasBackground.className = "canvas-background"; - canvasContainer.appendChild(canvasBackground); - - var canvasPreview = document.createElement("canvas"); - canvasPreview.className = "canvas tile-view" - - canvasPreview.setAttribute('width', framePixelWidth * previewTileCanvasDpi); - canvasPreview.setAttribute('height', framePixelHeight * previewTileCanvasDpi); - - previewTileRoot.addEventListener('click', function(evt) { - // has not class tile-action: - if(!evt.target.classList.contains('tile-action')) { - piskel.setActiveFrameAndRedraw(tileNumber); - } - }); - - var canvasPreviewDuplicateAction = document.createElement("button"); - canvasPreviewDuplicateAction.className = "tile-action" - canvasPreviewDuplicateAction.innerHTML = "dup" - - canvasPreviewDuplicateAction.addEventListener('click', function(evt) { - piskel.duplicateFrame(tileNumber); - }); - - renderer.render(frameSheet.getFrameByIndex(tileNumber), canvasPreview, previewTileCanvasDpi); - canvasContainer.appendChild(canvasPreview); - previewTileRoot.appendChild(canvasContainer); - previewTileRoot.appendChild(canvasPreviewDuplicateAction); - - if(tileNumber > 0 || frameSheet.getFrameCount() > 1) { - var canvasPreviewDeleteAction = document.createElement("button"); - canvasPreviewDeleteAction.className = "tile-action" - canvasPreviewDeleteAction.innerHTML = "del" - canvasPreviewDeleteAction.addEventListener('click', function(evt) { - piskel.removeFrame(tileNumber); - }); - previewTileRoot.appendChild(canvasPreviewDeleteAction); - } - - return previewTileRoot; - }, - - refreshAnimatedPreview : function () { - renderer.render(frameSheet.getFrameByIndex(animIndex), previewCanvas, previewAnimationCanvasDpi); - animIndex++; - if (animIndex == frameSheet.getFrameCount()) { - animIndex = 0; - } - }, - removeFrame: function(frameIndex) { frameSheet.removeFrameByIndex(frameIndex); - this.setActiveFrameAndRedraw(frameIndex - 1); }, duplicateFrame: function(frameIndex) { frameSheet.duplicateFrameByIndex(frameIndex); - this.setActiveFrameAndRedraw(frameIndex + 1); }, @@ -343,7 +238,7 @@ $.namespace("pskl"); // Note: The mousemove movement (and the mouseup) may end up outside // of the drawing canvas. // TODO: Remove that when we have the centralized redraw loop - this.createPreviews(); + this.previewsController.createPreviews(); } if(isRightClicked) {