diff --git a/css/style.css b/css/style.css index 7008f476..9136c5f0 100644 --- a/css/style.css +++ b/css/style.css @@ -76,13 +76,19 @@ ul, li { list-style-type: none; } -#preview-list .preview-tile { +.preview-tile { padding : 10px; overflow: hidden; + background-color: gray; +} + +.preview-tile .canvas-container { + float: left; } .preview-tile .tile-view { float: left; + border: blue 1px solid; } .preview-tile .tile-action { @@ -99,9 +105,29 @@ ul, li { } #preview-list .preview-tile.selected { - background-color: gray; + background-color: lightyellow; } +.canvas-container { + position: relative; + display: block; +} + +.canvas-container .canvas-background { + background: url(../img/transparent_background.png) repeat; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; +} + +.canvas { + position: relative; + z-index: 1; +} + +/* Force apparition of scrollbars on leopard */ ::-webkit-scrollbar { -webkit-appearance: none; width: 7px; diff --git a/img/transparent_background.png b/img/transparent_background.png new file mode 100644 index 00000000..4a1f7a8e Binary files /dev/null and b/img/transparent_background.png differ diff --git a/index.html b/index.html index bd5efe8b..6f9ae98c 100644 --- a/index.html +++ b/index.html @@ -13,27 +13,29 @@ - +
-
-
+
+
+
- +
+
+
+ diff --git a/js/frameSheetModel.js b/js/frameSheetModel.js new file mode 100644 index 00000000..5ea52a0d --- /dev/null +++ b/js/frameSheetModel.js @@ -0,0 +1,76 @@ + +var FrameSheetModel = (function() { + + var inst; + var frames = []; + var width; + var height; + + var createEmptyFrame_ = function() { + var emptyFrame = new Array(width); + for (var columnIndex=0; columnIndex < width; columnIndex++) { + emptyFrame[columnIndex] = new Array(height); + } + return emptyFrame; + }; + + return { + validate: function() { + return true; // I'm always right dude + }, + + // Could be use to pass around model using long GET param (good enough for simple models) and + // do some temporary locastorage + serialize: function() { + throw "FrameSheet.serialize Not implemented" + }, + + addEmptyFrame: function() { + frames.push(createEmptyFrame_()); + }, + + getFrameCount: function() { + return frames.length; + }, + + getFrameByIndex: function(index) { + if (isNaN(index)) { + throw "Bad argument value for getFrameByIndex method: <" + index + ">" + } else if (index < 0 || index > frames.length) { + throw "Out of bound index for frameSheet object." + } + + return frames[index]; + }, + + removeFrameByIndex: function(index) { + if(index < 0 || index > inst.getFrameCount()) { + throw "Bad index value for removeFrameByIndex."; + } + frames.splice(index, 1); + }, + + duplicateFrameByIndex: function(frameToDuplicateIndex) { + var frame = inst.getFrameByIndex(frameToDuplicateIndex); + var clonedFrame = []; + for(var i=0, l=frame.length; i 1) { + if(tileNumber > 0 || frameSheet.getFrameCount() > 1) { var canvasPreviewDeleteAction = document.createElement("button"); canvasPreviewDeleteAction.className = "tile-action" canvasPreviewDeleteAction.innerHTML = "del" - canvasPreviewDeleteAction.setAttribute('onclick', 'piskel.removeFrame('+ tileNumber +')'); - preview.appendChild(canvasPreviewDeleteAction); + canvasPreviewDeleteAction.addEventListener('click', function(evt) { + frameSheet.removeFrameByIndex(tileNumber); + animIndex = 0; + piskel.createPreviews(); + }); + previewTileRoot.appendChild(canvasPreviewDeleteAction); } - return preview; + return previewTileRoot; }, refreshAnimatedPreview : function () { - var context = $('animated-preview').getContext('2d'); - // erase canvas, verify proper way - context.fillStyle = "white"; - context.fillRect(0, 0, 256, 256); - - context.drawImage(frames[animIndex++], 0, 0, 320, 320, 0, 0 , 256, 256); - if (animIndex == frames.length) { + piskel.drawFrameToCanvas(frameSheet.getFrameByIndex(animIndex), previewCanvas, previewAnimationCanvasDpi); + animIndex++; + if (animIndex == frameSheet.getFrameCount()) { animIndex = 0; } }, - setFrame : function (frameIndex) { - index = frameIndex; - $('canvas-container').innerHTML = ""; - $('canvas-container').appendChild(this.getCurrentCanvas()); - this.createPreviews(); - }, + removeFrame: function(frameIndex) { + frameSheet.removeFrameByIndex(frameIndex); - removeFrame: function(frameIndex) { - index = frameIndex - 1 < 0 ? 0 : frameIndex - 1; - animIndex = 0; - frames.splice(frameIndex, 1); - $('canvas-container').innerHTML = ""; - $('canvas-container').appendChild(this.getCurrentCanvas()); - this.createPreviews(); + this.setActiveFrameAndRedraw(frameIndex - 1); }, duplicateFrame: function(frameIndex) { - index = frameIndex + 1; - animIndex = 0; - var duplicateCanvas = frames[frameIndex].cloneNode(true); - // Copy canvas content: - var context = duplicateCanvas.getContext('2d'); - context.drawImage(frames[frameIndex], 0, 0); + frameSheet.duplicateFrameByIndex(frameIndex); - // Insert cloned node into frame collection: - frames.splice(frameIndex + 1, 0, duplicateCanvas); - $('canvas-container').innerHTML = ""; - $('canvas-container').appendChild(this.getCurrentCanvas()); - this.createPreviews(); + this.setActiveFrameAndRedraw(frameIndex + 1); }, updateCursorInfo : function (event) { @@ -155,26 +243,73 @@ onCanvasMousedown : function (event) { isClicked = true; - button = event.button; var coords = this.getRelativeCoordinates(event.clientX, event.clientY); - this.drawAt(coords.x, coords.y); + if(event.button == 0) { + this.drawAt(coords.x, coords.y, penColor); + } else { + // Right click used to delete. + isRightClicked = true; + this.drawAt(coords.x, coords.y, TRANSPARENT_COLOR); + } + }, + + onCanvasMousemove : function (event) { + //this.updateCursorInfo(event); + if (isClicked) { + var coords = this.getRelativeCoordinates(event.clientX, event.clientY); + if(isRightClicked) { + this.drawAt(coords.x, coords.y, TRANSPARENT_COLOR); + } else { + this.drawAt(coords.x, coords.y, penColor); + } + } }, onCanvasMouseup : function (event) { + if(isClicked || isRightClicked) { + // A mouse button was clicked on the drawing canvas before this mouseup event, + // the user was probably drawing on the canvas. + // Note: The mousemove movement (and the mouseup) may end up outside + // of the drawing canvas. + this.createPreviews(); + } isClicked = false; + isRightClicked = false; }, - drawAt : function (x, y) { - if (x < 0 || y < 0 || x > 320 || y > 320) return; - var context = this.getCurrentCanvas().getContext('2d'); - if (button == 0) { - context.fillStyle = "black"; - } else { - context.fillStyle = "white"; - } + drawAt : function (x, y, color) { + var pixelWidthIndex = (x - x%drawingCanvasDpi) / 10; + var pixelHeightIndex = (y - y%drawingCanvasDpi) / 10; + + // Update model: + var currentFrame = frameSheet.getFrameByIndex(this.getActiveFrameIndex()); + + // TODO: make a better accessor for pixel state update: + // TODO: Make pen color dynamic: + currentFrame[pixelWidthIndex][pixelHeightIndex] = color; + + // Update view: + // TODO: Create a per pixel update function for perf ? + this.drawFrameToCanvas(currentFrame, drawingAreaCanvas, drawingCanvasDpi); + }, - context.fillRect(x - x%brushSize, y - y%brushSize, brushSize, brushSize); - this.createPreviews(); + // TODO: move that to a FrameRenderer (/w cache) ? + drawFrameToCanvas: function(frame, canvasElement, dpi) { + var pixelColor, context = canvasElement.getContext('2d'); + for(var col = 0, num_col = frame.length; col < num_col; col++) { + for(var row = 0, num_row = frame[col].length; row < num_row; row++) { + pixelColor = frame[col][row]; + + if(pixelColor == undefined || pixelColor == TRANSPARENT_COLOR) { + context.clearRect(col * dpi, row * dpi, dpi, dpi); + } else { + context.fillStyle = pixelColor; + context.fillRect(col * dpi, row * dpi, dpi, dpi); + } + + + } + } }, onCanvasContextMenu : function (event) { @@ -183,35 +318,13 @@ event.cancelBubble = true; return false; }, + getRelativeCoordinates : function (x, y) { - var canvas = this.getCurrentCanvas(); - var canvasRect = canvas.getBoundingClientRect(); + var canvasRect = drawingAreaCanvas.getBoundingClientRect(); return { x : x - canvasRect.left, y : y - canvasRect.top } - }, - - addFrame : function () { - var canvas = document.createElement("canvas"); - canvas.setAttribute('width', '320'); - canvas.setAttribute('height', '320'); - canvas.setAttribute('onmousemove', 'piskel.onCanvasMousemove(arguments[0])'); - canvas.setAttribute('oncontextmenu', 'piskel.onCanvasContextMenu(arguments[0])'); - //canvas.setAttribute('onclick', 'piskel.onCanvasClick(arguments[0])'); - var context = canvas.getContext('2d'); - - context.fillStyle = "white"; - context.fillRect(0, 0, 320, 320); - - if(frames[index]) { //is a valid canvas - context.drawImage(frames[index], 0, 0, 320, 320, 0, 0 , 320, 320); - } - - // TODO: We should probably store some metadata or enhance a domain object instead - // of the rendered view ? It will allow to decouple view and model and clean a bunch of code above. - frames.push(canvas); - this.setFrame(frames.length - 1); } };