Compare commits

..

14 Commits

Author SHA1 Message Date
Ryan Sobol
d703db5bac Improve the solution 2018-06-12 08:15:44 +05:30
Ryan Sobol
e7deb660cb Improve the solution 2018-06-12 08:15:44 +05:30
Ryan Sobol
f298427ab8 Improve the solution 2018-06-12 08:15:44 +05:30
Ryan Sobol
cecb8d1f40 Improve the solution 2018-06-12 08:15:44 +05:30
Ryan Sobol
6c1a57711b Improve the solution 2018-06-12 08:15:44 +05:30
Ryan Sobol
b0da25cd29 Improve the solution 2018-06-12 08:15:44 +05:30
Ryan Sobol
bd71278bba Add CNAME to solution 2018-06-12 08:15:44 +05:30
Ryan Sobol
f4152534a0 Tweak CSS 2018-06-12 08:15:44 +05:30
Ryan Sobol
ff1a006ded Complete the painting analogy 2018-06-12 08:15:44 +05:30
Ryan Sobol
08b5fe7384 Refactor to mostly pixels 2018-06-12 08:15:33 +05:30
Ryan Sobol
0dbbca96ed Refactor to flexbox 2018-06-12 08:15:33 +05:30
Ryan Sobol
976cd319cb Add semantic markupto solution :P 2018-06-12 08:15:33 +05:30
Ryan Sobol
98678f2d45 Make the solution more dynamic 2018-06-12 08:15:13 +05:30
Ian Smith
4534b665d6 added solution 2018-06-12 08:15:13 +05:30
11 changed files with 357 additions and 957 deletions

1
CNAME Normal file
View File

@ -0,0 +1 @@
ryansobol-pixel-art-maker.surge.sh

844
canvas.js
View File

@ -1,844 +0,0 @@
var mouseState = -1;
var selectedColor = "black";
var selectedColorBorder = "2px red solid";
var unselectedColorBorder = "2px black solid";
var ROWS = 30;
var COLS = 30;
var customColorToolbar=false;
var fillTool = false;
var changedPixels = {}; //pixel data for all changed pixels in this step.
/*
STEPTYPE:
ADD //Pixel was added. Remove all pixels in this object.
//Each pixel will have an old_pos_X_Y indicating their old color. Do the reverse to undo. The pixel list itself will be in "PIXELS": X,Y,X2,Y2,X3,Y3, etc.
FILL //Fill was done. A color is provided by "backcolor" and a pixel containing the click and source color. To undo it, fill with the backcolor instead of the source color.
RESIZE //A resize operation was done. All old removed pixels are stored for shrinks. No pixels are stored for expansions.
*/
var pixelStates = []; //Last 20 pixel states stored here.
var currentPixelState = -1; //Which pixel state we're on. Max out at 20.
var lastDrawPoint = null;
document.addEventListener("DOMContentLoaded",()=>{
var undoButton = document.createElement("button")
var redoButton = document.createElement("button")
var rowsControl = document.createElement("input")
var colsControl = document.createElement("input")
class Coordinate{
constructor(box) {
var idParse=box.id.split("_");
this.x = idParse[1];
this.y = idParse[2];
}
}
class PixelCoordinate extends Coordinate{
constructor(x,y) {
this.x = x;
this.y = y;
}
}
class Point{
constructor(x,y) {
this.x = x;
this.y = y;
}
}
//Returns a coordinate class.
var getCoordinates = (box)=>{
return new Coordinate(box);
}
function insertAfter(newNode, existingNode) {
existingNode.parentNode.insertBefore(newNode, existingNode.nextSibling);
}
function componentToHex(c) {
var hex = c.toString(16);
return hex.length == 1 ? "0" + hex : hex;
}
function rgbToHex(rgbvalue) {
var rawColors = rgbvalue.replace("rgb(","").replace(")","").split(",");
return "#" + componentToHex(Number(rawColors[0])) + componentToHex(Number(rawColors[1])) + componentToHex(Number(rawColors[2]));
}
function Undo() {
//Execute the reverse.
var state = pixelStates[--currentPixelState];
//console.log("Undo "+currentPixelState)
//console.log("Undo "+JSON.stringify(state))
switch (state["STEPTYPE"]) {
case "ADD":{
//Take each pixel and color it its previous color.
var pixels = state["PIXELS"].split(",")
for (var i=0;i<pixels.length;i+=2) {
var pixelX = pixels[i];
var pixelY = pixels[i+1];
var oldColor = state["old_pos_"+pixelX+"_"+pixelY]
var cell = document.getElementById("pos_"+pixelX+"_"+pixelY)
cell.style.background = oldColor;
}
}break;
case "FILL":{
floodFill(state["PIXELX"],state["PIXELY"],document.getElementById("pos_"+state["PIXELX"]+"_"+state["PIXELY"]).style.background,state["backcolor"])
}break;
case "RESIZEROWS":{
//If the old size was smaller, restore the data in PIXELS.
//If the old size was larger, just remove rows.
//Make sure to update the control.
var table = document.getElementsByTagName("table")[0];
rowsControl.value = state["oldsize"]
if (state["newsize"]>state["oldsize"]) {
while (table.getElementsByTagName("tr").length>rowsControl.value) {
var lastrow = table.getElementsByTagName("tr")[table.getElementsByTagName("tr").length-1]
lastrow.remove();
}
} else {
//We have to add back in rows. As we do, restore the cell data for them.
var newcells = 0;
while (table.getElementsByTagName("tr").length<rowsControl.value) {
//CREATE X amount of new rows.
var newrow = document.createElement("tr");
for (var i=0;i<COLS;i++){
var col = document.createElement("th");
col.style.border=document.querySelector("table").style.border;
col.id="pos_"+i+"_"+(Number(ROWS)+Number(newcells));
col.style.background=state[col.id];
newrow.appendChild(col);
}
newcells++;
table.appendChild(newrow);
}
}
ROWS = rowsControl.value;
}break;
case "RESIZECOLS":{
//If the old size was smaller, restore the data in PIXELS.
//If the old size was larger, just remove cols.
//Make sure to update the control.
var table = document.getElementsByTagName("table")[0];
colsControl.value = state["oldsize"]
if (state["newsize"]>state["oldsize"]) {
while (colsControl.value<COLS) {
for (var i=0;i<table.getElementsByTagName("tr").length;i++) {
var row = table.getElementsByTagName("tr")[i];
var col = row.getElementsByTagName("th")[row.getElementsByTagName("th").length-1]
var coords = getCoordinates(col)
col.remove();
}
COLS--;
}
} else {
//We have to add back in cols. As we do, restore the cell data for them.
while (colsControl.value>COLS) {
for (var i=0;i<table.getElementsByTagName("tr").length;i++) {
var row = table.getElementsByTagName("tr")[i]
var col = document.createElement("th");
col.id="pos_"+COLS+"_"+i;
col.style.background=state[col.id];
col.style.border=document.querySelector("table").style.border;
row.appendChild(col);
}
COLS++;
}
}
COLS = colsControl.value;
}break;
}
redoButton.disabled = false;
undoButton.disabled = true;
if (currentPixelState>0) {
//currentPixelState--;
undoButton.disabled = false;
}
}
function Redo() {
//Execute the re-reverse.
//console.log("Redo "+currentPixelState)
var state = pixelStates[currentPixelState++];
//console.log("Redo "+JSON.stringify(state))
switch (state["STEPTYPE"]) {
case "ADD":{
//Take each pixel and color it its previous color.
var pixels = state["PIXELS"].split(",")
for (var i=0;i<pixels.length;i+=2) {
var pixelX = pixels[i];
var pixelY = pixels[i+1];
var newColor = state["pos_"+pixelX+"_"+pixelY]
var cell = document.getElementById("pos_"+pixelX+"_"+pixelY)
cell.style.background = newColor;
}
}break;
case "FILL":{
floodFill(state["PIXELX"],state["PIXELY"],document.getElementById("pos_"+state["PIXELX"]+"_"+state["PIXELY"]).style.background,state["newcolor"])
}break;
case "RESIZEROWS":{
//If the old size was smaller, restore the data in PIXELS.
//If the old size was larger, just remove rows.
//Make sure to update the control.
var table = document.getElementsByTagName("table")[0];
rowsControl.value = state["newsize"]
if (state["newsize"]<state["oldsize"]) {
while (table.getElementsByTagName("tr").length>rowsControl.value) {
var lastrow = table.getElementsByTagName("tr")[table.getElementsByTagName("tr").length-1]
lastrow.remove();
}
} else {
//We have to add back in rows. As we do, restore the cell data for them.
var newcells = 0;
while (table.getElementsByTagName("tr").length<rowsControl.value) {
//CREATE X amount of new rows.
var newrow = document.createElement("tr");
for (var i=0;i<COLS;i++){
var col = document.createElement("th");
col.style.border=document.querySelector("table").style.border;
col.id="pos_"+i+"_"+(Number(ROWS)+Number(newcells));
col.style.background="white";
newrow.appendChild(col);
}
newcells++;
table.appendChild(newrow);
}
}
ROWS = rowsControl.value;
}break;
case "RESIZECOLS":{
//If the old size was smaller, restore the data in PIXELS.
//If the old size was larger, just remove rows.
//Make sure to update the control.
var table = document.getElementsByTagName("table")[0];
colsControl.value = state["newsize"]
if (state["newsize"]<state["oldsize"]) {
while (colsControl.value<COLS) {
for (var i=0;i<table.getElementsByTagName("tr").length;i++) {
var row = table.getElementsByTagName("tr")[i];
var col = row.getElementsByTagName("th")[row.getElementsByTagName("th").length-1]
var coords = getCoordinates(col)
col.remove();
}
COLS--;
}
} else {
//We have to add back in cols. As we do, fill in white for them.
var newcells = 0;
while (colsControl.value>COLS) {
for (var i=0;i<table.getElementsByTagName("tr").length;i++) {
var row = table.getElementsByTagName("tr")[i]
var col = document.createElement("th");
col.id="pos_"+COLS+"_"+i;
col.style.background="white";
col.style.border=document.querySelector("table").style.border;
row.appendChild(col);
}
COLS++;
}
}
COLS = colsControl.value;
}break;
}
redoButton.disabled = true;
undoButton.disabled = false;
if (currentPixelState<pixelStates.length) {
redoButton.disabled = false;
}
}
var canvas = document.getElementsByClassName("canvas")[0];
var toolbar = document.getElementsByClassName("toolbar")[0];
document.addEventListener("mousemove",(e)=>{
if (toolbar.style.visibility === "hidden" &&
e.target.tagName[0]!== "T" &&
e.target.tagName[0]!== "D") {
toolbar.style.visibility = "visible";
mouseState = -1;
}
})
var MouseListener = (e)=>{
e.preventDefault();
if (!fillTool) {
if (e.target.tagName==="TH") {
if (mouseState>=0) {
var mycoords = getCoordinates(e.target)
if (lastDrawPoint===null) {
lastDrawPoint = new Point(mycoords.x,mycoords.y);
if (!(e.target.id in changedPixels)) {
if ("PIXELS" in changedPixels) {
changedPixels["PIXELS"]+=","+mycoords.x+","+mycoords.y
} else {
changedPixels["PIXELS"]=mycoords.x+","+mycoords.y
}
changedPixels["old_"+e.target.id]=e.target.style.background;
if (mouseState<2) {
e.target.style.background=selectedColor;
changedPixels[e.target.id]=selectedColor;
} else {
e.target.style.background="white";
changedPixels[e.target.id]="white";
}
changedPixels["STEPTYPE"]="ADD"
}
} else {
//SMOOTH DRAWING
//Draw pixels for each point in
//between both positions. That
//way there's no skipped points.
var slopeX = mycoords.x - lastDrawPoint.x
var slopeY = mycoords.y - lastDrawPoint.y
//Whichever is larger is the one
//we increment by 1 pixel each turn.
if (Math.abs(slopeY)>Math.abs(slopeX)) {
//Y increases by 1.
var Xincr = slopeX/Math.abs(slopeY);
var Yincr = Math.sign(slopeY)*1;
var currX = Number(lastDrawPoint.x)
var currY = Number(lastDrawPoint.y)
while (Math.floor(currY)!==Math.floor(mycoords.y)) {
currY+=Yincr;
currX+=Xincr;
var target = document.getElementById("pos_"+Math.ceil(currX)+"_"+currY);
if (!(target.id in changedPixels)) {
if ("PIXELS" in changedPixels) {
changedPixels["PIXELS"]+=","+Math.ceil(currX)+","+currY
} else {
changedPixels["PIXELS"]=Math.ceil(currX)+","+currY
}
changedPixels["old_"+target.id]=target.style.background;
if (mouseState<2) {
target.style.background=selectedColor;
changedPixels[target.id]=selectedColor;
} else {
target.style.background="white";
changedPixels[target.id]="white";
}
changedPixels["STEPTYPE"]="ADD"
}
if (currX>COLS || currY>ROWS || currX<0 || currY<0) {
new Error("Rip")
break;
}
}
} else {
var Xincr = Math.sign(slopeX)*1;
var Yincr = slopeY/Math.abs(slopeX);
var currX = Number(lastDrawPoint.x)
var currY = Number(lastDrawPoint.y)
while (Math.floor(currX)!==Math.floor(mycoords.x)) {
currY+=Yincr;
currX+=Xincr;
var target = document.getElementById("pos_"+currX+"_"+Math.ceil(currY));
if (!(target.id in changedPixels)) {
if ("PIXELS" in changedPixels) {
changedPixels["PIXELS"]+=","+currX+","+Math.ceil(currY)
} else {
changedPixels["PIXELS"]=currX+","+Math.ceil(currY)
}
changedPixels["old_"+target.id]=target.style.background;
if (mouseState<2) {
target.style.background=selectedColor;
changedPixels[target.id]=selectedColor;
} else {
target.style.background="white";
changedPixels[target.id]="white";
}
changedPixels["STEPTYPE"]="ADD"
}
if (currX>COLS || currY>ROWS || currX<0 || currY<0) {
new Error("Rip")
break;
}
}
}
lastDrawPoint = new Point(mycoords.x,mycoords.y);
}
}
}
}
}
var MouseClickListener = (e)=>{
e.preventDefault();
}
var MouseStateUp = (e)=>{
e.preventDefault();
mouseState = -1;
if (fillTool) {
if (e.target.tagName==="TH") {
var coords = getCoordinates(e.target)
floodFill(coords.x,coords.y,e.target.style.background,selectedColor)
/*changedPixels["STEPTYPE"]="FILL"
changedPixels["backcolor"]=e.target.style.background
changedPixels["newcolor"]=selectedColor
changedPixels["PIXELX"]=coords.x
changedPixels["PIXELY"]=coords.y*/
}
}
toolbar.style.visibility = "visible";
//console.log(changedPixels);
AddStoredPixelStep();
}
var MouseStateDown = (e)=>{
e.preventDefault();
mouseState = e.button;
lastDrawPoint = null;
if (!fillTool) {
if (e.target.tagName==="TH") {
var mycoords = getCoordinates(e.target)
if (!(e.target.id in changedPixels)) {
if ("PIXELS" in changedPixels) {
changedPixels["PIXELS"]+=","+mycoords.x+","+mycoords.y
} else {
changedPixels["PIXELS"]=mycoords.x+","+mycoords.y
}
changedPixels["old_"+e.target.id]=e.target.style.background;
if (e.button===0) {
e.target.style.background=selectedColor;
changedPixels[e.target.id]=selectedColor;
} else {
e.target.style.background="white";
changedPixels[e.target.id]="white";
}
changedPixels["STEPTYPE"]="ADD"
}
}
}
toolbar.style.visibility = "hidden";
}
var colorPixelBasedOnID = (x,y,color)=>{
var cell = document.getElementById("pos_"+x+"_"+y);
cell.style.background=color;
}
var generateTable = (columns,rows)=>{
var table = document.createElement("table");
table.style.border="1px gray dashed";
for (var i=0;i<rows;i++) {
var row = document.createElement("tr");
for (var j=0;j<columns;j++) {
var col = document.createElement("th");
col.style.background="white";
col.id="pos_"+j+"_"+i;
row.appendChild(col);
}
table.appendChild(row);
}
table.addEventListener("click",MouseClickListener);
table.addEventListener("mouseover",MouseListener);
document.addEventListener("mouseup",MouseStateUp);
table.addEventListener("mousedown",MouseStateDown);
canvas.appendChild(table);
}
var createCustomColorInput = (customColorContainer,dot)=>{
var customColorInput = document.createElement("input");
customColorInput.type="color"
customColorInput.value="#222222"
customColorInput.classList.add("customColor")
customColorInput.id="customColor"
insertAfter(customColorInput,dot);
customColorInput.addEventListener("change",(e)=>{
dot.style.background=e.target.value
selectedColor=e.target.value
})
dot.src="transparent.png";
dot.style.background=customColorInput.value;
customColorToolbar=true;
}
var generateColors = (simple)=>{
for (var i=0;i<CSS_COLOR_NAMES.length;i++) {
var dot = document.createElement("span")
dot.classList.add("dot")
if (CSS_COLOR_NAMES[i]===selectedColor) {
dot.style.border=selectedColorBorder;
}
dot.style.background=CSS_COLOR_NAMES[i];
dot.addEventListener("mousedown",(e)=>{
e.preventDefault();
var otherDots = document.getElementsByClassName("dot");
for (var i=0;i<otherDots.length;i++) {
var dot = otherDots[i];
dot.style.border=unselectedColorBorder;
}
e.target.style.border=selectedColorBorder;
selectedColor = e.target.style.background;
})
toolbar.appendChild(dot);
}
var customColorContainer = document.createElement("div");
//customColorContainer.classList.add("toolbar")
var dot = document.createElement("img")
dot.classList.add("dot")
dot.classList.add("inline")
dot.src="colorwheel.png"
customColorContainer.append(dot);
toolbar.appendChild(customColorContainer);
customColorContainer.addEventListener("click",(e)=>{
if (customColorToolbar) {
var otherDots = document.getElementsByClassName("dot");
for (var i=0;i<otherDots.length;i++) {
var dot2 = otherDots[i];
dot2.style.border=unselectedColorBorder;
}
dot.style.border=selectedColorBorder;
selectedColor=e.target.style.background
} else {
createCustomColorInput(customColorContainer,dot);
}
})
}
var ModifyRows = (e)=>{
var table = document.getElementsByTagName("table")[0];
if (e.target.value>table.getElementsByTagName("tr").length) {
//Add a new row.
//To add a new row, just append a new row and a new set of cells.
var newcells = 0;
while (table.getElementsByTagName("tr").length<e.target.value) {
//CREATE X amount of new rows.
var newrow = document.createElement("tr");
for (var i=0;i<COLS;i++){
var col = document.createElement("th");
col.style.background="white";
col.style.border=document.querySelector("table").style.border;
col.id="pos_"+i+"_"+(Number(ROWS)+Number(newcells));
newrow.appendChild(col);
}
newcells++;
table.appendChild(newrow);
}
} else {
//Remove the last row.
while (table.getElementsByTagName("tr").length>e.target.value) {
//REMOVE X amount of rows.
var lastrow = table.getElementsByTagName("tr")[table.getElementsByTagName("tr").length-1]
//For all cells, store their color values.
for (var cell of lastrow.getElementsByTagName("th")) {
var coords = getCoordinates(cell)
changedPixels[cell.id]=cell.style.background
}
lastrow.remove();
}
}
changedPixels["newsize"]=e.target.value;
changedPixels["oldsize"]=ROWS
changedPixels["STEPTYPE"]="RESIZEROWS"
ROWS = e.target.value;
AddStoredPixelStep();
}
var ModifyCols = (e)=>{
var table = document.getElementsByTagName("table")[0];
changedPixels["oldsize"]=COLS
if (e.target.value>COLS) {
//console.log("In here.")
while (e.target.value>COLS) {
for (var i=0;i<table.getElementsByTagName("tr").length;i++) {
var row = table.getElementsByTagName("tr")[i]
var col = document.createElement("th");
col.style.background="white";
col.style.border=document.querySelector("table").style.border;
col.id="pos_"+COLS+"_"+i;
row.appendChild(col);
}
COLS++;
}
} else {
while (e.target.value<COLS) {
for (var i=0;i<table.getElementsByTagName("tr").length;i++) {
var row = table.getElementsByTagName("tr")[i];
var col = row.getElementsByTagName("th")[row.getElementsByTagName("th").length-1]
var coords = getCoordinates(col)
changedPixels[col.id]=col.style.background
col.remove();
//For all cells, store their color values.
}
COLS--;
}
}
changedPixels["newsize"]=e.target.value;
changedPixels["STEPTYPE"]="RESIZECOLS"
COLS = e.target.value;
AddStoredPixelStep();
}
var AddStoredPixelStep = ()=>{
if ("STEPTYPE" in changedPixels) {
//Add changedPixels to the current pixelState.
if (currentPixelState!==-1 && currentPixelState!==pixelStates.length) {
//Delete everything after this.
if (currentPixelState===0) {
pixelStates = [];
} else {
pixelStates = pixelStates.slice(0,currentPixelState)
}
redoButton.disabled = true;
}
if (pixelStates.length<20) {
pixelStates.push(changedPixels);
undoButton.disabled = false;
redoButton.disabled = true;
} else {
pixelStates = pixelStates.slice(1)
pixelStates.push(changedPixels)
}
changedPixels = {}
//console.log(pixelStates)
currentPixelState = pixelStates.length;
}
}
var LoadData = ()=>{
var data = JSON.parse(localStorage.getItem("save_pixelart"))
var ROWSDATA = document.getElementById("rowcontrol")
ROWSDATA.value=data["ROWS"]
ModifyRows({"target":ROWSDATA});
var COLSDATA = document.getElementById("colcontrol")
COLSDATA.value=data["COLS"]
ModifyCols({"target":COLSDATA});
if ("CUSTOMCOLOR" in data) {
if (!customColorToolbar) {
var customColorContainer = document.getElementsByClassName("toolbar")[0];
var dot = document.getElementsByTagName("img")[0];
createCustomColorInput(customColorContainer,dot);
}
document.getElementsByTagName("img")[0].style.background = data["CUSTOMCOLOR"];
document.getElementById("customColor").value = rgbToHex(data["CUSTOMCOLOR"]);
}
for (var i=0;i<ROWS;i++) {
for (var j=0;j<COLS;j++) {
var posKey = "pos_"+j+"_"+i;
var cell = document.getElementById(posKey);
cell.style.background=data[posKey]
}
}
var consoleText = document.createElement("span")
consoleText.innerHTML = "Loaded!"
consoleText.classList.add("console")
toolbar.appendChild(consoleText);
setTimeout(()=>{
var consoleText = document.getElementsByClassName("console")[0];
consoleText.remove();
},2000)
undoButton.disabled=true;
redoButton.disabled=true;
pixelStates = []
currentPixelState = -1;
}
var SaveData = ()=>{
var finalData = {};
for (var i=0;i<ROWS;i++) {
for (var j=0;j<COLS;j++) {
var posKey = "pos_"+j+"_"+i;
var cell = document.getElementById(posKey);
finalData[posKey]=cell.style.background
}
}
finalData["ROWS"]=ROWS;
finalData["COLS"]=COLS;
finalData["CUSTOMCOLOR"]=document.getElementsByTagName("img")[0].style.background;
console.log(JSON.stringify(finalData))
localStorage.setItem("save_pixelart",JSON.stringify(finalData))
var consoleText = document.createElement("span")
consoleText.innerHTML = "Saved!"
consoleText.classList.add("console")
toolbar.appendChild(consoleText);
setTimeout(()=>{
var consoleText = document.getElementsByClassName("console")[0];
consoleText.remove();
},2000)
}
var generateControls = ()=>{
var rowsLabel = document.createElement("label")
rowsLabel.innerHTML = "Rows"
rowsLabel.classList.add("tinylabel")
rowsControl.type = "number"
rowsControl.classList.add("inline")
rowsControl.classList.add("small")
rowsControl.id = "rowcontrol"
rowsControl.value = ROWS;
rowsControl.addEventListener("change",ModifyRows)
toolbar.appendChild(rowsLabel);
toolbar.appendChild(rowsControl);
var colsLabel = document.createElement("label")
colsLabel.innerHTML = "Cols"
colsLabel.classList.add("tinylabel")
colsControl.type = "number"
colsControl.classList.add("inline")
colsControl.classList.add("small")
colsControl.id = "colcontrol"
colsControl.value = COLS;
colsControl.addEventListener("change",ModifyCols)
toolbar.appendChild(colsLabel);
toolbar.appendChild(colsControl);
var loadButton = document.createElement("button")
loadButton.type = "button"
loadButton.innerText = "Load"
loadButton.classList.add("loadbutton")
loadButton.addEventListener("click",LoadData)
var saveButton = document.createElement("button")
saveButton.type = "button"
saveButton.innerText = "Save"
saveButton.classList.add("savebutton")
saveButton.addEventListener("click",SaveData)
toolbar.appendChild(loadButton);
toolbar.appendChild(saveButton);
undoButton.type = "button"
undoButton.innerText = "Undo"
undoButton.classList.add("undobutton")
undoButton.disabled = true
undoButton.addEventListener("click",Undo)
redoButton.type = "button"
redoButton.innerText = "Redo"
redoButton.classList.add("redobutton")
redoButton.addEventListener("click",Redo)
redoButton.disabled = true
toolbar.appendChild(undoButton);
toolbar.appendChild(redoButton);
var toggleGridButton = document.createElement("img");
toggleGridButton.src = "gridtoggle.png";
toggleGridButton.classList.add("tinybutton");
toggleGridButton.addEventListener("click",()=>{
var elements = document.querySelectorAll("table,tr,th")
if (document.querySelector("body > div.container > div > table").style.border.includes("1px")) {
for (var item of elements) {
item.style.border = "0px";
}
} else {
for (var item of elements) {
item.style.border = "1px gray dashed";
}
}
})
toolbar.appendChild(toggleGridButton);
var fillToolButton = document.createElement("img");
fillToolButton.src = "filltool.png"
fillToolButton.classList.add("tinybutton")
fillToolButton.addEventListener("click",()=>{
fillTool = !fillTool
if (fillTool) {
fillToolButton.style.border = "2px red solid"
} else {
fillToolButton.style.border = "2px black solid"
}
})
toolbar.appendChild(fillToolButton);
}
var floodFill = (startx,starty,baseColor,newColor,force=false)=>{
//console.log("Flood fill at "+startx+","+starty)
//Start a flood fill in 4 cardinations directions and the current spot.
//Set the base color to what the dot currently is. Then all around this spot, fill in any dots that are also this color. Don't spread the dot if there's not a color of that type.
if (baseColor===newColor) {
return
}
startx = Number(startx)
starty = Number(starty)
if (document.getElementById("pos_"+(startx)+"_"+(starty)).style.background===baseColor) {
var target = document.getElementById("pos_"+(startx)+"_"+(starty));
if (!(target.id in changedPixels)) {
var mycoords = getCoordinates(target)
if ("PIXELS" in changedPixels) {
changedPixels["PIXELS"]+=","+mycoords.x+","+mycoords.y
} else {
changedPixels["PIXELS"]=mycoords.x+","+mycoords.y
}
changedPixels["old_"+target.id]=target.style.background;
changedPixels[target.id]=newColor
changedPixels["STEPTYPE"]="ADD"
}
target.style.background = newColor
floodFill(startx,starty,baseColor,newColor,force)
}
if (startx+1 < COLS && document.getElementById("pos_"+(startx+1)+"_"+(starty+0)).style.background===baseColor) {
var target = document.getElementById("pos_"+(startx+1)+"_"+(starty));
if (!(target.id in changedPixels)) {
var mycoords = getCoordinates(target)
if ("PIXELS" in changedPixels) {
changedPixels["PIXELS"]+=","+mycoords.x+","+mycoords.y
} else {
changedPixels["PIXELS"]=mycoords.x+","+mycoords.y
}
changedPixels["old_"+target.id]=target.style.background;
changedPixels[target.id]=newColor
changedPixels["STEPTYPE"]="ADD"
}
target.style.background = newColor
floodFill(startx+1,starty,baseColor,newColor,force)
}
if (startx-1 >= 0 && document.getElementById("pos_"+(startx-1)+"_"+(starty+0)).style.background===baseColor) {
var target = document.getElementById("pos_"+(startx-1)+"_"+(starty));
if (!(target.id in changedPixels)) {
var mycoords = getCoordinates(target)
if ("PIXELS" in changedPixels) {
changedPixels["PIXELS"]+=","+mycoords.x+","+mycoords.y
} else {
changedPixels["PIXELS"]=mycoords.x+","+mycoords.y
}
changedPixels["old_"+target.id]=target.style.background;
changedPixels[target.id]=newColor
changedPixels["STEPTYPE"]="ADD"
}
target.style.background = newColor
floodFill(startx-1,starty,baseColor,newColor,force)
}
if (starty+1 < ROWS && document.getElementById("pos_"+(startx)+"_"+(starty+1)).style.background===baseColor) {
var target = document.getElementById("pos_"+(startx)+"_"+(starty+1));
if (!(target.id in changedPixels)) {
var mycoords = getCoordinates(target)
if ("PIXELS" in changedPixels) {
changedPixels["PIXELS"]+=","+mycoords.x+","+mycoords.y
} else {
changedPixels["PIXELS"]=mycoords.x+","+mycoords.y
}
changedPixels["old_"+target.id]=target.style.background;
changedPixels[target.id]=newColor
changedPixels["STEPTYPE"]="ADD"
}
target.style.background = newColor
floodFill(startx,starty+1,baseColor,newColor,force)
}
if (starty-1 >= 0 && document.getElementById("pos_"+(startx)+"_"+(starty-1)).style.background===baseColor) {
var target = document.getElementById("pos_"+(startx)+"_"+(starty-1));
if (!(target.id in changedPixels)) {
var mycoords = getCoordinates(target)
if ("PIXELS" in changedPixels) {
changedPixels["PIXELS"]+=","+mycoords.x+","+mycoords.y
} else {
changedPixels["PIXELS"]=mycoords.x+","+mycoords.y
}
changedPixels["old_"+target.id]=target.style.background;
changedPixels[target.id]=newColor
changedPixels["STEPTYPE"]="ADD"
}
target.style.background = newColor
floodFill(startx,starty-1,baseColor,newColor,force)
}
}
generateTable(ROWS,COLS);
canvas.appendChild(document.createElement("br"))
generateColors(false);
generateControls();
document.addEventListener("scroll",()=>{
toolbar.style.position="absolute";
toolbar.style.left=window.scrollX+"px";
toolbar.style.bottom=-window.scrollY+"px";
})
})

View File

@ -1,18 +0,0 @@
const CSS_COLOR_NAMES = [
"black",
"silver",
"gray",
"white",
"maroon",
"red",
"purple",
"fuchsia",
"green",
"lime",
"olive",
"yellow",
"navy",
"blue",
"teal",
"aqua"
];

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

219
index.css Normal file
View File

@ -0,0 +1,219 @@
html {
box-sizing: border-box;
}
* {
box-sizing: inherit;
}
body {
background-color: #ccc;
}
h2 {
color: #999;
font-family: Gill Sans;
font-size: 1.5rem;
font-weight: 400;
letter-spacing: 3px;
line-height: 2.2rem;
margin: 6px 0 0 20px;
}
main {
background-color: #e5e5e5;
border-radius: 15px;
box-shadow: 0 0 20px #999;
margin: 20px auto 0;
padding: 20px;
width: 894px;
}
.pixel {
background-color: white;
border: 1px solid #e5e5e5;
height: 14px;
width: 14px;
}
.brushIndicator {
border: 1px solid #999999;
border-radius: 3px;
height: 35.5px;
margin: 6px 0 0 12px;
width: 100px;
}
.color {
/* Prevent classes like 'carmine-red' from specifying the border-color */
border: 1px solid #bbbbbb !important;
border-radius: 50%;
height: 35.5px;
margin: 5px 5px 0 0;
width: 35.5px;
}
.carmine-red {
background-color: #b23232;
border-color: #b23232;
}
.coral-red {
background-color: #ff4848;
border-color: #ff4848;
}
.pastel-red {
background-color: #ff6c6c;
border-color: #ff6c6c;
}
.tigers-eye-orange {
background-color: #e59b40;
border-color: #e59b40;
}
.pastel-orange {
background-color: #ffad48;
border-color: #ffad48;
}
.topaz-orange {
background-color: #ffc57e;
border-color: #ffc57e;
}
.sandstorm-yellow {
background-color: #e5de40;
border-color: #e5de40;
}
.lemon-yellow {
background-color: #fff748;
border-color: #fff748;
}
.pastel-yellow {
background-color: #fffa91;
border-color: #fffa91;
}
.lime-green {
background-color: #39cc4b;
border-color: #39cc4b;
}
.ufo-green {
background-color: #48ff5e;
border-color: #48ff5e;
}
.mint-green {
background-color: #91ff9e;
border-color: #91ff9e;
}
.cerulean-blue {
background-color: #3248b2;
border-color: #3248b2;
}
.ultramarine-blue {
background-color: #4867ff;
border-color: #4867ff;
}
.carolina-blue {
background-color: #91a3ff;
border-color: #91a3ff;
}
.purple-heart {
background-color: #6432b2;
border-color: #6432b2;
}
.lavender-indigo {
background-color: #8f48ff;
border-color: #8f48ff;
}
.bright-lavender {
background-color: #bb91ff;
border-color: #bb91ff;
}
.bruise-purple {
background-color: #7c2b99;
border-color: #7c2b99;
}
.heliotrope-purple {
background-color: #cf48ff;
border-color: #cf48ff;
}
.lavender-magenta {
background-color: #e291ff;
border-color: #e291ff;
}
.black-black {
background-color: #000000;
border-color: #000000;
}
.drab-black {
background-color: #323232;
border-color: #323232;
}
.dim-gray {
background-color: #666666;
border-color: #666666;
}
.manatee-gray {
background-color: #999999;
border-color: #999999;
}
.pastel-gray {
background-color: #cccccc;
border-color: #cccccc;
}
.white-white {
background-color: #ffffff;
border-color: #ffffff;
}
.bistre-brown {
background-color: #3a2119;
border-color: #3a2119;
}
.noir-brown {
background-color: #512e23;
border-color: #512e23;
}
.bole-brown {
background-color: #754233;
border-color: #754233;
}
.chestnut-brown {
background-color: #90675b;
border-color: #90675b;
}
.grullo-brown {
background-color: #ac8d84;
border-color: #ac8d84;
}
#canvas, #palette {
display: flex;
flex-wrap: wrap;
}

View File

@ -1,19 +1,15 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="style.css">
<script src="colorList.js"></script>
<script src="canvas.js"></script>
</head>
<body>
<div class="container">
<div class="canvas">
</div>
</div>
<div class="toolbar">
</div>
</body>
</html>
<head>
<meta charset="utf-8">
<title>Pixel Art Maker</title>
<script defer src="index.js"></script>
<link rel="stylesheet" href="index.css" />
</head>
<body>
<main>
<div id="canvas"></div>
<div id="palette"></div>
</main>
</body>
</html>

124
index.js Normal file
View File

@ -0,0 +1,124 @@
(function() {
'use strict';
let brushClass = 'drab-black';
const paintPixel = function(pixel) {
pixel.classList.remove(pixel.classList.item(1));
pixel.classList.add(brushClass);
}
const drawCanvas = function() {
const canvas = document.querySelector('#canvas');
for (let i = 0; i < 2013; i++) {
const pixel = document.createElement('div');
pixel.classList.add('pixel');
canvas.appendChild(pixel);
}
canvas.addEventListener('click', (event) => {
if (event.target === canvas) {
return;
}
paintPixel(event.target);
});
// Bonus 1 - paintbrush
//
// let isPainting = false;
//
// canvas.addEventListener('mousedown', () => {
// isPainting = true;
//
// if (event.target === canvas) {
// return;
// }
//
// paintPixel(event.target);
// });
//
// document.addEventListener('mouseup', () => {
// isPainting = false;
// });
//
// const pixels = document.querySelectorAll('.pixel');
//
// for (const pixel of pixels) {
// pixel.addEventListener('mouseenter', () => {
// if (isPainting) {
// paintPixel(event.target);
// }
// });
// }
}
const drawPalette = function() {
const palette = document.querySelector('#palette');
const colorClasses = [
'carmine-red',
'coral-red',
'pastel-red',
'tigers-eye-orange',
'pastel-orange',
'topaz-orange',
'sandstorm-yellow',
'lemon-yellow',
'pastel-yellow',
'lime-green',
'ufo-green',
'mint-green',
'cerulean-blue',
'ultramarine-blue',
'carolina-blue',
'purple-heart',
'lavender-indigo',
'bright-lavender',
'bruise-purple',
'heliotrope-purple',
'lavender-magenta',
'black-black',
'drab-black',
'dim-gray',
'manatee-gray',
'pastel-gray',
'white-white',
'bistre-brown',
'noir-brown',
'bole-brown',
'chestnut-brown',
'grullo-brown'
];
for (const colorClass of colorClasses) {
const color = document.createElement('div');
color.classList.add('color', colorClass);
palette.appendChild(color);
}
const heading = document.createElement('h2');
heading.textContent = 'BRUSH COLOR >';
const brushIndicator = document.createElement('div');
brushIndicator.classList.add('brushIndicator', brushClass);
palette.appendChild(heading);
palette.appendChild(brushIndicator);
palette.addEventListener('click', (event) => {
if (event.target === palette) {
return;
}
brushClass = event.target.classList.item(1);
brushIndicator.classList.remove(brushIndicator.classList.item(1));
brushIndicator.classList.add(brushClass);
});
}
drawCanvas();
drawPalette();
})();

View File

@ -1,78 +0,0 @@
table,tr,th{
border:1px gray dashed;
border-spacing:0px;
gap:0px;
}
th{
min-width:16px;
min-height:16px;
width:16px;
height:16px;
}
.container{
text-align: center;
}
.toolbar{
width: 100%;
}
.canvas{
display:inline-block;
background-color: #EEEEFF;
border-radius: 10%;
padding: 48px;
}
.customColor{
vertical-align: middle;
position:relative;
top:-8px;
}
.loadbutton{
margin: 0px 2px 0px 2px;
background-color: #9999FF;
}
.savebutton{
margin: 0px 2px 0px 2px;
background-color: #FF9999;
}
.undobutton{
margin: 0px 2px 0px 2px;
background-color: #99FF99;
}
.redobutton{
margin: 0px 2px 0px 2px;
background-color: #FFFF99;
}
.console{
font-style: strong;
margin-left: 4px;
}
.inline{
display:inline;
}
.small{
width:36px;
}
.tinylabel{
font-size: 12px;
}
.dot{
height: 25px;
width: 25px;
background-color: #bbb;
border-radius: 50%;
display: inline-block;
border:2px black solid;
margin-left:1px;
}
.tinybutton{
margin-top:-24px;
position:relative;
top: 6px;
height: 24px;
width: 24px;
background-color: #bbb;
border-radius: 50%;
display: inline-block;
border:2px black solid;
margin-left:1px;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB