Added layer move

The user can now move layers and choose which one to put on top of each other just by dragging and dropping them in the layer menu. Also fixed a bug in the eyedropper, that can now pick a colour even though the layer it's on is not selected.
This commit is contained in:
unsettledgames 2020-06-19 23:31:36 +02:00
parent 6cdc764586
commit ab4129546c
5 changed files with 131 additions and 25 deletions

View File

@ -35,6 +35,7 @@ body {
z-index: 1120;
list-style-type: none;
overflow-y:scroll;
overflow-x:hidden;
// TODO: make the scroll bar a bit fancier

View File

@ -8,12 +8,20 @@
* Merge with bottom layer option
* Flatten visible option
* Flatten everything option
- Must move a layer when dragging it in the layer list (https://codepen.io/retrofuturistic/pen/tlbHE)
- When saving an artwork, the layers must be flattened to a temporary layer, which is then exported and deleted
- Saving the state of an artwork to a .lospec file so that people can work on it later keeping
the layers they created? That'd be cool, even for the app users, that could just double click on a lospec
file for it to be opened right in the pixel editor
HISTORY:
- Store states for every canvas
- Save add layer
- Save deleted layer
- Save merge layers
- Save flatten layers
- Save move layers
OPTIONAL:
1 - Fix issues
@ -27,8 +35,6 @@
THINGS TO TEST:
1 - Undo / redo
2 - Copy / cut / paste selection
3 - Colour picking from underlying layer
4 - File export
*/
@ -52,6 +58,9 @@ let layerDragSource = null;
let layerCount = 1;
let maxZIndex = 3;
let unusedIDs = [];
let currentID = layerCount;
on('click',"add-layer-button", function(){
// Creating a new canvas
let newCanvas = document.createElement("canvas");
@ -98,8 +107,20 @@ class Layer {
this.isLocked = false;
this.menuEntry = menuEntry;
let id = unusedIDs.pop();
if (id == null) {
id = currentID;
currentID++;
}
this.id = id;
if (menuEntry != null) {
menuEntry.id = "layer" + id;
}
if (menuEntry != null) {
console.log("Aggiungo eventi");
menuEntry.onclick = () => this.select();
menuEntry.getElementsByTagName("button")[0].onclick = () => this.toggleLock();
menuEntry.getElementsByTagName("button")[1].onclick = () => this.toggleVisibility();
@ -140,18 +161,14 @@ class Layer {
}
layerDragStart(element) {
console.log("Elemento: " + element.dataTransfer.getData('text/html'));
console.log("UELA");
layerDragSource = this;
element.dataTransfer.effectAllowed = 'move';
element.dataTransfer.setData('text/html', this.outerHTML);
element.dataTransfer.setData('text/html', this.id);
this.classList.add('dragElem');
}
layerDragOver(element) {
console.log("Elemento: " + element.dataTransfer.getData('text/html'));
if (element.preventDefault) {
element.preventDefault(); // Necessary. Allows us to drop.
}
@ -163,7 +180,6 @@ class Layer {
}
layerDragLeave(element) {
console.log("Elemento: " + element.dataTransfer.getData('text/html'));
this.classList.remove('layerdragover');
}
@ -175,22 +191,21 @@ class Layer {
// Don't do anything if dropping the same column we're dragging.
if (layerDragSource != this) {
// Set the source column's HTML to the HTML of the column we dropped on.
this.parentNode.removeChild(layerDragSource);
var dropHTML = element.dataTransfer.getData('text/html');
this.insertAdjacentHTML('beforebegin',dropHTML);
var dropElem = this.previousSibling;
let toDropID = element.dataTransfer.getData('text/html');
let thisID = this.id;
addDnDHandlers(dropElem);
console.log("ID di quello spostato: " + toDropID + ", di quello su cui metterlo: " + thisID);
swapLayerEntries(toDropID, thisID);
}
this.classList.remove('layerdragover');
dragging = false;
return false;
}
layerDragEnd(element) {
console.log("Elemento: " + element.dataTransfer.getData('text/html'));
console.log(currentLayer);
this.classList.remove('layerdragover');
}
@ -311,6 +326,39 @@ class Layer {
}
}
// Swap two layer entries in the layer menu
function swapLayerEntries(id1, id2) {
let entry1 = document.getElementById(id1);
let entry2 = document.getElementById(id2);
console.log("id1: " + id1);
console.log("id2: " + id2);
console.log("entry1: " + entry1);
console.log("entry2: " + entry2);
let layer1 = getLayerByID(id1);
let layer2 = getLayerByID(id2);
let tmpZIndex;
let after2 = entry2.nextSibling;
let parent = entry1.parentNode;
parent.insertBefore(entry2, entry1);
if (after2) {
parent.insertBefore(entry1, after2);
} else {
parent.appendChild(entry1);
}
tmpZIndex = layer1.canvas.style.zIndex;
console.log("1: " + layer1.canvas.style.zIndex);
console.log("2: " + layer2.canvas.style.zIndex);
layer1.canvas.style.zIndex = layer2.canvas.style.zIndex;
layer2.canvas.style.zIndex = tmpZIndex;
}
// Finds a layer given its name
function getLayerByName(name) {
for (let i=0; i<layers.length; i++) {
@ -321,5 +369,18 @@ function getLayerByName(name) {
}
}
return null;
}
// Finds a layer given its id
function getLayerByID(id) {
for (let i=0; i<layers.length; i++) {
if (layers[i].menuEntry != null) {
if (layers[i].menuEntry.id == id) {
return layers[i];
}
}
}
return null;
}

View File

@ -76,11 +76,15 @@ window.addEventListener("mouseup", function (mouseEvent) {
if (currentTool.name == 'eyedropper' && mouseEvent.target.className == 'drawingCanvas') {
var cursorLocation = getCursorPosition(mouseEvent);
// TODO: adjust so that if the picked colour is transparent, the underlying layer is checked
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 selectedColor = getEyedropperColor(cursorLocation);
var newColor = rgbToHex(selectedColor[0],selectedColor[1],selectedColor[2]);
currentGlobalColor = "#" + newColor;
for (let i=1; i<layers.length - 1; i++) {
layers[i].context.fillStyle = currentGlobalColor;
}
var colors = document.getElementsByClassName('color-button');
for (var i = 0; i < colors.length; i++) {
console.log(colors[i].jscolor.toString());
@ -269,7 +273,7 @@ function draw (mouseEvent) {
}
}
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;
let selectedColor = getEyedropperColor(cursorLocation);
eyedropperPreview.style.borderColor = '#'+rgbToHex(selectedColor[0],selectedColor[1],selectedColor[2]);
eyedropperPreview.style.display = 'block';
@ -379,4 +383,4 @@ canvasView.addEventListener("wheel", function(mouseEvent){
}
}
});
});

View File

@ -1,10 +1,22 @@
function newPixel (width, height, palette) {
layerList = document.getElementById("layers-menu");
layerListEntry = layerList.firstElementChild;
let firstPixel = true;
function newPixel (width, height, palette) {
if (firstPixel) {
layerList = document.getElementById("layers-menu");
layerListEntry = layerList.firstElementChild;
firstPixel = false;
}
else {
// TODO: clean layers before creating a new pixel
// Devo togliere tutte le entries tranne la prima
// Devo pulire la preview della prima entry
// Devo cancellare tutte le tele tranne quella con id pixel-canvas
}
console.log("Layer entry: " + layerListEntry);
// Setting up the current layer
currentLayer = new Layer(width, height, canvas, layerListEntry);
canvas.style.zIndex = 2;
// Cloning the entry so that when I change something on the first layer, those changes aren't
// propagated to the other ones

View File

@ -1,7 +1,35 @@
function isPixelEmpty(pixel) {
if (pixel == null || pixel === undefined) {
return false;
}
if ((pixel[0] == 0 && pixel[1] == 0 && pixel[2] == 0) || pixel[3] == 0) {
return true;
}
return false;
}
function getEyedropperColor(cursorLocation) {
let max = -1;
let tmpColour;
let selectedColor;
for (let i=1; i<layers.length; i++) {
tmpColour = layers[i].context.getImageData(Math.floor(cursorLocation[0]/zoom),Math.floor(cursorLocation[1]/zoom),1,1).data;
if (layers[i].canvas.style.zIndex > max || isPixelEmpty(selectedColor) || selectedColor === undefined) {
max = layers[i].canvas.style.zIndex;
if (!isPixelEmpty(tmpColour)) {
selectedColor = tmpColour;
}
}
}
if (isPixelEmpty(tmpColour) && selectedColor === undefined) {
selectedColor = [0, 0, 0];
}
return selectedColor;
}