diff --git a/css/style.css b/css/style.css index 8ef86e8d..ce04f1d1 100644 --- a/css/style.css +++ b/css/style.css @@ -127,6 +127,26 @@ ul, li { z-index: 1; } +.drawing-canvas-container { + float: left; +} + +.tool-paint-bucket .drawing-canvas-container:hover { + cursor: url(../img/tools/paint-bucket.png) 18 17, pointer; +} + +.tool-pen .drawing-canvas-container:hover { + cursor: url(../img/tools/pen.png) 7 21, pointer; +} + +.tool-eraser .drawing-canvas-container:hover { + cursor: url(../img/tools/eraser.png) 5 21, pointer; +} + +/** + * Tool section: + */ + #palette li { display : inline-block; height : 20px; @@ -134,11 +154,55 @@ ul, li { margin : 5px; } +.tools-container { + float: left; +} + +.tools-container .tool-icon { + display: block; + float: left; + margin-right: 2px; +} + +.tool-icon { + width: 30px; + height: 30px; + border: 5px solid #fff; +} + +.tool-icon:hover { + border-color: #; +} + +.tool-icon:hover { + cursor: pointer; + border: 5px solid #eee; +} + +.tool-icon.selected { + cursor: auto; + border: 5px solid #ddd; +} + +.tool-icon.tool-pen { + background: #fff url(../img/tools/pen.png) 3px 3px no-repeat; +} + +.tool-icon.tool-paint-bucket { + background: #fff url(../img/tools/paint-bucket.png) 3px 3px no-repeat; +} + +.tool-icon.tool-eraser { + background: #fff url(../img/tools/eraser.png) 3px 3px no-repeat; +} + #preview-fps { width : 200px; } -/* User messages */ +/** + * User messages + */ .user-message { position: fixed; bottom: 0; diff --git a/img/tools/.DS_Store b/img/tools/.DS_Store new file mode 100644 index 00000000..5008ddfc Binary files /dev/null and b/img/tools/.DS_Store differ diff --git a/img/tools/eraser.png b/img/tools/eraser.png new file mode 100644 index 00000000..b9b7522e Binary files /dev/null and b/img/tools/eraser.png differ diff --git a/img/tools/paint-bucket.png b/img/tools/paint-bucket.png new file mode 100644 index 00000000..4d3e6901 Binary files /dev/null and b/img/tools/paint-bucket.png differ diff --git a/img/tools/pen.png b/img/tools/pen.png new file mode 100644 index 00000000..c6768e89 Binary files /dev/null and b/img/tools/pen.png differ diff --git a/index.html b/index.html index b28787d6..80c61854 100644 --- a/index.html +++ b/index.html @@ -23,12 +23,16 @@
-
+
- - +
+
+
+
+ +
    +
    @@ -42,9 +46,20 @@
    - + + + + + + + + + + + + diff --git a/js/Constants.js b/js/Constants.js new file mode 100644 index 00000000..abd47a36 --- /dev/null +++ b/js/Constants.js @@ -0,0 +1,3 @@ +var Constants = { + TRANSPARENT_COLOR : "tc" +}; \ No newline at end of file diff --git a/js/Events.js b/js/Events.js new file mode 100644 index 00000000..a5cbfd7f --- /dev/null +++ b/js/Events.js @@ -0,0 +1,6 @@ +Events = { + + TOOL_SELECTED : "TOOL_SELECTED", + CANVAS_RIGHT_CLICKED: "CANVAS_RIGHT_CLICKED", + CANVAS_RIGHT_CLICK_RELEASED: "CANVAS_RIGHT_CLICK_RELEASED" +}; \ No newline at end of file diff --git a/js/ToolSelector.js b/js/ToolSelector.js new file mode 100644 index 00000000..45bae855 --- /dev/null +++ b/js/ToolSelector.js @@ -0,0 +1,84 @@ +/* + * @provide pskl.ToolSelector + * + * @require Constants + * @require Events + * @require pskl.drawingtools + */ + +pskl.ToolSelector = (function() { + + var toolInstances = { + "simplePen" : new pskl.drawingtools.SimplePen(), + "eraser" : new pskl.drawingtools.Eraser(), + "paintBucket" : new pskl.drawingtools.PaintBucket() + }; + var currentSelectedTool = toolInstances.simplePen; + var previousSelectedTool = toolInstances.simplePen; + + var selectTool_ = function(tool) { + + var maincontainer = $("body"); + var previousSelectedToolClass = maincontainer.data("selected-tool-class"); + if(previousSelectedToolClass) { + maincontainer.removeClass(previousSelectedToolClass); + } + maincontainer.addClass(toolBehavior.toolId); + $("#drawing-canvas-container").data("selected-tool-class", toolBehavior.toolId); + }; + + var activateToolOnStage_ = function(tool) { + var stage = $("body"); + var previousSelectedToolClass = stage.data("selected-tool-class"); + if(previousSelectedToolClass) { + stage.removeClass(previousSelectedToolClass); + } + stage.addClass(tool.toolId); + stage.data("selected-tool-class", tool.toolId); + }; + + var selectTool_ = function(tool) { + console.log("Selecting Tool:" , currentSelectedTool); + currentSelectedTool = tool; + activateToolOnStage_(currentSelectedTool); + $.publish(Events.TOOL_SELECTED, [tool]); + }; + + var onToolIconClicked_ = function(evt) { + var target = $(evt.target); + var clickedTool = target.closest(".tool-icon"); + + if(clickedTool.length) { + for(var tool in toolInstances) { + if (toolInstances[tool].toolId == clickedTool.data()["toolId"]) { + selectTool_(toolInstances[tool]); + + // Show tool as selected: + $("#tools-container .tool-icon.selected").removeClass("selected"); + clickedTool.addClass("selected"); + } + } + } + }; + + return { + init: function() { + $.subscribe(Events.CANVAS_RIGHT_CLICKED, function() { + previousSelectedTool = currentSelectedTool; + currentSelectedTool = toolInstances.eraser; + $.publish(Events.TOOL_SELECTED, [currentSelectedTool]); + }); + + $.subscribe(Events.CANVAS_RIGHT_CLICK_RELEASED, function() { + currentSelectedTool = previousSelectedTool; + $.publish(Events.TOOL_SELECTED, [currentSelectedTool]); + }); + + // Set SimplePen as default selected tool: + selectTool_(toolInstances.simplePen); + + // Activate listener on tool panel: + $("#tools-container").click(onToolIconClicked_); + } + }; +})() \ No newline at end of file diff --git a/js/drawingtools/BaseTool.js b/js/drawingtools/BaseTool.js new file mode 100644 index 00000000..cd102510 --- /dev/null +++ b/js/drawingtools/BaseTool.js @@ -0,0 +1,39 @@ +/* + * @provide pskl.drawingtools.BaseTool + * + * @require pskl.utils + */ +(function() { + var ns = $.namespace("pskl.drawingtools"); + + ns.BaseTool = function() {}; + + ns.BaseTool.prototype.applyToolOnFrameAt = function(col, row, frame, color) {}; + + ns.BaseTool.prototype.applyToolOnCanvasAt = function(col, row, canvas, color, dpi) {}; + + ns.BaseTool.prototype.releaseToolAt = function() {}; + + ns.BaseTool.prototype.drawPixelInCanvas = function (col, row, canvas, color, dpi) { + var context = canvas.getContext('2d'); + if(color == undefined || color == Constants.TRANSPARENT_COLOR) { + context.clearRect(col * dpi, row * dpi, dpi, dpi); + } + else { + // TODO(vincz): Remove this global access to piskel when Palette component is created. + piskel.addColorToPalette(color); + context.fillStyle = color; + context.fillRect(col * dpi, row * dpi, dpi, dpi); + } + }; + + ns.BaseTool.prototype.drawFrameInCanvas = function (frame, canvas, dpi) { + var color; + 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++) { + color = pskl.utils.normalizeColor(frame[col][row]); + this.drawPixelInCanvas(col, row,canvas, color, dpi); + } + } + }; +})(); diff --git a/js/drawingtools/Eraser.js b/js/drawingtools/Eraser.js new file mode 100644 index 00000000..c6592c38 --- /dev/null +++ b/js/drawingtools/Eraser.js @@ -0,0 +1,38 @@ +/* + * @provide pskl.drawingtools.Eraser + * + * @require Constants + * @require pskl.utils + */ + (function() { + var ns = $.namespace("pskl.drawingtools"); + + ns.Eraser = function() { + this.toolId = "tool-eraser"; + }; + + pskl.utils.inherit(ns.Eraser, ns.BaseTool); + + /** + * @override + */ + ns.Eraser.prototype.applyToolOnFrameAt = function(col, row, frame, color) { + frame[col][row] = Constants.TRANSPARENT_COLOR; + }; + + /** + * @override + */ + ns.Eraser.prototype.applyToolOnCanvasAt = function(col, row, canvas, frame, color, dpi) { + + this.drawPixelInCanvas(col, row, canvas, Constants.TRANSPARENT_COLOR, dpi); + }; + + /** + * @override + */ + ns.Eraser.prototype.releaseToolAt = function() { + // Do nothing + console.log('Eraser release'); + }; +})(); \ No newline at end of file diff --git a/js/drawingtools/PaintBucket.js b/js/drawingtools/PaintBucket.js new file mode 100644 index 00000000..0a55d411 --- /dev/null +++ b/js/drawingtools/PaintBucket.js @@ -0,0 +1,158 @@ +/* + * @provide pskl.drawingtools.PaintBucket + * + * @require pskl.utils + */ +(function() { + var ns = $.namespace("pskl.drawingtools"); + + ns.PaintBucket = function() { + this.toolId = "tool-paint-bucket" + }; + + pskl.utils.inherit(ns.PaintBucket, ns.BaseTool); + + /** + * @override + */ + ns.PaintBucket.prototype.applyToolOnFrameAt = function(col, row, frame, color) {}; + + /** + * @override + */ + ns.PaintBucket.prototype.applyToolOnCanvasAt = function(col, row, canvas, frame, replacementColor, dpi) { + + var targetColor = pskl.utils.normalizeColor(frame[col][row]); + //this.recursiveFloodFill(frame, col, row, targetColor, replacementColor); + this.queueLinearFloodFill(frame, col, row, targetColor, replacementColor); + this.drawFrameInCanvas(frame, canvas, dpi); + }; + + /** + * @override + */ + ns.PaintBucket.prototype.releaseToolAt = function() { + // Do nothing + console.log('PaintBucket release'); + }; + + /** + * Flood-fill (node, target-color, replacement-color): + 1. Set Q to the empty queue. + 2. If the color of node is not equal to target-color, return. + 3. Add node to Q. + 4. For each element n of Q: + 5. If the color of n is equal to target-color: + 6. Set w and e equal to n. + 7. Move w to the west until the color of the node to the west of w no longer matches target-color. + 8. Move e to the east until the color of the node to the east of e no longer matches target-color. + 9. Set the color of nodes between w and e to replacement-color. + 10. For each node n between w and e: + 11. If the color of the node to the north of n is target-color, add that node to Q. + 12. If the color of the node to the south of n is target-color, add that node to Q. + 13. Continue looping until Q is exhausted. + 14. Return. + */ + ns.PaintBucket.prototype.queueLinearFloodFill = function(frame, col, row, targetColor, replacementColor) { + + var queue = []; + var dy = [-1, 0, 1, 0]; + var dx = [0, 1, 0, -1]; + + try { + if(frame[col][row] == replacementColor) { + return; + } + } catch(e) { + // Frame out of bound exception. + } + + var isInFrameBound = function(frame_, col_, row_) { + if( col_ < 0 || + col_ >= frame_.length || + row_ < 0 || + row_ >= frame_[0].length) { + return false; + } + return true; + } + + queue.push({"col": col, "row": row}); + var loopCount = 0; + var cellCount = frame.length * frame[0].length; + while(queue.length > 0) { + loopCount ++; + + var currentItem = queue.pop(); + frame[currentItem.col][currentItem.row] = replacementColor; + + for (var i = 0; i < 4; i++) { + var nextCol = currentItem.col + dx[i] + var nextRow = currentItem.row + dy[i] + try { + if (isInFrameBound(frame, nextCol, nextRow) && frame[nextCol][nextRow] == targetColor) { + queue.push({"col": nextCol, "row": nextRow }); + } + } catch(e) { + // Frame out of bound exception. + } + } + + // Security loop breaker: + if(loopCount > 10 * cellCount) { + console.log("loop breaker called") + break; + } + } + } + + /** + * Basic Flood-fill implementation (Stack explosion !): + * Flood-fill (node, target-color, replacement-color): + * 1. If the color of node is not equal to target-color, return. + * 2. Set the color of node to replacement-color. + * 3. Perform Flood-fill (one step to the west of node, target-color, replacement-color). + * Perform Flood-fill (one step to the east of node, target-color, replacement-color). + * Perform Flood-fill (one step to the north of node, target-color, replacement-color). + * Perform Flood-fill (one step to the south of node, target-color, replacement-color). + * 4. Return. + * + * @private + */ + ns.PaintBucket.prototype.recursiveFloodFill = function(frame, col, row, targetColor, replacementColor) { + + // Step 1: + if( col < 0 || + col >= frame.length || + row < 0 || + row >= frame[0].length || + frame[col][row] != targetColor) { + return; + } + + // Step 2: + frame[col][row] = replacementColor; + + //Step 3: + this.simpleFloodFill(frame, col - 1, row, targetColor, replacementColor); + this.simpleFloodFill(frame, col + 1, row, targetColor, replacementColor); + this.simpleFloodFill(frame, col, row - 1, targetColor, replacementColor); + this.simpleFloodFill(frame, col, row + 1, targetColor, replacementColor); + + return; + }; + +})(); + + + + + + + + + + + + + diff --git a/js/drawingtools/SimplePen.js b/js/drawingtools/SimplePen.js new file mode 100644 index 00000000..c21c1cc1 --- /dev/null +++ b/js/drawingtools/SimplePen.js @@ -0,0 +1,41 @@ +/* + * @provide pskl.drawingtools.SimplePen + * + * @require pskl.utils + */ +(function() { + var ns = $.namespace("pskl.drawingtools"); + + ns.SimplePen = function() { + this.toolId = "tool-pen" + }; + + pskl.utils.inherit(ns.SimplePen, ns.BaseTool); + + /** + * @override + */ + ns.SimplePen.prototype.applyToolOnFrameAt = function(col, row, frame, color) { + var color = pskl.utils.normalizeColor(color); + if (color != frame[col][row]) { + frame[col][row] = color; + } + }; + + /** + * @override + */ + ns.SimplePen.prototype.applyToolOnCanvasAt = function(col, row, canvas, frame, color, dpi) { + + this.drawPixelInCanvas(col, row, canvas, color, dpi); + }; + + /** + * @override + */ + ns.SimplePen.prototype.releaseToolAt = function() { + // Do nothing + console.log('SimplePen release'); + }; + +})(); diff --git a/js/frameSheetModel.js b/js/frameSheetModel.js index f6d215e3..20f2544c 100644 --- a/js/frameSheetModel.js +++ b/js/frameSheetModel.js @@ -1,3 +1,4 @@ + var FrameSheetModel = (function() { var inst; @@ -5,12 +6,22 @@ var FrameSheetModel = (function() { var width; var height; + /** + * @private + */ var createEmptyFrame_ = function() { var emptyFrame = new Array(width); for (var columnIndex=0; columnIndex < width; columnIndex++) { emptyFrame[columnIndex] = new Array(height); } return emptyFrame; + }; + + /** + * @private + */ + var requestLocalStorageSave_ = function() { + }; return { diff --git a/js/lib/.DS_Store b/js/lib/.DS_Store new file mode 100644 index 00000000..7fdcaf84 Binary files /dev/null and b/js/lib/.DS_Store differ diff --git a/js/lib/jsColor_1_4_0/.DS_Store b/js/lib/jsColor_1_4_0/.DS_Store new file mode 100644 index 00000000..56a80359 Binary files /dev/null and b/js/lib/jsColor_1_4_0/.DS_Store differ diff --git a/js/lib/pubsub.js b/js/lib/pubsub.js index 928bb70c..f6f0b168 100644 --- a/js/lib/pubsub.js +++ b/js/lib/pubsub.js @@ -7,6 +7,7 @@ var o = $({}); $.subscribe = function() { + console.log("SUBSCRIBE: " + arguments[0]); o.on.apply(o, arguments); }; @@ -15,7 +16,10 @@ }; $.publish = function() { + console.log("PUBLISH: " + arguments[0]); o.trigger.apply(o, arguments); }; -}(jQuery)); \ No newline at end of file +}(jQuery)); + + diff --git a/js/piskel.js b/js/piskel.js index 1064ecf8..fd2fd40a 100644 --- a/js/piskel.js +++ b/js/piskel.js @@ -1,8 +1,13 @@ -(function ($) { +/** + * @require Constants + * @require Events + */ +$.namespace("pskl"); + +(function () { var frameSheet, // Constants: - TRANSPARENT_COLOR = 'tc', DEFAULT_PEN_COLOR = '#000000', PISKEL_SERVICE_URL = 'http://2.piskel-app.appspot.com', @@ -32,10 +37,12 @@ animIndex = 0, penColor = DEFAULT_PEN_COLOR, paletteColors = [], + currentFrame = null; + currentToolBehavior = null, //utility _normalizeColor = function (color) { - if(color == undefined || color == TRANSPARENT_COLOR || color.indexOf("#") == 0) { + if(color == undefined || color == Constants.TRANSPARENT_COLOR || color.indexOf("#") == 0) { return color; } else { return "#" + color; @@ -50,9 +57,9 @@ var piskel = { init : function () { frameSheet = FrameSheetModel.getInstance(framePixelWidth, framePixelHeight); - this.setActiveFrame(0); frameSheet.addEmptyFrame(); - + this.setActiveFrame(0); + var frameId = this.getFrameIdFromUrl(); if (frameId) { this.displayMessage("Loading animation with id : [" + frameId + "]"); @@ -63,12 +70,19 @@ }, finishInit : function () { + + $.subscribe(Events.TOOL_SELECTED, function(evt, toolBehavior) { + console.log("Tool selected: ", toolBehavior); + currentToolBehavior = toolBehavior; + }); + this.initPalette(); this.initDrawingArea(); this.initPreviewSlideshow(); this.initAnimationPreview(); this.initColorPicker(); this.initLocalStorageBackup(); + pskl.ToolSelector.init(); this.startAnimation(); }, @@ -123,6 +137,17 @@ } }, + persistToLocalStorageRequest: function() { + // Persist to localStorage when drawing. We throttle localStorage accesses + // for high frequency drawing (eg mousemove). + if(localStorageThrottler == null) { + localStorageThrottler = window.setTimeout(function() { + piskel.persistToLocalStorage(); + localStorageThrottler = null; + }, 1000); + } + }, + persistToLocalStorage: function() { console.log('persited') window.localStorage['snapShot'] = frameSheet.serialize(); @@ -139,13 +164,14 @@ setActiveFrame: function(index) { activeFrameIndex = index; + currentFrame = frameSheet.getFrameByIndex(activeFrameIndex) }, setActiveFrameAndRedraw: function(index) { this.setActiveFrame(index); // Update drawing canvas: - this.drawFrameToCanvas(frameSheet.getFrameByIndex(this.getActiveFrameIndex()), drawingAreaCanvas, drawingCanvasDpi); + this.drawFrameToCanvas(currentFrame, drawingAreaCanvas, drawingCanvasDpi); // Update slideshow: this.createPreviews(); @@ -162,21 +188,22 @@ }, initColorPicker: function() { - this.colorPicker = $('color-picker'); - this.colorPicker.value = DEFAULT_PEN_COLOR; - this.colorPicker.addEventListener('change', this.onPickerChange.bind(this)); + this.colorPicker = $('#color-picker'); + this.colorPicker.val(DEFAULT_PEN_COLOR); + this.colorPicker.change(this.onPickerChange.bind(this)); }, onPickerChange : function(evt) { - penColor = _normalizeColor(this.colorPicker.value); + var inputPicker = $(evt.target); + penColor = _normalizeColor(inputPicker.val()); }, initPalette : function (color) { - paletteEl = $('palette'); + paletteEl = $('#palette')[0]; }, addColorToPalette : function (color) { - if (color && color != TRANSPARENT_COLOR && paletteColors.indexOf(color) == -1) { + if (color && color != Constants.TRANSPARENT_COLOR && paletteColors.indexOf(color) == -1) { var colorEl = document.createElement("li"); colorEl.setAttribute("data-color", color); colorEl.setAttribute("title", color); @@ -187,7 +214,7 @@ }, initDrawingArea : function() { - drawingAreaContainer = $('drawing-canvas-container'); + drawingAreaContainer = $('#drawing-canvas-container')[0]; drawingAreaCanvas = document.createElement("canvas"); drawingAreaCanvas.className = 'canvas'; @@ -201,14 +228,14 @@ drawingAreaContainer.appendChild(drawingAreaCanvas); var body = document.getElementsByTagName('body')[0]; - body.setAttribute('onmouseup', 'piskel.onCanvasMouseup(event)'); + body.setAttribute('onmouseup', 'piskel.onDocumentBodyMouseup(event)'); drawingAreaContainer.setAttribute('onmousedown', 'piskel.onCanvasMousedown(event)'); drawingAreaContainer.setAttribute('onmousemove', 'piskel.onCanvasMousemove(event)'); - this.drawFrameToCanvas(frameSheet.getFrameByIndex(this.getActiveFrameIndex()), drawingAreaCanvas, drawingCanvasDpi); + this.drawFrameToCanvas(currentFrame, drawingAreaCanvas, drawingCanvasDpi); }, initPreviewSlideshow: function() { - var addFrameButton = $('add-frame-button'); + var addFrameButton = $('#add-frame-button')[0]; addFrameButton.addEventListener('mousedown', function() { frameSheet.addEmptyFrame(); piskel.setActiveFrameAndRedraw(frameSheet.getFrameCount() - 1); @@ -218,7 +245,7 @@ initAnimationPreview : function() { - var previewAnimationContainer = $('preview-canvas-container'); + var previewAnimationContainer = $('#preview-canvas-container')[0]; previewCanvas = document.createElement('canvas'); previewCanvas.className = 'canvas'; previewAnimationContainer.setAttribute('style', @@ -230,7 +257,7 @@ startAnimation : function () { var scope = this; - var animFPSTuner = $("preview-fps"); + var animFPSTuner = $("#preview-fps")[0]; var animPreviewFPS = parseInt(animFPSTuner.value, 10); var startPreviewRefresh = function() { return setInterval(scope.refreshAnimatedPreview, 1000/animPreviewFPS); @@ -240,13 +267,13 @@ animFPSTuner.addEventListener('change', function(evt) { window.clearInterval(refreshUpdater); animPreviewFPS = parseInt(animFPSTuner.value, 10); - $("display-fps").innerHTML = animPreviewFPS + " fps"; + $("#display-fps").html(animPreviewFPS + " fps"); refreshUpdater = startPreviewRefresh(); }); }, createPreviews : function () { - var container = $('preview-list'), previewTile; + var container = $('#preview-list')[0], previewTile; container.innerHTML = ""; for (var i = 0, l = frameSheet.getFrameCount(); i < l ; i++) { previewTile = this.createPreviewTile(i); @@ -346,29 +373,35 @@ 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. + + if(event.button == 2) { // right click isRightClicked = true; - this.drawAt(coords.x, coords.y, TRANSPARENT_COLOR); + $.publish(Events.CANVAS_RIGHT_CLICKED); } + var spriteCoordinate = this.getSpriteCoordinate(event); + currentToolBehavior.applyToolOnFrameAt( + spriteCoordinate.col, spriteCoordinate.row, currentFrame, penColor); + currentToolBehavior.applyToolOnCanvasAt( + spriteCoordinate.col, spriteCoordinate.row, drawingAreaCanvas, currentFrame, penColor, drawingCanvasDpi); + + piskel.persistToLocalStorageRequest(); }, 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); - } + var spriteCoordinate = this.getSpriteCoordinate(event) + currentToolBehavior.applyToolOnFrameAt( + spriteCoordinate.col, spriteCoordinate.row, currentFrame, penColor); + currentToolBehavior.applyToolOnCanvasAt( + spriteCoordinate.col, spriteCoordinate.row, drawingAreaCanvas, currentFrame, penColor, drawingCanvasDpi); + + piskel.persistToLocalStorageRequest(); } }, - onCanvasMouseup : function (event) { + onDocumentBodyMouseup : 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. @@ -376,49 +409,16 @@ // of the drawing canvas. this.createPreviews(); } + if(isRightClicked) { + $.publish(Events.CANVAS_RIGHT_CLICK_RELEASED); + } isClicked = false; isRightClicked = false; + var spriteCoordinate = this.getSpriteCoordinate(event) + currentToolBehavior.releaseToolAt(spriteCoordinate.col, spriteCoordinate.row, penColor); }, - drawAt : function (x, y, color) { - var col = (x - x%drawingCanvasDpi) / drawingCanvasDpi; - var row = (y - y%drawingCanvasDpi) / drawingCanvasDpi; - - // Update model: - var currentFrame = frameSheet.getFrameByIndex(this.getActiveFrameIndex()); - - // TODO: make a better accessor for pixel state update: - // TODO: Make pen color dynamic: - var color = _normalizeColor(color); - if (color != currentFrame[col][row]) { - currentFrame[col][row] = color; - this.drawPixelInCanvas(row, col, color, drawingAreaCanvas, drawingCanvasDpi); - } - - // Persist to localStorage when drawing. We throttle localStorage accesses - // for high frequency drawing (eg mousemove). - if(localStorageThrottler == null) { - localStorageThrottler = window.setTimeout(function() { - piskel.persistToLocalStorage(); - localStorageThrottler = null; - }, 1000); - } - - - }, - - drawPixelInCanvas : function (row, col, color, canvas, dpi) { - var context = canvas.getContext('2d'); - if(color == undefined || color == TRANSPARENT_COLOR) { - context.clearRect(col * dpi, row * dpi, dpi, dpi); - } else { - this.addColorToPalette(color); - context.fillStyle = color; - context.fillRect(col * dpi, row * dpi, dpi, dpi); - } - }, - - // TODO: move that to a FrameRenderer (/w cache) ? + // TODO(vincz/julz): Refactor to make this disappear in a big event-driven redraw loop drawFrameToCanvas: function(frame, canvasElement, dpi) { var color; for(var col = 0, num_col = frame.length; col < num_col; col++) { @@ -429,6 +429,17 @@ } }, + // TODO(vincz/julz): Refactor to make this disappear in a big event-driven redraw loop + drawPixelInCanvas : function (row, col, color, canvas, dpi) { + var context = canvas.getContext('2d'); + if(color == undefined || color == Constants.TRANSPARENT_COLOR) { + context.clearRect(col * dpi, row * dpi, dpi, dpi); + } else { + context.fillStyle = color; + context.fillRect(col * dpi, row * dpi, dpi, dpi); + } + }, + onCanvasContextMenu : function (event) { event.preventDefault(); event.stopPropagation(); @@ -437,11 +448,12 @@ }, onPaletteClick : function (event) { - var color = event.target.getAttribute("data-color"); + var color = $(event.target).data("color"); if (null !== color) { - var colorPicker = $('color-picker'); - colorPicker.color.fromString(color); - this.onPickerChange(); + //debugger; + var colorPicker = $('#color-picker'); + colorPicker[0].color.fromString(color); + penColor = color; } }, @@ -453,11 +465,21 @@ } }, + getSpriteCoordinate : function(event) { + var coord = this.getRelativeCoordinates(event.x, event.y); + var coords = this.getRelativeCoordinates(event.clientX, event.clientY); + return { + "col" : (coords.x - coords.x%drawingCanvasDpi) / drawingCanvasDpi, + "row" : (coords.y - coords.y%drawingCanvasDpi) / drawingCanvasDpi + } + }, + + // TODO(julz): Create package ? storeSheet : function (event) { var xhr = new XMLHttpRequest(); var formData = new FormData(); formData.append('framesheet_content', frameSheet.serialize()); - formData.append('fps_speed', $('preview-fps').value); + formData.append('fps_speed', $('#preview-fps').val()); xhr.open('POST', PISKEL_SERVICE_URL + "/store", true); xhr.onload = function(e) { if (this.status == 200) { @@ -478,4 +500,3 @@ piskel.init(); })(function(id){return document.getElementById(id)}); -//small change for checking my git setup :( diff --git a/js/utils.js b/js/utils.js new file mode 100644 index 00000000..42cbbeb6 --- /dev/null +++ b/js/utils.js @@ -0,0 +1,45 @@ +jQuery.namespace = function() { + var a=arguments, o=null, i, j, d; + for (i=0; i 255 || g > 255 || b > 255) + throw "Invalid color component"; + return ((r << 16) | (g << 8) | b).toString(16); + }; + + ns.normalizeColor = function (color) { + if(color == undefined || color == Constants.TRANSPARENT_COLOR || color.indexOf("#") == 0) { + return color; + } else { + return "#" + color; + } + }; + + ns.inherit = function(extendedObject, inheritFrom) { + extendedObject.prototype = Object.create(inheritFrom.prototype); + extendedObject.prototype.constructor = extendedObject; + //pskl.ToolBehavior.Eraser.prototype = Object.create(pskl.ToolBehavior.BaseTool.prototype); + //prototypeskl.ToolBehavior.Eraser.prototype.constructor = pskl.ToolBehavior.Eraser; + }; + +})() + diff --git a/resources/cursors-resources.jpg b/resources/cursors-resources.jpg new file mode 100644 index 00000000..cb47c0fa Binary files /dev/null and b/resources/cursors-resources.jpg differ