diff --git a/js/piskel.js b/js/piskel.js index 3600c233..38a067da 100644 --- a/js/piskel.js +++ b/js/piskel.js @@ -3,7 +3,6 @@ // Constants: TRANSPARENT_COLOR = 'tc', - //TRANSPARENT_COLOR = 'pink', DEFAULT_PEN_COLOR = '#000000', // Configuration: @@ -52,7 +51,6 @@ frameSheet = FrameSheetModel.getInstance(framePixelWidth, framePixelHeight); frameSheet.addEmptyFrame(); this.setActiveFrame(0); - this.initPalette(); this.initDrawingArea(); this.initPreviewSlideshow(); @@ -157,7 +155,6 @@ body.setAttribute('onmouseup', 'piskel.onCanvasMouseup(event)'); drawingAreaContainer.setAttribute('onmousedown', 'piskel.onCanvasMousedown(event)'); drawingAreaContainer.setAttribute('onmousemove', 'piskel.onCanvasMousemove(event)'); - this.drawFrameToCanvas(frameSheet.getFrameByIndex(this.getActiveFrameIndex()), drawingAreaCanvas, drawingCanvasDpi); }, @@ -218,6 +215,7 @@ } $("display-fps").innerHTML = animPreviewFPS + " fps"; animFPSTuner.value = animPreviewFPS; + $("fps-value").innerHTML = animPreviewFPS + " fps"; refreshUpdater = startPreviewRefresh(); }); }, @@ -434,4 +432,5 @@ window.piskel = piskel; piskel.init(); -})(function(id){return document.getElementById(id)}); \ No newline at end of file +})(function(id){return document.getElementById(id)}); +//small change for checking my git setup :( diff --git a/preview/css/piskel.css b/preview/css/piskel.css new file mode 100644 index 00000000..c384327b --- /dev/null +++ b/preview/css/piskel.css @@ -0,0 +1,36 @@ +html, body { + height : 100%; +} + +.debug { + border : 1px Solid black; +} + +.left-nav { + position:absolute; + top : 0; + bottom : 0; + width : 200px; + background : #000; +} + +.main-panel { + position:absolute; + top : 0; + bottom : 0; + left : 200px; + right : 0; + background : #ccc; +} + +.preview-container { + position : absolute; + top : 30px; + right : 0; + height : 200px; + width : 200px; + background : white; + border : 0px Solid black; + border-radius:5px 0px 0px 5px; + box-shadow : 0px 0px 2px rgba(0,0,0,0.2); +} \ No newline at end of file diff --git a/preview/css/style.css b/preview/css/style.css new file mode 100644 index 00000000..9136c5f0 --- /dev/null +++ b/preview/css/style.css @@ -0,0 +1,140 @@ +html, body { + height : 100%; + margin : 0; + cursor : default; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +ul, li { + margin : 0; + padding : 0; +} + +.debug { + border : 1px Solid black; +} + +.left-nav { + position:absolute; + top : 0; + bottom : 0; + width : 200px; + overflow-y: scroll; + background : #000; + padding : 10px; +} + +.main-panel { + position:absolute; + top : 0; + bottom : 0; + left : 220px; + right : 0; + background : #ccc; +} + +.preview-container { + position : absolute; + top : 30px; + right : 0; + height : 256px; + width : 256px; + background : white; + border : 0px Solid black; + border-radius:5px 0px 0px 5px; + box-shadow : 0px 0px 2px rgba(0,0,0,0.2); +} + +.preview-container canvas { + border : 0px Solid transparent; + border-radius:5px 0px 0px 5px; +} + +#cursorInfo { + position : fixed; + cursor : default; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.action-button { + background-color : white; + width : 150px; + display : inline-block; +} + +#preview-list { + list-style-type: none; +} + +.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 { + display: none; + float: right; +} + +.preview-tile:hover .tile-action { + display: block; +} + +.preview-tile:hover { + background-color: lightgray; +} + +#preview-list .preview-tile.selected { + 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; +} + +::-webkit-scrollbar-thumb { + border-radius: 4px; + background-color: rgba(180,180,180,.7); + -webkit-box-shadow: 0 0 1px rgba(255,255,255,.5); +} \ No newline at end of file diff --git a/preview/img/transparent_background.png b/preview/img/transparent_background.png new file mode 100644 index 00000000..4a1f7a8e Binary files /dev/null and b/preview/img/transparent_background.png differ diff --git a/preview/index.html b/preview/index.html new file mode 100644 index 00000000..c08fa6e8 --- /dev/null +++ b/preview/index.html @@ -0,0 +1,42 @@ + + + + + + + Piskel + + + + + + + + + +
+ Get URL + + +
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+ + + + diff --git a/preview/js/app.js b/preview/js/app.js new file mode 100644 index 00000000..d66a847f --- /dev/null +++ b/preview/js/app.js @@ -0,0 +1,35 @@ +// Generated by CoffeeScript 1.3.1 +(function() { + + $(document).ready(function() { + var Piskel; + Piskel = Em.Application.create(); + Piskel.mainView = Em.View.create({ + templateName: 'main', + onCanvasClick: function() {} + }); + Piskel.mainView.append; + Piskel.Art = Ember.Object.extend({ + setContext: function(context) { + this.context = context; + }, + clear: function() { + this.context.save(); + return this.context.clearRect(0, 0, this.context.canvas.width, this.context.canvas.height); + } + }); + $('#main').ready(function() { + var context, mainCanvas; + mainCanvas = document.getElementById('main'); + context = mainCanvas.getContext('2d'); + context.save(); + context.clearRect(0, 0, context.canvas.width, context.canvas.height); + context.fillStyle = 'white'; + context.fillRect(0, 0, context.canvas.width, context.canvas.height); + context.restore(); + return Piskel.mainCanvas = document.getElementById('main'); + }); + return window.Piskel = Piskel; + }); + +}).call(this); diff --git a/preview/js/frameSheetModel.js b/preview/js/frameSheetModel.js new file mode 100644 index 00000000..d4d2712b --- /dev/null +++ b/preview/js/frameSheetModel.js @@ -0,0 +1,101 @@ +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; + }; + + var serializeFrame_ = function (frame) { + var buffer = []; + for (var i = 0 ; i < frame.length ; i++) { + var serializedLine = ""; + for(var j = 0 ; j < frame[i].length ; j++) { + if (typeof frame[i][j] == 'undefined' || frame[i][j] == 'tc') { + serializedLine += "0" + } else { + serializedLine += "1" + } + } + buffer.push(parseInt(serializedLine, 2).toString(36)); + + } + return buffer.join(","); + }; + + 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() { + var buffer = []; + for (var i = 0 ; i < frames.length ; i++) { + buffer.push(serializeFrame_(frames[i])); + } + return buffer.join("+"); + //throw "FrameSheet.serialize Not implemented" + }, + + addEmptyFrame: function() { + this.addFrame(createEmptyFrame_()); + }, + + addFrame : function (frame) { + frames.push(frame); + }, + + 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 100) { + animPreviewFPS = 100; + } + animFPSTuner.value = animPreviewFPS; + refreshUpdater = startPreviewRefresh(); + }); + }, + + createPreviews : function () { + var container = $('preview-list'), 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: + // TODO: let me know when you want to start using a framework :) + if(!evt.target.className.match(new RegExp('(\\s|^)'+ 'tile-action' +'(\\s|$)'))) { + piskel.setActiveFrameAndRedraw(tileNumber); + } + }); + + var canvasPreviewDuplicateAction = document.createElement("button"); + canvasPreviewDuplicateAction.className = "tile-action" + canvasPreviewDuplicateAction.innerHTML = "dup" + + canvasPreviewDuplicateAction.addEventListener('click', function(evt) { + piskel.duplicateFrame(tileNumber); + }); + + this.drawFrameToCanvas(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) { + frameSheet.removeFrameByIndex(tileNumber); + animIndex = 0; + piskel.createPreviews(); + }); + previewTileRoot.appendChild(canvasPreviewDeleteAction); + } + + return previewTileRoot; + }, + + refreshAnimatedPreview : function () { + piskel.drawFrameToCanvas(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); + }, + + updateCursorInfo : function (event) { + var cursor = $('cursorInfo'); + cursor.style.top = event.clientY + 10 + "px"; + cursor.style.left = event.clientX + 10 + "px"; + + var coordinates = this.getRelativeCoordinates(event.clientX, event.clientY) + cursor.innerHTML = [ + "X : " + coordinates.x, + "Y : " + coordinates.y + ].join(", "); + }, + + onCanvasMousedown : function (event) { + isClicked = true; + var coords = this.getRelativeCoordinates(event.clientX, event.clientY); + 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, 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); + }, + + // 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) { + event.preventDefault(); + event.stopPropagation(); + event.cancelBubble = true; + return false; + }, + + getRelativeCoordinates : function (x, y) { + var canvasRect = drawingAreaCanvas.getBoundingClientRect(); + return { + x : x - canvasRect.left, + y : y - canvasRect.top + } + } + }; + + window.piskel = piskel; + piskel.init(); + +})(function(id){return document.getElementById(id)}); \ No newline at end of file