diff --git a/css/style.css b/css/style.css index 63e082c9..fb44b886 100644 --- a/css/style.css +++ b/css/style.css @@ -73,10 +73,7 @@ } .drawing-canvas { - position: absolute; - top: 0; - left: 0; - z-index: 1; + float: left; } .canvas-overlay { diff --git a/index.html b/index.html index 8cfdadac..91efbe58 100644 --- a/index.html +++ b/index.html @@ -91,10 +91,10 @@ + - diff --git a/js/controller/AnimatedPreviewController.js b/js/controller/AnimatedPreviewController.js index fd99578b..c8b7f51a 100644 --- a/js/controller/AnimatedPreviewController.js +++ b/js/controller/AnimatedPreviewController.js @@ -1,36 +1,27 @@ (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(); + var renderingOptions = { + "dpi": dpi + }; + this.renderer = new pskl.rendering.FrameRenderer(this.container, renderingOptions); }; ns.AnimatedPreviewController.prototype.init = function () { this.initDom(); + + this.renderer.init(this.framesheet.getFrameByIndex(this.animIndex)); + 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)); }; @@ -54,8 +45,7 @@ if (!this.framesheet.hasFrameAtIndex(this.animIndex)) { this.animIndex = 0; } - - this.renderer.render(this.framesheet.getFrameByIndex(this.animIndex), this.previewCanvas, this.dpi); + this.renderer.render(this.framesheet.getFrameByIndex(this.animIndex)); this.animIndex++; this.startAnimationTimer(); }; diff --git a/js/controller/DrawingController.js b/js/controller/DrawingController.js index 3752bd52..f6043669 100644 --- a/js/controller/DrawingController.js +++ b/js/controller/DrawingController.js @@ -3,62 +3,46 @@ ns.DrawingController = function (frame, container, dpi) { this.dpi = dpi; + var renderingOptions = { + "dpi": dpi, + "displayGrid": true // Retrieve from localsotrage config + } + // Public this.frame = frame; - this.overlay = pskl.model.Frame.createEmptyFromFrame(frame); + this.overlayFrame = pskl.model.Frame.createEmptyFromFrame(frame); // Type is frame // Private this.container = container; - this.mainCanvas = this.createMainCanvas(); - this.overlayCanvas = this.createOverlayCanvas(); - this.renderer = new pskl.rendering.FrameRenderer(); + this.renderer = new pskl.rendering.FrameRenderer(this.container, renderingOptions, "drawing-canvas"); + this.overlayRenderer = new pskl.rendering.FrameRenderer(this.container, renderingOptions, "canvas-overlay"); + + this.renderer.init(this.frame); + this.overlayRenderer.init(this.frame); + }; + + ns.DrawingController.prototype.updateDPI = function (newDPI) { + this.renderer.updateDPI(newDPI); + this.overlayRenderer.updateDPI(newDPI); + + this.renderer.render(this.frame); + this.overlayRenderer.render(this.frame); }; ns.DrawingController.prototype.renderFrame = function () { - this.renderer.render(this.frame, this.mainCanvas, this.dpi); + this.renderer.render(this.frame); }; ns.DrawingController.prototype.renderFramePixel = function (col, row) { - this.renderer.drawPixel(col, row, this.frame, this.mainCanvas, this.dpi); + this.renderer.drawPixel(col, row, this.frame); }; ns.DrawingController.prototype.renderOverlay = function () { - this.renderer.render(this.overlay, this.overlayCanvas, this.dpi); + this.overlayRenderer.render(this.overlayFrame); }; ns.DrawingController.prototype.clearOverlay = function () { - this.overlay = pskl.model.Frame.createEmptyFromFrame(this.frame); - this.overlayCanvas.getContext("2d").clearRect(0, 0, this.overlayCanvas.width, this.overlayCanvas.height); - }; - - ns.DrawingController.prototype.createMainCanvas = function () { - var mainCanvas = this.createCanvas(); - mainCanvas.className = "drawing-canvas"; - this.container.appendChild(mainCanvas); - return mainCanvas; - }; - - // For some tools, we need a fake canvas that overlay the drawing canvas. These tools are - // generally 'drap and release' based tools (stroke, selection, etc) and the fake canvas - // will help to visualize the tool interaction (without modifying the canvas). - ns.DrawingController.prototype.createOverlayCanvas = function () { - var overlayCanvas = this.createCanvas(); - overlayCanvas.className = "canvas-overlay"; - this.container.appendChild(overlayCanvas); - return overlayCanvas; - }; - - // For some tools, we need a fake canvas that overlay the drawing canvas. These tools are - // generally 'drap and release' based tools (stroke, selection, etc) and the fake canvas - // will help to visualize the tool interaction (without modifying the canvas). - ns.DrawingController.prototype.createCanvas = function () { - var width = this.frame.getWidth(), - height = this.frame.getHeight(); - - var canvas = document.createElement("canvas"); - canvas.setAttribute("width", width * this.dpi); - canvas.setAttribute("height", height * this.dpi); - - return canvas; + this.overlayFrame = pskl.model.Frame.createEmptyFromFrame(this.frame); + this.overlayRenderer.clear(); }; })(); \ No newline at end of file diff --git a/js/controller/PreviewFilmController.js b/js/controller/PreviewFilmController.js index 4da2be07..8317066e 100644 --- a/js/controller/PreviewFilmController.js +++ b/js/controller/PreviewFilmController.js @@ -5,14 +5,14 @@ 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 () { @@ -140,9 +140,9 @@ * TODO(vincz): clean this giant rendering function & remove listeners. */ ns.PreviewFilmController.prototype.createPreviewTile_ = function(tileNumber) { - var frame = this.framesheet.getFrameByIndex(tileNumber); - var width = frame.getWidth() * this.dpi, - height = frame.getHeight() * this.dpi; + var currentFrame = this.framesheet.getFrameByIndex(tileNumber); + //var width = frame.getWidth() * this.dpi, + // height = frame.getHeight() * this.dpi; var previewTileRoot = document.createElement("li"); var classname = "preview-tile"; @@ -155,18 +155,19 @@ var canvasContainer = document.createElement("div"); canvasContainer.className = "canvas-container"; - canvasContainer.setAttribute('style', 'width:' + width + 'px; height:' + height + 'px;'); + //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')) { @@ -182,8 +183,14 @@ piskel.duplicateFrame(tileNumber); }); - this.renderer.render(this.framesheet.getFrameByIndex(tileNumber), canvasPreview, this.dpi); - canvasContainer.appendChild(canvasPreview); + //this.renderer.render(this.framesheet.getFrameByIndex(tileNumber), canvasPreview); + + // TODO(vincz): Eventually optimize this part by not recreating a FrameRenderer. Note that the real optim + // is to make this update function (#createPreviewTile) less aggressive. + var renderingOptions = {"dpi": this.dpi }; + var currentFrameRenderer = new pskl.rendering.FrameRenderer(canvasContainer, renderingOptions, "tile-view"); + currentFrameRenderer.init(currentFrame); + previewTileRoot.appendChild(canvasContainer); previewTileRoot.appendChild(canvasPreviewDuplicateAction); diff --git a/js/drawingtools/Rectangle.js b/js/drawingtools/Rectangle.js index 87709026..36967878 100644 --- a/js/drawingtools/Rectangle.js +++ b/js/drawingtools/Rectangle.js @@ -24,7 +24,7 @@ this.startRow = row; // Drawing the first point of the rectangle in the fake overlay canvas: - drawer.overlay.setPixel(col, row, color); + drawer.overlayFrame.setPixel(col, row, color); drawer.renderOverlay(); }; @@ -42,7 +42,7 @@ if(color == Constants.TRANSPARENT_COLOR) { color = Constants.SELECTION_TRANSPARENT_COLOR; } - drawer.overlay.setPixel(strokePoints[i].col, strokePoints[i].row, color); + drawer.overlayFrame.setPixel(strokePoints[i].col, strokePoints[i].row, color); } drawer.renderOverlay(); }; diff --git a/js/drawingtools/Stroke.js b/js/drawingtools/Stroke.js index 92bb8450..13dabac3 100644 --- a/js/drawingtools/Stroke.js +++ b/js/drawingtools/Stroke.js @@ -34,7 +34,7 @@ // The fake canvas where we will draw the preview of the stroke: // Drawing the first point of the stroke in the fake overlay canvas: - drawer.overlay.setPixel(col, row, color); + drawer.overlayFrame.setPixel(col, row, color); drawer.renderOverlay(); }; @@ -57,7 +57,7 @@ // eg deleting the equivalent of a stroke. color = Constants.SELECTION_TRANSPARENT_COLOR; } - drawer.overlay.setPixel(strokePoints[i].col, strokePoints[i].row, color); + drawer.overlayFrame.setPixel(strokePoints[i].col, strokePoints[i].row, color); } drawer.renderOverlay(); }; diff --git a/js/piskel.js b/js/piskel.js index e195c05d..af4f7a79 100644 --- a/js/piskel.js +++ b/js/piskel.js @@ -11,23 +11,18 @@ $.namespace("pskl"); */ var frameSheet, - // Temporary zoom implementation to easily get bigger canvases to - // see how good perform critical algorithms on big canvas. - zoom = 1, - // Configuration: // Canvas size in pixel size (not dpi related) - framePixelWidth = 32 * zoom, - framePixelHeight = 32 * zoom, - + framePixelWidth = 32, + framePixelHeight = 32, // Scaling factors for a given frameSheet rendering: // Main drawing area: - drawingCanvasDpi = Math.ceil(20/ zoom), - // Canvas previous in the slideshow: - previewTileCanvasDpi = Math.ceil(4 / zoom), - // Ainmated canvas preview: - previewAnimationCanvasDpi = Math.ceil(8 / zoom), + drawingCanvasDpi = 20, + // Canvas preview film canvases: + previewTileCanvasDpi = 4, + // Animated canvas preview: + previewAnimationCanvasDpi = 8, // DOM references: drawingAreaContainer, @@ -53,6 +48,9 @@ $.namespace("pskl"); var piskel = { init : function () { + + piskel.initDPIs_(); + frameSheet = new pskl.model.FrameSheet(framePixelWidth, framePixelHeight); frameSheet.addEmptyFrame(); @@ -95,6 +93,29 @@ $.namespace("pskl"); } }, + /** + * Override default DPIs. + * @private + */ + initDPIs_ : function() { + + drawingCanvasDpi = piskel.calculateDPIsForDrawingCanvas_(); + // TODO(vincz): Add throttling on window.resize event. + $(window).resize($.proxy(function() { + drawingCanvasDpi = piskel.calculateDPIsForDrawingCanvas_(); + this.drawingController.updateDPI(drawingCanvasDpi); + }, this)); + // TODO(vincz): Check for user settings eventually from localstorage. + }, + + /** + * @private + */ + calculateDPIsForDrawingCanvas_ : function() { + var availableViewportHeight = $('.main-panel').height(); + return Math.floor(availableViewportHeight / framePixelHeight); + }, + finishInit : function () { $.subscribe(Events.TOOL_SELECTED, function(evt, toolBehavior) { @@ -186,8 +207,6 @@ $.namespace("pskl"); document.body.addEventListener('mouseup', this.onMouseup.bind(this)); drawingAreaContainer.addEventListener('mousedown', this.onMousedown.bind(this)); drawingAreaContainer.addEventListener('mousemove', this.onMousemove.bind(this)); - drawingAreaContainer.style.width = framePixelWidth * drawingCanvasDpi + "px"; - drawingAreaContainer.style.height = framePixelHeight * drawingCanvasDpi + "px"; document.body.addEventListener('contextmenu', this.onCanvasContextMenu); }, diff --git a/js/rendering/FrameRenderer.js b/js/rendering/FrameRenderer.js index 8b630579..63b96533 100644 --- a/js/rendering/FrameRenderer.js +++ b/js/rendering/FrameRenderer.js @@ -1,20 +1,49 @@ (function () { var ns = $.namespace("pskl.rendering"); - ns.FrameRenderer = function () {}; - ns.FrameRenderer.prototype.render = function (frame, canvas, dpi) { + this.dpi = null; + this.canvas = null; + + ns.FrameRenderer = function (container, renderingOptions, className) { + if(container == undefined) { + throw "Bad FrameRenderer initialization. undefined."; + } + this.container = container; + + if(renderingOptions == undefined || renderingOptions.dpi == undefined || isNaN(dpi)) { + throw "Bad FrameRenderer initialization. not well defined."; + } + + this.displayGrid = !!renderingOptions.displayGrid; + this.dpi = renderingOptions.dpi; + this.className = className; + + // Flag to know if the config was altered + this.canvasConfigDirty = true; + }; + + ns.FrameRenderer.prototype.init = function (frame) { + this.render(frame); + }; + + ns.FrameRenderer.prototype.updateDPI = function (newDPI) { + this.dpi = newDPI; + this.canvasConfigDirty = true; + }; + + ns.FrameRenderer.prototype.render = function (frame) { for(var col = 0, width = frame.getWidth(); col < width; col++) { for(var row = 0, height = frame.getHeight(); row < height; row++) { - this.drawPixel(col, row, frame, canvas, dpi); + this.drawPixel(col, row, frame, this.getCanvas_(frame), this.dpi); } } }; - ns.FrameRenderer.prototype.drawPixel = function (col, row, frame, canvas, dpi) { - var context = canvas.getContext('2d'); + ns.FrameRenderer.prototype.drawPixel = function (col, row, frame) { + var context = this.getCanvas_(frame).getContext('2d'); var color = frame.getPixel(col, row); if(color == Constants.TRANSPARENT_COLOR) { - context.clearRect(col * dpi, row * dpi, dpi, dpi); + context.clearRect(col * this.dpi, row * this.dpi, this.dpi, this.dpi); } else { if(color != Constants.SELECTION_TRANSPARENT_COLOR) { @@ -22,7 +51,39 @@ $.publish(Events.COLOR_USED, [color]); } context.fillStyle = color; - context.fillRect(col * dpi, row * dpi, dpi, dpi); + context.fillRect(col * this.dpi, row * this.dpi, this.dpi, this.dpi); } }; + + ns.FrameRenderer.prototype.clear = function (col, row, frame) { + var canvas = this.getCanvas_(frame) + canvas.getContext("2d").clearRect(0, 0, canvas.width, canvas.height); + }; + + /** + * @private + */ + ns.FrameRenderer.prototype.getCanvas_ = function (frame) { + if(this.canvasConfigDirty) { + $(this.canvas).remove(); + var width = frame.getWidth(), + height = frame.getHeight(); + + var canvas = document.createElement("canvas"); + canvas.setAttribute("width", width * this.dpi); + canvas.setAttribute("height", height * this.dpi); + + var canvasClassname = "canvas"; + if(this.className) { + canvasClassname += " " + this.className; + } + canvas.setAttribute("class", canvasClassname); + + this.canvas = canvas; + this.container.appendChild(this.canvas); + + this.canvasConfigDirty = false; + } + return this.canvas; + }; })(); \ No newline at end of file