Add undo/redo features for resizing the canvas, to prevent errors and restore lost pixels.

master
Joshua Sigona 5 years ago
parent 53518eb8fd
commit ea9c092b01
  1. 213
      canvas.js

@ -13,6 +13,7 @@ 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.
@ -23,6 +24,8 @@ 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) {
@ -64,7 +67,7 @@ document.addEventListener("DOMContentLoaded",()=>{
function Undo() {
//Execute the reverse.
var state = pixelStates[--currentPixelState];
console.log("Undo "+currentPixelState)
//console.log("Undo "+currentPixelState)
//console.log("Undo "+JSON.stringify(state))
switch (state["STEPTYPE"]) {
case "ADD":{
@ -81,6 +84,68 @@ document.addEventListener("DOMContentLoaded",()=>{
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;
@ -92,7 +157,7 @@ document.addEventListener("DOMContentLoaded",()=>{
function Redo() {
//Execute the re-reverse.
console.log("Redo "+currentPixelState)
//console.log("Redo "+currentPixelState)
var state = pixelStates[currentPixelState++];
//console.log("Redo "+JSON.stringify(state))
switch (state["STEPTYPE"]) {
@ -110,6 +175,69 @@ document.addEventListener("DOMContentLoaded",()=>{
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;
@ -171,13 +299,9 @@ document.addEventListener("DOMContentLoaded",()=>{
var Yincr = Math.sign(slopeY)*1;
var currX = Number(lastDrawPoint.x)
var currY = Number(lastDrawPoint.y)
var iterations=0;
while (Math.floor(currY)!==Math.floor(mycoords.y)) {
currY+=Yincr;
currX+=Xincr;
if (iterations>=1) {
console.log("X:"+currX+","+currY+"/"+lastDrawPoint.x+","+lastDrawPoint.y+"/"+mycoords.x+","+mycoords.y)
}
var target = document.getElementById("pos_"+Math.ceil(currX)+"_"+currY);
if (!(target.id in changedPixels)) {
if ("PIXELS" in changedPixels) {
@ -199,20 +323,15 @@ document.addEventListener("DOMContentLoaded",()=>{
new Error("Rip")
break;
}
iterations++;
}
} else {
var Xincr = Math.sign(slopeX)*1;
var Yincr = slopeY/Math.abs(slopeX);
var currX = Number(lastDrawPoint.x)
var currY = Number(lastDrawPoint.y)
var iterations=0;
while (Math.floor(currX)!==Math.floor(mycoords.x)) {
currY+=Yincr;
currX+=Xincr;
if (iterations>=1) {
console.log("Y:"+currX+","+currY+"/"+lastDrawPoint.x+","+lastDrawPoint.y+"/"+mycoords.x+","+mycoords.y)
}
var target = document.getElementById("pos_"+currX+"_"+Math.ceil(currY));
if (!(target.id in changedPixels)) {
if ("PIXELS" in changedPixels) {
@ -230,7 +349,6 @@ document.addEventListener("DOMContentLoaded",()=>{
}
changedPixels["STEPTYPE"]="ADD"
}
iterations++;
if (currX>COLS || currY>ROWS || currX<0 || currY<0) {
new Error("Rip")
break;
@ -262,30 +380,7 @@ document.addEventListener("DOMContentLoaded",()=>{
}
toolbar.style.visibility = "visible";
//console.log(changedPixels);
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+1)
}
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;
}
AddStoredPixelStep();
}
var MouseStateDown = (e)=>{
e.preventDefault();
@ -425,14 +520,24 @@ document.addEventListener("DOMContentLoaded",()=>{
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) {
@ -451,11 +556,45 @@ document.addEventListener("DOMContentLoaded",()=>{
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 = ()=>{
@ -528,7 +667,6 @@ document.addEventListener("DOMContentLoaded",()=>{
var rowsLabel = document.createElement("label")
rowsLabel.innerHTML = "Rows"
rowsLabel.classList.add("tinylabel")
var rowsControl = document.createElement("input")
rowsControl.type = "number"
rowsControl.classList.add("inline")
rowsControl.classList.add("small")
@ -540,7 +678,6 @@ document.addEventListener("DOMContentLoaded",()=>{
var colsLabel = document.createElement("label")
colsLabel.innerHTML = "Cols"
colsLabel.classList.add("tinylabel")
var colsControl = document.createElement("input")
colsControl.type = "number"
colsControl.classList.add("inline")
colsControl.classList.add("small")

Loading…
Cancel
Save