diff --git a/README.md b/README.md index 9bd0347..2a488cb 100644 --- a/README.md +++ b/README.md @@ -16,32 +16,25 @@ Suggestions / Planned features: - custom code without dependencies - more features such as sliders / color modes -- Mobile - - Touch equivalent for mouse clicks - - Hide or scale ui - - Maybe rearrange UI on portrait - - Stack colors when too many - - Fix popups +- Mobile + - Touch equivalent for mouse clicks + - Hide or scale ui + - Maybe rearrange UI on portrait + - Stack colors when too many + - Fix popups -- Selections - - New selection tool - - New currentLayer.canvas layer above the drawing layer - - Move when click and drag - - Merge with currentLayer.canvas when click outside +- Copy/paste + - Add as selection + - Show colors which would need to be added to palette -- Copy/paste - - Add as selection - - Show colors which would need to be added to palette +- Palette option remove unused colors +- Pixel Grid + - Another currentLayer.canvas + - Must be rescaled each zoom -- Palette option remove unused colors -- Pixel Grid - - Another currentLayer.canvas - - Must be rescaled each zoom - -- Possibly add collaborate function using together.js -- Bug fix - - Alt + scroll broken - - Add edge support? +- Possibly add collaborate function using together.js +- Bug fix + - Alt + scroll broken ## How to Contribute diff --git a/changelog.json b/changelog.json new file mode 100644 index 0000000..34deaa5 --- /dev/null +++ b/changelog.json @@ -0,0 +1,13 @@ +{ + "1.2.0 - 4/14/20": [ + {"change": "Added rectangle / selection tools", "author": "Unsettled"} + ], + + "1.1.0 - 4/4/19": [ + {"change": "Added transparency / eraser tool", "author": "Unsettled"} + ], + + "1.0.0 - 11/17/17": [ + {"change": "Initial release", "author": "skeddles"} + ] +} \ No newline at end of file diff --git a/css/pixel-editor.scss b/css/pixel-editor.scss index d954313..48a386e 100644 --- a/css/pixel-editor.scss +++ b/css/pixel-editor.scss @@ -35,6 +35,11 @@ svg { outline: 0 !important; } +.weak { + font-size: 0.8em; + color: color(base,foreground,weak); +} + .drawingCanvas { cursor: url('/pixel-art-where-to-start/pencil-tool-cursor.png'); diff --git a/js/_changeTool.js b/js/_changeTool.js index 25ebcf3..d58a21a 100644 --- a/js/_changeTool.js +++ b/js/_changeTool.js @@ -1,21 +1,26 @@ -function changeTool (selectedTool) { +function changeTool (newToolName) { + + console.log('changing tool to',newToolName) + + var selectedTool = tool[newToolName]; + // Ending any selection in progress - if (currentTool.includes("select") && !selectedTool.includes("select") && !selectionCanceled) { + if (currentTool.name.includes("select") && !selectedTool.name.includes("select") && !selectionCanceled) { endSelection(); } //set tool and temp tje tje tpp; currentTool = selectedTool; currentToolTemp = selectedTool; - + var tools = document.getElementById("tools-menu").children; - + for (var i = 0; i < tools.length; i++) { tools[i].classList.remove("selected"); } - + //give the button of the selected tool the .selected class - document.getElementById(selectedTool+"-button").parentNode.classList.add("selected"); - + document.getElementById(selectedTool.name+"-button").parentNode.classList.add("selected"); + //change cursor - updateCursor(); + currentTool.updateCursor(); } \ No newline at end of file diff --git a/js/_changeZoom.js b/js/_changeZoom.js index 57eb42f..8619af3 100644 --- a/js/_changeZoom.js +++ b/js/_changeZoom.js @@ -3,14 +3,14 @@ function changeZoom (layer, direction, cursorLocation) { var oldWidth = canvasSize[0] * zoom; var oldHeight = canvasSize[1] * zoom; var newWidth, newHeight; - + //change zoom level //if you want to zoom out, and the zoom isnt already at the smallest level if (direction == 'out' && zoom > 1) { zoom -= Math.ceil(zoom / 10); newWidth = canvasSize[0] * zoom; newHeight = canvasSize[1] * zoom; - + //adjust canvas position setCanvasOffset(layer.canvas, layer.canvas.offsetLeft + (oldWidth - newWidth) *cursorLocation[0]/oldWidth, layer.canvas.offsetTop + (oldHeight - newHeight) *cursorLocation[1]/oldWidth) } @@ -19,14 +19,14 @@ function changeZoom (layer, direction, cursorLocation) { zoom += Math.ceil(zoom/10); newWidth = canvasSize[0] * zoom; newHeight = canvasSize[1] * zoom; - + //adjust canvas position setCanvasOffset(layer.canvas, layer.canvas.offsetLeft - Math.round((newWidth - oldWidth)*cursorLocation[0]/oldWidth), layer.canvas.offsetTop - Math.round((newHeight - oldHeight)*cursorLocation[1]/oldHeight)) } - + //resize canvas layer.resize(); // adjust brush size - updateCursor(); + currentTool.updateCursor(); } \ No newline at end of file diff --git a/js/_drawLine.js b/js/_drawLine.js index 455cffe..8e6f605 100644 --- a/js/_drawLine.js +++ b/js/_drawLine.js @@ -1,6 +1,6 @@ //draw a line between two points on canvas function line(x0,y0,x1,y1, brushSize) { - + var dx = Math.abs(x1-x0); var dy = Math.abs(y1-y0); var sx = (x0 < x1 ? 1 : -1); @@ -10,14 +10,14 @@ function line(x0,y0,x1,y1, brushSize) { while (true) { //set pixel // If the current tool is the brush - if (currentTool == 'pencil' || currentTool == 'rectangle') { + if (currentTool.name == 'pencil' || currentTool.name == 'rectangle') { // I fill the rect currentLayer.context.fillRect(x0-Math.floor(brushSize/2), y0-Math.floor(brushSize/2), brushSize, brushSize); - } else if (currentTool == 'eraser') { + } else if (currentTool.name == 'eraser') { // In case I'm using the eraser I must clear the rect - currentLayer.context.clearRect(x0-Math.floor(eraserSize/2), y0-Math.floor(eraserSize/2), eraserSize, eraserSize); + currentLayer.context.clearRect(x0-Math.floor(tool.eraser.brushSize/2), y0-Math.floor(tool.eraser.brushSize/2), tool.eraser.brushSize, tool.eraser.brushSize); } - + //if we've reached the end goal, exit the loop if ((x0==x1) && (y0==y1)) break; var e2 = 2*err; @@ -28,7 +28,7 @@ function line(x0,y0,x1,y1, brushSize) { //draw a line between two points on canvas function lineOnLayer(x0,y0,x1,y1, brushSize, context) { - + var dx = Math.abs(x1-x0); var dy = Math.abs(y1-y0); var sx = (x0 < x1 ? 1 : -1); @@ -38,14 +38,14 @@ function lineOnLayer(x0,y0,x1,y1, brushSize, context) { while (true) { //set pixel // If the current tool is the brush - if (currentTool == 'pencil' || currentTool == 'rectangle') { + if (currentTool.name == 'pencil' || currentTool.name == 'rectangle') { // I fill the rect context.fillRect(x0-Math.floor(brushSize/2), y0-Math.floor(brushSize/2), brushSize, brushSize); - } else if (currentTool == 'eraser') { + } else if (currentTool.name == 'eraser') { // In case I'm using the eraser I must clear the rect - context.clearRect(x0-Math.floor(eraserSize/2), y0-Math.floor(eraserSize/2), eraserSize, eraserSize); + context.clearRect(x0-Math.floor(tool.eraser.brushSize/2), y0-Math.floor(tool.eraser.brushSize/2), tool.eraser.brushSize, tool.eraser.brushSize); } - + //if we've reached the end goal, exit the loop if ((x0==x1) && (y0==y1)) break; var e2 = 2*err; diff --git a/js/_hotkeyListener.js b/js/_hotkeyListener.js index 13021d4..121de55 100644 --- a/js/_hotkeyListener.js +++ b/js/_hotkeyListener.js @@ -15,65 +15,65 @@ function KeyPress(e) { if (e.key === "Escape") { if (!selectionCanceled) { endSelection(); - changeTool('pencil'); + tool.pencil.switchTo(); } } else { switch (keyboardEvent.keyCode) { //pencil tool - 1, b - case 49: case 66: - changeTool('pencil'); + case 49: case 66: + tool.pencil.switchTo(); break; //fill tool - 2, f case 50: case 70: - changeTool('fill'); + tool.fill.switchTo(); break; //eyedropper - 3, e case 51: case 69: - changeTool('eyedropper'); + tool.eyedropper.switchTo(); break; //pan - 4, p, case 52: case 80: - changeTool('pan'); + tool.pan.switchTo(); break; //zoom - 5 case 53: - changeTool('zoom'); + tool.zoom.switchTo(); break; // eraser -6, r case 54: case 82: console.log("Pressed r"); - changeTool('eraser'); + tool.eraser.switchTo() break; - // Rectangular selection + // Rectangular selection case 77: case 109: - changeTool('rectselect'); + tool.rectselect.switchTo() break; //Z case 90: console.log('PRESSED Z ', keyboardEvent.ctrlKey) //CTRL+ALT+Z redo - if (keyboardEvent.altKey && keyboardEvent.ctrlKey) + if (keyboardEvent.altKey && keyboardEvent.ctrlKey) redo(); if (!selectionCanceled) { endSelection(); - changeTool('pencil'); + tool.pencil.switchTo() } //CTRL+Z undo else if (keyboardEvent.ctrlKey) { undo(); if (!selectionCanceled) { endSelection(); - changeTool('pencil'); + tool.pencil.switchTo() } } //Z switch to zoom tool - else - changeTool('zoom'); + else + tool.zoom.switchTo() break; //redo - ctrl y case 89: - if (keyboardEvent.ctrlKey) + if (keyboardEvent.ctrlKey) redo(); break; case 32: @@ -86,7 +86,7 @@ function KeyPress(e) { document.onkeydown = KeyPress; window.addEventListener("keyup", function (e) { - + if (e.keyCode == 32) spacePressed = false; }); diff --git a/js/_mouseEvents.js b/js/_mouseEvents.js index a590d36..23ff627 100644 --- a/js/_mouseEvents.js +++ b/js/_mouseEvents.js @@ -7,54 +7,55 @@ window.addEventListener("mousedown", function (mouseEvent) { // Saving the event in case something else needs it currentMouseEvent = mouseEvent; canDraw = true; - + //if no document has been created yet, or this is a dialog open if (!documentCreated || dialogueOpen) return; //prevent right mouse clicks and such, which will open unwanted menus //mouseEvent.preventDefault(); - + lastPos = getCursorPosition(mouseEvent); - + dragging = true; //left or right click ? if (mouseEvent.which == 1) { - if (spacePressed) - currentTool = 'pan'; - else if (mouseEvent.altKey) - currentTool = 'eyedropper'; - else if (mouseEvent.target.className == 'drawingCanvas' && - (currentTool == 'pencil' || currentTool == 'eraser' || currentTool == 'rectangle')) + if (spacePressed) + currentTool = tool.pan; + else if (mouseEvent.altKey) + currentTool = tool.eyedropper; + else if (mouseEvent.target.className == 'drawingCanvas' && + (currentTool.name == 'pencil' || currentTool.name == 'eraser' || currentTool.name == 'rectangle')) new HistoryStateEditCanvas(); - else if (currentTool == 'moveselection') { + else if (currentTool.name == 'moveselection') { if (!cursorInSelectedArea()) { - changeTool('pencil'); + tool.pencil.switchTo(); canDraw = false; } } //saveHistoryState({type: 'canvas', canvas: context.getImageData(0, 0, canvasSize[0], canvasSize[1])}); - - updateCursor(); - + + currentTool.updateCursor(); + if (canDraw) { draw(mouseEvent); } } - else if (currentTool == 'pencil' && mouseEvent.which == 3) { - currentTool = 'resize-brush'; - prevBrushSize = pencilSize; + else if (currentTool.name == 'pencil' && mouseEvent.which == 3) { + currentTool = tool.resizebrush; + tool.pencil.previousBrushSize = tool.pencil.brushSize; } - else if (currentTool == 'eraser' && mouseEvent.which == 3) { - currentTool = 'resize-eraser'; - prevEraserSize = eraserSize; + else if (currentTool.name == 'eraser' && mouseEvent.which == 3) { + console.log('resize eraser') + currentTool = tool.resizeeraser; + tool.eraser.previousBrushSize = tool.eraser.brushSize; } - else if (currentTool == 'rectangle' && mouseEvent.which == 3) { - currentTool = 'resize-rectangle'; - prevRectangleSize = rectangleSize; + else if (currentTool.name == 'rectangle' && mouseEvent.which == 3) { + currentTool = tool.resizerectangle; + tool.rectangle.previousBrushSize = tool.rectangle.brushSize; } - if (currentTool == 'eyedropper' && mouseEvent.target.className == 'drawingCanvas') + if (currentTool.name == 'eyedropper' && mouseEvent.target.className == 'drawingCanvas') eyedropperPreview.style.display = 'block'; - + return false; }, false); @@ -66,53 +67,53 @@ window.addEventListener("mouseup", function (mouseEvent) { currentMouseEvent = mouseEvent; closeMenu(); - + if (!documentCreated || dialogueOpen) return; - - if (currentTool == 'eyedropper' && mouseEvent.target.className == 'drawingCanvas') { + + if (currentTool.name == 'eyedropper' && mouseEvent.target.className == 'drawingCanvas') { var cursorLocation = getCursorPosition(mouseEvent); var selectedColor = context.getImageData(Math.floor(cursorLocation[0]/zoom),Math.floor(cursorLocation[1]/zoom),1,1); - var newColor = rgbToHex(selectedColor.data[0],selectedColor.data[1],selectedColor.data[2]); + var newColor = rgbToHex(selectedColor.data[0],selectedColor.data[1],selectedColor.data[2]); currentGlobalColor = "#" + newColor; - + var colors = document.getElementsByClassName('color-button'); for (var i = 0; i < colors.length; i++) { console.log(colors[i].jscolor.toString()); - + //if picked color matches this color if (newColor == colors[i].jscolor.toString()) { console.log('color found'); - + //remove current color selection var selectedColor = document.querySelector("#colors-menu li.selected") if (selectedColor) selectedColor.classList.remove("selected"); - + //set current color context.fillStyle = '#'+newColor; - + //make color selected colors[i].parentElement.classList.add('selected'); - + //hide eyedropper eyedropperPreview.style.display = 'none'; } } } - else if (currentTool == 'fill' && mouseEvent.target.className == 'drawingCanvas') { + else if (currentTool.name == 'fill' && mouseEvent.target.className == 'drawingCanvas') { console.log('filling') - + //get cursor postion var cursorLocation = getCursorPosition(mouseEvent); - + //offset to match cursor point cursorLocation[0] += 2; cursorLocation[1] += 12; - + //fill starting at the location fill(cursorLocation); } - else if (currentTool == 'zoom' && mouseEvent.target.className == 'drawingCanvas') { + else if (currentTool.name == 'zoom' && mouseEvent.target.className == 'drawingCanvas') { let mode; if (mouseEvent.which == 1){ mode = "in"; @@ -127,25 +128,25 @@ window.addEventListener("mouseup", function (mouseEvent) { layers[i].copyData(layers[0]); } } - else if (currentTool == 'rectselect' && isRectSelecting) { + else if (currentTool.name == 'rectselect' && isRectSelecting) { endRectSelection(mouseEvent); } - else if (currentTool == 'rectangle') { + else if (currentTool.name == 'rectangle') { endRectDrawing(mouseEvent); } dragging = false; currentTool = currentToolTemp; - - updateCursor(); - + currentTool.updateCursor(); + + }, false); // OPTIMIZABLE: redundant || mouseEvent.target.className in currentTool ifs -//mouse is moving on canvas +//mouse is moving on canvas window.addEventListener("mousemove", draw, false); function draw (mouseEvent) { lastMousePos = getCursorPosition(mouseEvent); @@ -153,18 +154,18 @@ function draw (mouseEvent) { currentMouseEvent = mouseEvent; var cursorLocation = lastMousePos; - + //if a document hasnt yet been created, exit this function if (!documentCreated || dialogueOpen) return; - - + + eyedropperPreview.style.display = 'none'; - - if (currentTool == 'pencil') { + + if (currentTool.name == 'pencil') { //move the brush preview - brushPreview.style.left = cursorLocation[0] + currentLayer.canvas.offsetLeft - pencilSize * zoom / 2 + 'px'; - brushPreview.style.top = cursorLocation[1] + currentLayer.canvas.offsetTop - pencilSize * zoom / 2 + 'px'; - + brushPreview.style.left = cursorLocation[0] + currentLayer.canvas.offsetLeft - tool.pencil.brushSize * zoom / 2 + 'px'; + brushPreview.style.top = cursorLocation[1] + currentLayer.canvas.offsetTop - tool.pencil.brushSize * zoom / 2 + 'px'; + //hide brush preview outside of canvas / canvas view if (mouseEvent.target.className == 'drawingCanvas'|| mouseEvent.target.className == 'drawingCanvas') brushPreview.style.visibility = 'visible'; @@ -174,25 +175,25 @@ function draw (mouseEvent) { //draw line to current pixel if (dragging) { if (mouseEvent.target.className == 'drawingCanvas' || mouseEvent.target.className == 'drawingCanvas') { - line(Math.floor(lastPos[0]/zoom),Math.floor(lastPos[1]/zoom),Math.floor(cursorLocation[0]/zoom),Math.floor(cursorLocation[1]/zoom), pencilSize); + line(Math.floor(lastPos[0]/zoom),Math.floor(lastPos[1]/zoom),Math.floor(cursorLocation[0]/zoom),Math.floor(cursorLocation[1]/zoom), tool.pencil.brushSize); lastPos = cursorLocation; } } - + //get lightness value of color var selectedColor = context.getImageData(Math.floor(cursorLocation[0]/zoom),Math.floor(cursorLocation[1]/zoom),1,1).data; var colorLightness = Math.max(selectedColor[0],selectedColor[1],selectedColor[2]) - + //for the darkest 50% of colors, change the brush preview to dark mode if (colorLightness>127) brushPreview.classList.remove('dark'); else brushPreview.classList.add('dark'); } // Decided to write a different implementation in case of differences between the brush and the eraser tool - else if (currentTool == 'eraser') { + else if (currentTool.name == 'eraser') { // Uses the same preview as the brush //move the brush preview - brushPreview.style.left = cursorLocation[0] + canvas.offsetLeft - eraserSize * zoom / 2 + 'px'; - brushPreview.style.top = cursorLocation[1] + canvas.offsetTop - eraserSize * zoom / 2 + 'px'; + brushPreview.style.left = cursorLocation[0] + canvas.offsetLeft - currentTool.brushSize * zoom / 2 + 'px'; + brushPreview.style.top = cursorLocation[1] + canvas.offsetTop - currentTool.brushSize * zoom / 2 + 'px'; //hide brush preview outside of canvas / canvas view if (mouseEvent.target.className == 'drawingCanvas' || mouseEvent.target.className == 'drawingCanvas') @@ -203,16 +204,16 @@ function draw (mouseEvent) { //draw line to current pixel if (dragging) { if (mouseEvent.target.className == 'drawingCanvas' || mouseEvent.target.className == 'drawingCanvas') { - line(Math.floor(lastPos[0]/zoom),Math.floor(lastPos[1]/zoom),Math.floor(cursorLocation[0]/zoom),Math.floor(cursorLocation[1]/zoom), eraserSize); + line(Math.floor(lastPos[0]/zoom),Math.floor(lastPos[1]/zoom),Math.floor(cursorLocation[0]/zoom),Math.floor(cursorLocation[1]/zoom), currentTool.brushSize); lastPos = cursorLocation; } } } - else if (currentTool == 'rectangle') + else if (currentTool.name == 'rectangle') { //move the brush preview - brushPreview.style.left = cursorLocation[0] + currentLayer.canvas.offsetLeft - rectangleSize * zoom / 2 + 'px'; - brushPreview.style.top = cursorLocation[1] + currentLayer.canvas.offsetTop - rectangleSize * zoom / 2 + 'px'; + brushPreview.style.left = cursorLocation[0] + currentLayer.canvas.offsetLeft - currentTool.brushSize * zoom / 2 + 'px'; + brushPreview.style.top = cursorLocation[1] + currentLayer.canvas.offsetTop - currentTool.brushSize * zoom / 2 + 'px'; //hide brush preview outside of canvas / canvas view if (mouseEvent.target.className == 'drawingCanvas'|| mouseEvent.target.className == 'drawingCanvas') @@ -227,7 +228,7 @@ function draw (mouseEvent) { updateRectDrawing(mouseEvent); } } - else if (currentTool == 'pan' && dragging) { + else if (currentTool.name == 'pan' && dragging) { // Setting first layer position setCanvasOffset(layers[0].canvas, layers[0].canvas.offsetLeft + (cursorLocation[0] - lastPos[0]), layers[0].canvas.offsetTop + (cursorLocation[1] - lastPos[1])); // Copying that position to the other layers @@ -235,73 +236,73 @@ function draw (mouseEvent) { layers[i].copyData(layers[0]); } } - else if (currentTool == 'eyedropper' && dragging && mouseEvent.target.className == 'drawingCanvas') { + else if (currentTool.name == 'eyedropper' && dragging && mouseEvent.target.className == 'drawingCanvas') { var selectedColor = context.getImageData(Math.floor(cursorLocation[0]/zoom),Math.floor(cursorLocation[1]/zoom),1,1).data; eyedropperPreview.style.borderColor = '#'+rgbToHex(selectedColor[0],selectedColor[1],selectedColor[2]); eyedropperPreview.style.display = 'block'; - + eyedropperPreview.style.left = cursorLocation[0] + currentLayer.canvas.offsetLeft - 30 + 'px'; eyedropperPreview.style.top = cursorLocation[1] + currentLayer.canvas.offsetTop - 30 + 'px'; - + var colorLightness = Math.max(selectedColor[0],selectedColor[1],selectedColor[2]); - + //for the darkest 50% of colors, change the eyedropper preview to dark mode if (colorLightness>127) eyedropperPreview.classList.remove('dark'); else eyedropperPreview.classList.add('dark'); } - else if (currentTool == 'resize-brush' && dragging) { + else if (currentTool.name == 'resizebrush' && dragging) { //get new brush size based on x distance from original clicking location var distanceFromClick = cursorLocation[0] - lastPos[0]; //var roundingAmount = 20 - Math.round(distanceFromClick/10); //this doesnt work in reverse... because... it's not basing it off of the brush size which it should be var brushSizeChange = Math.round(distanceFromClick/10); - var newBrushSize = prevBrushSize + brushSizeChange; + var newBrushSize = tool.pencil.previousBrushSize + brushSizeChange; //set the brush to the new size as long as its bigger than 1 - pencilSize = Math.max(1,newBrushSize); + tool.pencil.brushSize = Math.max(1,newBrushSize); //fix offset so the cursor stays centered - brushPreview.style.left = lastPos[0] + currentLayer.canvas.offsetLeft - pencilSize * zoom / 2 + 'px'; - brushPreview.style.top = lastPos[1] + currentLayer.canvas.offsetTop - pencilSize * zoom / 2 + 'px'; + brushPreview.style.left = lastPos[0] + currentLayer.canvas.offsetLeft - tool.pencil.brushSize * zoom / 2 + 'px'; + brushPreview.style.top = lastPos[1] + currentLayer.canvas.offsetTop - tool.pencil.brushSize * zoom / 2 + 'px'; - updateCursor(); + currentTool.updateCursor(); } - else if (currentTool == 'resize-eraser' && dragging) { + else if (currentTool.name == 'resizeeraser' && dragging) { //get new brush size based on x distance from original clicking location var distanceFromClick = cursorLocation[0] - lastPos[0]; //var roundingAmount = 20 - Math.round(distanceFromClick/10); //this doesnt work in reverse... because... it's not basing it off of the brush size which it should be var eraserSizeChange = Math.round(distanceFromClick/10); - var newEraserSizeChange = prevEraserSize + eraserSizeChange; + var newEraserSizeChange = tool.eraser.previousBrushSize + eraserSizeChange; //set the brush to the new size as long as its bigger than 1 - eraserSize = Math.max(1,newEraserSizeChange); + tool.eraser.brushSize = Math.max(1,newEraserSizeChange); //fix offset so the cursor stays centered - brushPreview.style.left = lastPos[0] + currentLayer.canvas.offsetLeft - eraserSize * zoom / 2 + 'px'; - brushPreview.style.top = lastPos[1] + currentLayer.canvas.offsetTop - eraserSize * zoom / 2 + 'px'; + brushPreview.style.left = lastPos[0] + currentLayer.canvas.offsetLeft - tool.eraser.brushSize * zoom / 2 + 'px'; + brushPreview.style.top = lastPos[1] + currentLayer.canvas.offsetTop - tool.eraser.brushSize * zoom / 2 + 'px'; - updateCursor(); + currentTool.updateCursor(); } - else if (currentTool == 'resize-rectangle' && dragging) { + else if (currentTool.name == 'resizerectangle' && dragging) { //get new brush size based on x distance from original clicking location var distanceFromClick = cursorLocation[0] - lastPos[0]; //var roundingAmount = 20 - Math.round(distanceFromClick/10); //this doesnt work in reverse... because... it's not basing it off of the brush size which it should be var rectangleSizeChange = Math.round(distanceFromClick/10); - var newRectangleSize = prevRectangleSize + rectangleSizeChange; + var newRectangleSize = tool.rectangle.previousBrushSize + rectangleSizeChange; //set the brush to the new size as long as its bigger than 1 - rectangleSize = Math.max(1,newRectangleSize); + tool.rectangle.brushSize = Math.max(1,newRectangleSize); //fix offset so the cursor stays centered - brushPreview.style.left = lastPos[0] + currentLayer.canvas.offsetLeft - rectangleSize * zoom / 2 + 'px'; - brushPreview.style.top = lastPos[1] + currentLayer.canvas.offsetTop - rectangleSize * zoom / 2 + 'px'; + brushPreview.style.left = lastPos[0] + currentLayer.canvas.offsetLeft - tool.rectangle.brushSize * zoom / 2 + 'px'; + brushPreview.style.top = lastPos[1] + currentLayer.canvas.offsetTop - tool.rectangle.brushSize * zoom / 2 + 'px'; - updateCursor(); + currentTool.updateCursor(); } - else if (currentTool == 'rectselect') { + else if (currentTool.name == 'rectselect') { if (dragging && !isRectSelecting && mouseEvent.target.className == 'drawingCanvas') { isRectSelecting = true; console.log("cominciata selezione su " + mouseEvent.target.className); @@ -314,9 +315,9 @@ function draw (mouseEvent) { endRectSelection(); } } - else if (currentTool == 'moveselection') { + else if (currentTool.name == 'moveselection') { // Updating the cursor (move if inside rect, cross if not) - updateCursor(); + currentTool.updateCursor(); // If I'm dragging, I move the preview if (dragging && cursorInSelectedArea()) { @@ -327,8 +328,8 @@ function draw (mouseEvent) { //mousewheel scrroll canvasView.addEventListener("wheel", function(mouseEvent){ - - if (currentTool == 'zoom' || mouseEvent.altKey) { + + if (currentTool.name == 'zoom' || mouseEvent.altKey) { let mode; if (mouseEvent.deltaY < 0){ mode = 'in'; @@ -345,5 +346,5 @@ canvasView.addEventListener("wheel", function(mouseEvent){ layers[i].copyData(layers[0]); } } - + }); \ No newline at end of file diff --git a/js/_newPixel.js b/js/_newPixel.js index 9f38ec7..59235c8 100644 --- a/js/_newPixel.js +++ b/js/_newPixel.js @@ -21,63 +21,63 @@ function newPixel (width, height, palette) { layers.push(TMPLayer); layers.push(currentLayer); layers.push(checkerBoard); - + //remove current palette colors = document.getElementsByClassName('color-button'); while (colors.length > 0) { colors[0].parentElement.remove(); } - + //add colors from selected palette var selectedPalette = getText('palette-button'); if (selectedPalette != 'Choose a palette...') { - + //if this palette isnt the one specified in the url, then reset the url if (!palettes[selectedPalette].specified) history.pushState(null, null, '/pixel-editor/app'); - + //fill the palette with specified palette createColorPalette(palettes[selectedPalette].colors,true); } else { //this wasn't a specified palette, so reset the url history.pushState(null, null, '/pixel-editor/app'); - + //generate default colors var fg = hslToRgb(Math.floor(Math.random()*255), 230,70); var bg = hslToRgb(Math.floor(Math.random()*255), 230,170); - + //convert colors to hex var defaultForegroundColor = rgbToHex(fg.r,fg.g,fg.b); var defaultBackgroundColor = rgbToHex(bg.r,bg.g,bg.b); - + //add colors to paletee addColor(defaultForegroundColor).classList.add('selected'); addColor(defaultBackgroundColor); - + //fill background of canvas with bg color fillCheckerboard(); /* currentLayer.context.fillStyle = '#'+defaultBackgroundColor; currentLayer.context.fillRect(0, 0, canvasSize[0], canvasSize[1]); - + console.log('#'+defaultBackgroundColor) */ - + //set current drawing color as foreground color currentLayer.context.fillStyle = '#'+defaultForegroundColor; currentGlobalColor = '#' + defaultForegroundColor; selectedPalette = 'none'; } - + //reset undo and redo states undoStates = []; redoStates = []; - + closeDialogue(); - updateCursor(); - + currentTool.updateCursor(); + document.getElementById('save-as-button').classList.remove('disabled'); documentCreated = true; - + } \ No newline at end of file diff --git a/js/_onLoad.js b/js/_onLoad.js index e4c9e18..50cc704 100644 --- a/js/_onLoad.js +++ b/js/_onLoad.js @@ -1,7 +1,7 @@ //when the page is donw loading, you can get ready to start window.onload = function(){ - updateCursor(); - + currentTool.updateCursor(); + //if the user specified dimentions if (specifiedDimentions) //create a new pixel diff --git a/js/_rectSelect.js b/js/_rectSelect.js index 74c02f0..93d5089 100644 --- a/js/_rectSelect.js +++ b/js/_rectSelect.js @@ -37,7 +37,7 @@ function startRectSelection(mouseEvent) { function updateRectSelection(mouseEvent) { let pos = getCursorPosition(mouseEvent); - + // Drawing the rect drawRect(Math.round(pos[0] / zoom) + 0.5, Math.round(pos[1] / zoom) + 0.5); } @@ -62,21 +62,21 @@ function endRectSelection(mouseEvent) { } // Selecting the move tool - currentTool = 'moveselection'; + currentTool = tool.moveselection; currentToolTemp = currentTool; // Resetting this isRectSelecting = false; - // Updating the cursor - updateCursor(); + // Updating the cursor + currentTool.updateCursor(); } function cutSelection(mouseEvent) { console.log("Coordinate: start x, y: " + startX + ", " + startY + " end x, y: " + endX + ", " + endY); // Getting the selected pixels imageDataToMove = currentLayer.context.getImageData(startX, startY, endX - startX + 1, endY - startY + 1); - + currentLayer.context.clearRect(startX - 0.5, startY - 0.5, endX - startX + 1, endY - startY + 1); // Moving those pixels from the current layer to the tmp layer TMPLayer.context.putImageData(imageDataToMove, startX + 1, startY); diff --git a/js/_rectangle.js b/js/_rectangle.js index 3249ce4..8e576ab 100644 --- a/js/_rectangle.js +++ b/js/_rectangle.js @@ -1,5 +1,4 @@ -var rectangleSize = 1; -var prevRectangleSie = rectangleSize; + var emptySVG = document.getElementById("empty-button-svg"); var fullSVG = document.getElementById("full-button-svg"); @@ -28,7 +27,7 @@ function startRectDrawing(mouseEvent) { function updateRectDrawing(mouseEvent) { let pos = getCursorPosition(mouseEvent); - + // Drawing the rect drawRectangle(Math.round(pos[0] / zoom) + 0.5, Math.round(pos[1] / zoom) + 0.5); } @@ -64,13 +63,13 @@ function endRectDrawing(mouseEvent) { endRectX -= 0.5; startRectX -= 0.5; - currentLayer.context.lineWidth = rectangleSize; + currentLayer.context.lineWidth = tool.rectangle.brushSize; currentLayer.context.fillStyle = currentGlobalColor; - line(startRectX, startRectY, endRectX, startRectY, rectangleSize); - line(endRectX, startRectY, endRectX, endRectY, rectangleSize); - line(endRectX, endRectY, startRectX, endRectY, rectangleSize); - line(startRectX, endRectY, startRectX, startRectY, rectangleSize); + line(startRectX, startRectY, endRectX, startRectY, tool.rectangle.brushSize); + line(endRectX, startRectY, endRectX, endRectY, tool.rectangle.brushSize); + line(endRectX, endRectY, startRectX, endRectY, tool.rectangle.brushSize); + line(startRectX, endRectY, startRectX, startRectY, tool.rectangle.brushSize); if (drawMode == 'fill') { currentLayer.context.fillRect(startRectX, startRectY, endRectX - startRectX, endRectY - startRectY); @@ -79,7 +78,7 @@ function endRectDrawing(mouseEvent) { // Clearing the vfx canvas vfxContext.clearRect(0, 0, VFXCanvas.width, VFXCanvas.height); } - + function drawRectangle(x, y) { // Getting the vfx context let vfxContext = VFXCanvas.getContext("2d"); @@ -88,18 +87,18 @@ function drawRectangle(x, y) { vfxContext.clearRect(0, 0, VFXCanvas.width, VFXCanvas.height); // Drawing the rect - vfxContext.lineWidth = rectangleSize; + vfxContext.lineWidth = tool.rectangle.brushSize; vfxContext.strokeStyle = currentGlobalColor; // Drawing the rect vfxContext.beginPath(); - if ((rectangleSize % 2 ) == 0) { + if ((tool.rectangle.brushSize % 2 ) == 0) { vfxContext.rect(startRectX - 0.5, startRectY - 0.5, x - startRectX, y - startRectY); } else { vfxContext.rect(startRectX, startRectY, x - startRectX, y - startRectY); } - + vfxContext.setLineDash([]); vfxContext.stroke(); diff --git a/js/_toolButtons.js b/js/_toolButtons.js index 6c846d1..521ca8f 100644 --- a/js/_toolButtons.js +++ b/js/_toolButtons.js @@ -1,41 +1,39 @@ //pencil on('click',"pencil-button", function(){ - changeTool('pencil'); + tool.pencil.switchTo(); }, false); //pencil bigger on('click',"pencil-bigger-button", function(){ - brushSize++; - updateCursor(); + tool.pencil.brushSize++; }, false); //pencil smaller on('click',"pencil-smaller-button", function(){ - if(brushSize > 1) brushSize--; - updateCursor(); + if(tool.pencil.brushSize > 1) + tool.pencil.brushSize--; }, false); //eraser on('click',"eraser-button", function(){ - changeTool('eraser'); + tool.eraser.switchTo(); }, false); //eraser bigger on('click',"eraser-bigger-button", function(){ - eraserSize++; - updateCursor(); + tool.eraser.brushSize++; }, false); //eraser smaller on('click',"eraser-smaller-button", function(e){ - if(eraserSize > 1) eraserSize--; - updateCursor(); + if(tool.eraser.brushSize > 1) + tool.eraser.brushSize--; }, false); // rectangle on('click',"rectangle-button", function(){ // If the user clicks twice on the button, they change the draw mode - if (currentTool == 'rectangle') { + if (currentTool.name == 'rectangle') { if (drawMode == 'empty') { drawMode = 'fill'; setRectToolSvg(); @@ -46,40 +44,39 @@ on('click',"rectangle-button", function(){ } } else { - changeTool('rectangle'); + tool.rectangle.switchTo(); } }, false); // rectangle bigger on('click',"rectangle-bigger-button", function(){ - rectangleSize++; - updateCursor(); + tool.rectangle.brushSize++; }, false); // rectangle smaller on('click',"rectangle-smaller-button", function(e){ - if(rectangleSize > 1) rectangleSize--; - updateCursor(); + if(tool.rectangle.brushSize > 1) + tool.rectangle.brushSize--; }, false); //fill on('click',"fill-button", function(){ - changeTool('fill'); -}, false); + tool.fill.switchTo(); +}, false); //pan on('click',"pan-button", function(){ - changeTool('pan'); -}, false); + tool.pan.switchTo(); +}, false); //eyedropper on('click',"eyedropper-button", function(){ - changeTool('eyedropper'); -}, false); + tool.eyedropper.switchTo(); +}, false); //zoom tool button on('click',"zoom-button", function(){ - changeTool('zoom'); + tool.zoom.switchTo(); }, false); //zoom in button @@ -103,5 +100,7 @@ on('click',"zoom-out-button", function(){ //rectangular selection button on('click', "rectselect-button", function(){ - changeTool('rectselect'); -}, false); \ No newline at end of file + tool.rectselect.switchTo(); +}, false); + +/*global on */ \ No newline at end of file diff --git a/js/_tools.js b/js/_tools.js new file mode 100644 index 0000000..f7cca56 --- /dev/null +++ b/js/_tools.js @@ -0,0 +1,70 @@ +//tools container / list, automatically managed when you create a new Tool(); +var tool = {}; + +//class for tools +class Tool { + constructor (name, options) { + + //stores the name in object, only needed for legacy functions from when currentTool was just a string + this.name = name; + + //copy options to this object + if (options.cursor) { + //passed statically as a string + if (typeof options.cursor == 'string') this.cursor = options.cursor; + //passed a function which should be used as a getter function + if (typeof options.cursor == 'function') Object.defineProperty(this, 'cursor', { get: options.cursor}); + } + + if (options.imageCursor) this.cursor = "url(\'/pixel-editor/"+options.imageCursor+".png\'), auto"; + + if (options.brushPreview) { + this.brushPreview = true; + this.currentBrushSize = 1; + this.previousBrushSize = 1; + } + + //add to tool object so it can be referenced + tool[name] = this; + } + + get brushSize () { + return this.currentBrushSize; + } + + set brushSize (value) { + this.currentBrushSize = value; + this.updateCursor(); + } + + + //switch to this tool (replaced global changeTool()) + switchTo () { + + console.log('changing tool to',this.name) + + // Ending any selection in progress + if (currentTool.name.includes("select") && !this.name.includes("select") && !selectionCanceled) { + endSelection(); + } + + //set tool and temp tje tje tpp; + currentTool = this; + currentToolTemp = this; + + var tools = document.getElementById("tools-menu").children; + + for (var i = 0; i < tools.length; i++) { + tools[i].classList.remove("selected"); + } + + //give the button of the selected tool the .selected class + document.getElementById(this.name+"-button").parentNode.classList.add("selected"); + + //change cursor + this.updateCursor(); + } +} + + +/*global dragging currentTool, currentToolTemp, selectionCanceled, endSelection*/ \ No newline at end of file diff --git a/js/_updateCursor.js b/js/_updateCursor.js index bd564b8..0ed59b6 100644 --- a/js/_updateCursor.js +++ b/js/_updateCursor.js @@ -1,23 +1,27 @@ + //set the correct cursor for the current tool -function updateCursor () { - if (currentTool == 'pencil' || currentTool == 'resize-brush') { - canvasView.style.cursor = 'crosshair'; +Tool.prototype.updateCursor = function () { + + console.log('updateCursor()', currentTool) + + //switch to that tools cursor + canvasView.style.cursor = this.cursor || 'default'; + + //if the tool uses a brush preview, make it visible and update the size + if (this.brushPreview) { + console.log('brush size',this.currentBrushSize) brushPreview.style.display = 'block'; - brushPreview.style.width = pencilSize * zoom + 'px'; - brushPreview.style.height = pencilSize * zoom + 'px'; - } else if (currentTool == 'eraser' || currentTool == 'resize-eraser') { - canvasView.style.cursor = 'crosshair'; - brushPreview.style.display = 'block'; - brushPreview.style.width = eraserSize * zoom + 'px'; - brushPreview.style.height = eraserSize * zoom + 'px'; - } else if (currentTool == 'rectangle' || currentTool == 'resize-rectangle') { - canvasView.style.cursor = 'crosshair'; - brushPreview.style.display = 'block'; - brushPreview.style.width = rectangleSize * zoom + 'px'; - brushPreview.style.height = rectangleSize * zoom + 'px'; + brushPreview.style.width = this.currentBrushSize * zoom + 'px'; + brushPreview.style.height = this.currentBrushSize * zoom + 'px'; } - else if (currentTool == 'moveselection') { + + //show / hide eyedropper color preview + if (this.eyedropperPreview) eyedropperPreview.style.display = 'block'; + else eyedropperPreview.style.display = 'none'; + + //moveSelection + if (currentTool.name == 'moveselection') { if (cursorInSelectedArea()) { canMoveSelection = true; canvasView.style.cursor = 'move'; @@ -27,28 +31,6 @@ function updateCursor () { canvasView.style.cursor = 'crosshair'; } } - else if (currentTool == 'rectselect') - canvasView.style.cursor = 'crosshair'; - else - brushPreview.style.display = 'none'; - - if (currentTool == 'eyedropper') { - canvasView.style.cursor = "url('/pixel-editor/eyedropper.png'), auto"; - } else - eyedropperPreview.style.display = 'none'; - - if (currentTool == 'pan') - if (dragging) - canvasView.style.cursor = "url('/pixel-editor/pan-held.png'), auto"; - else - canvasView.style.cursor = "url('/pixel-editor/pan.png'), auto"; - - if (currentTool == 'fill') - canvasView.style.cursor = "url('/pixel-editor/fill.png'), auto"; - - if (currentTool == 'zoom') - canvasView.style.cursor = "url('/pixel-editor/zoom-in.png'), auto"; - - if (currentTool == 'resize-brush' || currentTool == 'resize-eraser') - canvasView.style.cursor = 'default'; -} \ No newline at end of file +} + +/*global Tool, dragging, canvasView, brushPreview, canMoveSelection, cursorInSelectedArea, eyedropperPreview, zoom, currentTool */ \ No newline at end of file diff --git a/js/_variables.js b/js/_variables.js index 8008542..7823f55 100644 --- a/js/_variables.js +++ b/js/_variables.js @@ -2,12 +2,6 @@ var canvasSize,zoom; var dragging = false; var lastPos = [0,0]; -var currentTool = 'pencil'; -var currentToolTemp = 'pencil'; -var pencilSize = 1; -var eraserSize = 1; -var prevBrushSize = 1; -var prevEraserSize = 1; var dialogueOpen = false; var documentCreated = false; @@ -27,7 +21,7 @@ var eyedropperPreview = document.getElementById("eyedropper-preview"); var canvasView = document.getElementById("canvas-view"); var colors = document.getElementsByClassName("color-button"); var colorsMenu = document.getElementById("colors-menu"); -var popUpContainer = document.getElementById("pop-up-container"); +var popUpContainer = document.getElementById("pop-up-container"); // main canvas var canvas = document.getElementById("pixel-canvas"); diff --git a/js/pixel-editor.js b/js/pixel-editor.js index 13d99b4..e309406 100644 --- a/js/pixel-editor.js +++ b/js/pixel-editor.js @@ -1,74 +1,70 @@ - - /**utilities**/ -//=include utilities/on.js -//=include utilities/onChildren.js -//=include utilities/onClick.js -//=include utilities/onClickChildren.js -//=include utilities/select.js -//=include utilities/getSetText.js -//=include utilities/getSetValue.js -//=include utilities/hexToRgb.js +//=include utilities/on.js +//=include utilities/onChildren.js +//=include utilities/onClick.js +//=include utilities/onClickChildren.js +//=include utilities/select.js +//=include utilities/getSetText.js +//=include utilities/getSetValue.js +//=include utilities/hexToRgb.js //=include utilities/rgbToHex.js //=include utilities/rgbToHsl.js //=include utilities/hslToRgb.js //=include libraries/cookies.js //=include _pixelEditorUtility.js - - /**init**/ //=include _consts.js -//=include _variables.js -//=include _settings.js +//=include _variables.js +//=include _settings.js /**dropdown formatting**/ //=include _presets.js -//=include _palettes.js +//=include _palettes.js /**functions**/ -//=include _newPixel.js +//=include _tools.js +//=include tools/*.js +//=include _newPixel.js //=include _createColorPalette.js -//=include _setCanvasOffset.js -//=include _changeZoom.js -//=include _addColor.js -//=include _colorChanged.js -//=include _initColor.js -//=include _changeTool.js -//=include _dialogue.js -//=include _updateCursor.js -//=include _drawLine.js -//=include _getCursorPosition.js -//=include _fill.js -//=include _history.js -//=include _deleteColor.js +//=include _setCanvasOffset.js +//=include _changeZoom.js +//=include _addColor.js +//=include _colorChanged.js +//=include _initColor.js +//=include _changeTool.js +//=include _dialogue.js +//=include _updateCursor.js +//=include _drawLine.js +//=include _getCursorPosition.js +//=include _fill.js +//=include _history.js +//=include _deleteColor.js //=include _replaceAllOfColor.js //=include _checkerboard.js //=include _layer.js - /**load file**/ -//=include _loadImage.js -//=include _loadPalette.js +//=include _loadImage.js +//=include _loadPalette.js /**event listeners**/ -//=include _hotkeyListener.js -//=include _mouseEvents.js +//=include _hotkeyListener.js +//=include _mouseEvents.js /**buttons**/ -//=include _toolButtons.js -//=include _addColorButton.js -//=include _clickedColor.js -//=include _fileMenu.js -//=include _createButton.js +//=include _toolButtons.js +//=include _addColorButton.js +//=include _clickedColor.js +//=include _fileMenu.js +//=include _createButton.js //=include _rectSelect.js //=include _move.js //=include _rectangle.js - /**onload**/ -//=include _onLoad.js -//=include _onbeforeunload.js +//=include _onLoad.js +//=include _onbeforeunload.js /**libraries**/ -//=include _jscolor.js \ No newline at end of file +//=include _jscolor.js diff --git a/js/tools/_eraser.js b/js/tools/_eraser.js new file mode 100644 index 0000000..fd888fb --- /dev/null +++ b/js/tools/_eraser.js @@ -0,0 +1,13 @@ + +new Tool('eraser', { + cursor: 'crosshair', + brushPreview: true, +}); + + + +new Tool('resizeeraser', { + cursor: 'default', +}); + +/*global Tool, tool*/ \ No newline at end of file diff --git a/js/tools/_eyedropper.js b/js/tools/_eyedropper.js new file mode 100644 index 0000000..cf1b0b6 --- /dev/null +++ b/js/tools/_eyedropper.js @@ -0,0 +1,7 @@ + +new Tool('eyedropper', { + imageCursor: 'eyedropper', +}); + + +/*global Tool, tool*/ \ No newline at end of file diff --git a/js/tools/_fill.js b/js/tools/_fill.js new file mode 100644 index 0000000..090a38e --- /dev/null +++ b/js/tools/_fill.js @@ -0,0 +1,7 @@ + +new Tool('fill', { + imageCursor: 'fill', +}); + + +/*global Tool, tool*/ \ No newline at end of file diff --git a/js/tools/_pan.js b/js/tools/_pan.js new file mode 100644 index 0000000..ead4854 --- /dev/null +++ b/js/tools/_pan.js @@ -0,0 +1,10 @@ + +new Tool('pan', { + cursor: function () { + if (dragging) return 'url(\'/pixel-editor/pan-held.png\'), auto'; + else return 'url(\'/pixel-editor/pan.png\'), auto'; + }, +}); + + +/*global Tool, tool*/ \ No newline at end of file diff --git a/js/tools/_pencil.js b/js/tools/_pencil.js new file mode 100644 index 0000000..707c177 --- /dev/null +++ b/js/tools/_pencil.js @@ -0,0 +1,17 @@ + +new Tool('pencil', { + cursor: 'crosshair', + brushPreview: true, +}); + + +new Tool('resizebrush', { + cursor: 'default', +}); + + +//set as default tool +var currentTool = tool.pencil; +var currentToolTemp = tool.pencil; + +/*global Tool, tool*/ \ No newline at end of file diff --git a/js/tools/_rectangle.js b/js/tools/_rectangle.js new file mode 100644 index 0000000..677bccd --- /dev/null +++ b/js/tools/_rectangle.js @@ -0,0 +1,14 @@ + +new Tool('rectangle', { + cursor: 'crosshair', + brushPreview: true, +}); + + + +new Tool('resizerectangle', { + cursor: 'default', +}); + + +/*global Tool, tool*/ \ No newline at end of file diff --git a/js/tools/_select.js b/js/tools/_select.js new file mode 100644 index 0000000..984a015 --- /dev/null +++ b/js/tools/_select.js @@ -0,0 +1,13 @@ + + +new Tool('rectselect', { + cursor: 'crosshair', +}); + + +new Tool('moveselection', { + cursor: 'crosshair', +}); + + +/*global Tool, tool*/ \ No newline at end of file diff --git a/js/tools/_zoom.js b/js/tools/_zoom.js new file mode 100644 index 0000000..b3e5c72 --- /dev/null +++ b/js/tools/_zoom.js @@ -0,0 +1,6 @@ + +new Tool('zoom', { + imageCursor: 'zoom-in', +}); + +/*global Tool, tool*/ \ No newline at end of file diff --git a/views/pixel-editor.hbs b/views/pixel-editor.hbs index 82179bf..2624833 100644 --- a/views/pixel-editor.hbs +++ b/views/pixel-editor.hbs @@ -1,5 +1,5 @@ -