mirror of
https://github.com/lospec/pixel-editor.git
synced 2023-08-10 21:12:51 +03:00
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:
parent
6cdc764586
commit
ab4129546c
@ -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
|
||||
|
||||
|
95
js/_layer.js
95
js/_layer.js
@ -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;
|
||||
}
|
@ -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){
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
});
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user