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:
@ -35,6 +35,7 @@ body {
|
|||||||
z-index: 1120;
|
z-index: 1120;
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
overflow-y:scroll;
|
overflow-y:scroll;
|
||||||
|
overflow-x:hidden;
|
||||||
|
|
||||||
// TODO: make the scroll bar a bit fancier
|
// 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
|
* Merge with bottom layer option
|
||||||
* Flatten visible option
|
* Flatten visible option
|
||||||
* Flatten everything 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
|
- 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
|
- 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
|
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
|
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:
|
OPTIONAL:
|
||||||
|
|
||||||
1 - Fix issues
|
1 - Fix issues
|
||||||
@ -27,8 +35,6 @@
|
|||||||
THINGS TO TEST:
|
THINGS TO TEST:
|
||||||
|
|
||||||
1 - Undo / redo
|
1 - Undo / redo
|
||||||
2 - Copy / cut / paste selection
|
|
||||||
3 - Colour picking from underlying layer
|
|
||||||
4 - File export
|
4 - File export
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -52,6 +58,9 @@ let layerDragSource = null;
|
|||||||
let layerCount = 1;
|
let layerCount = 1;
|
||||||
let maxZIndex = 3;
|
let maxZIndex = 3;
|
||||||
|
|
||||||
|
let unusedIDs = [];
|
||||||
|
let currentID = layerCount;
|
||||||
|
|
||||||
on('click',"add-layer-button", function(){
|
on('click',"add-layer-button", function(){
|
||||||
// Creating a new canvas
|
// Creating a new canvas
|
||||||
let newCanvas = document.createElement("canvas");
|
let newCanvas = document.createElement("canvas");
|
||||||
@ -98,8 +107,20 @@ class Layer {
|
|||||||
this.isLocked = false;
|
this.isLocked = false;
|
||||||
this.menuEntry = menuEntry;
|
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) {
|
if (menuEntry != null) {
|
||||||
console.log("Aggiungo eventi");
|
|
||||||
menuEntry.onclick = () => this.select();
|
menuEntry.onclick = () => this.select();
|
||||||
menuEntry.getElementsByTagName("button")[0].onclick = () => this.toggleLock();
|
menuEntry.getElementsByTagName("button")[0].onclick = () => this.toggleLock();
|
||||||
menuEntry.getElementsByTagName("button")[1].onclick = () => this.toggleVisibility();
|
menuEntry.getElementsByTagName("button")[1].onclick = () => this.toggleVisibility();
|
||||||
@ -140,18 +161,14 @@ class Layer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
layerDragStart(element) {
|
layerDragStart(element) {
|
||||||
console.log("Elemento: " + element.dataTransfer.getData('text/html'));
|
|
||||||
console.log("UELA");
|
|
||||||
|
|
||||||
layerDragSource = this;
|
layerDragSource = this;
|
||||||
element.dataTransfer.effectAllowed = 'move';
|
element.dataTransfer.effectAllowed = 'move';
|
||||||
element.dataTransfer.setData('text/html', this.outerHTML);
|
element.dataTransfer.setData('text/html', this.id);
|
||||||
|
|
||||||
this.classList.add('dragElem');
|
this.classList.add('dragElem');
|
||||||
}
|
}
|
||||||
|
|
||||||
layerDragOver(element) {
|
layerDragOver(element) {
|
||||||
console.log("Elemento: " + element.dataTransfer.getData('text/html'));
|
|
||||||
if (element.preventDefault) {
|
if (element.preventDefault) {
|
||||||
element.preventDefault(); // Necessary. Allows us to drop.
|
element.preventDefault(); // Necessary. Allows us to drop.
|
||||||
}
|
}
|
||||||
@ -163,7 +180,6 @@ class Layer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
layerDragLeave(element) {
|
layerDragLeave(element) {
|
||||||
console.log("Elemento: " + element.dataTransfer.getData('text/html'));
|
|
||||||
this.classList.remove('layerdragover');
|
this.classList.remove('layerdragover');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,22 +191,21 @@ class Layer {
|
|||||||
|
|
||||||
// Don't do anything if dropping the same column we're dragging.
|
// Don't do anything if dropping the same column we're dragging.
|
||||||
if (layerDragSource != this) {
|
if (layerDragSource != this) {
|
||||||
// Set the source column's HTML to the HTML of the column we dropped on.
|
let toDropID = element.dataTransfer.getData('text/html');
|
||||||
this.parentNode.removeChild(layerDragSource);
|
let thisID = this.id;
|
||||||
var dropHTML = element.dataTransfer.getData('text/html');
|
|
||||||
this.insertAdjacentHTML('beforebegin',dropHTML);
|
|
||||||
var dropElem = this.previousSibling;
|
|
||||||
|
|
||||||
addDnDHandlers(dropElem);
|
console.log("ID di quello spostato: " + toDropID + ", di quello su cui metterlo: " + thisID);
|
||||||
|
swapLayerEntries(toDropID, thisID);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.classList.remove('layerdragover');
|
this.classList.remove('layerdragover');
|
||||||
|
dragging = false;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
layerDragEnd(element) {
|
layerDragEnd(element) {
|
||||||
console.log("Elemento: " + element.dataTransfer.getData('text/html'));
|
console.log(currentLayer);
|
||||||
this.classList.remove('layerdragover');
|
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
|
// Finds a layer given its name
|
||||||
function getLayerByName(name) {
|
function getLayerByName(name) {
|
||||||
for (let i=0; i<layers.length; i++) {
|
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;
|
return null;
|
||||||
}
|
}
|
@ -76,11 +76,15 @@ window.addEventListener("mouseup", function (mouseEvent) {
|
|||||||
if (currentTool.name == 'eyedropper' && mouseEvent.target.className == 'drawingCanvas') {
|
if (currentTool.name == 'eyedropper' && mouseEvent.target.className == 'drawingCanvas') {
|
||||||
var cursorLocation = getCursorPosition(mouseEvent);
|
var cursorLocation = getCursorPosition(mouseEvent);
|
||||||
// TODO: adjust so that if the picked colour is transparent, the underlying layer is checked
|
// 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 selectedColor = getEyedropperColor(cursorLocation);
|
||||||
var newColor = rgbToHex(selectedColor.data[0],selectedColor.data[1],selectedColor.data[2]);
|
var newColor = rgbToHex(selectedColor[0],selectedColor[1],selectedColor[2]);
|
||||||
|
|
||||||
currentGlobalColor = "#" + newColor;
|
currentGlobalColor = "#" + newColor;
|
||||||
|
|
||||||
|
for (let i=1; i<layers.length - 1; i++) {
|
||||||
|
layers[i].context.fillStyle = currentGlobalColor;
|
||||||
|
}
|
||||||
|
|
||||||
var colors = document.getElementsByClassName('color-button');
|
var colors = document.getElementsByClassName('color-button');
|
||||||
for (var i = 0; i < colors.length; i++) {
|
for (var i = 0; i < colors.length; i++) {
|
||||||
console.log(colors[i].jscolor.toString());
|
console.log(colors[i].jscolor.toString());
|
||||||
@ -269,7 +273,7 @@ function draw (mouseEvent) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (currentTool.name == '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;
|
let selectedColor = getEyedropperColor(cursorLocation);
|
||||||
|
|
||||||
eyedropperPreview.style.borderColor = '#'+rgbToHex(selectedColor[0],selectedColor[1],selectedColor[2]);
|
eyedropperPreview.style.borderColor = '#'+rgbToHex(selectedColor[0],selectedColor[1],selectedColor[2]);
|
||||||
eyedropperPreview.style.display = 'block';
|
eyedropperPreview.style.display = 'block';
|
||||||
@ -379,4 +383,4 @@ canvasView.addEventListener("wheel", function(mouseEvent){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
@ -1,10 +1,22 @@
|
|||||||
function newPixel (width, height, palette) {
|
let firstPixel = true;
|
||||||
layerList = document.getElementById("layers-menu");
|
|
||||||
layerListEntry = layerList.firstElementChild;
|
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
|
// Setting up the current layer
|
||||||
currentLayer = new Layer(width, height, canvas, layerListEntry);
|
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
|
// Cloning the entry so that when I change something on the first layer, those changes aren't
|
||||||
// propagated to the other ones
|
// propagated to the other ones
|
||||||
|
@ -1,7 +1,35 @@
|
|||||||
function isPixelEmpty(pixel) {
|
function isPixelEmpty(pixel) {
|
||||||
|
if (pixel == null || pixel === undefined) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if ((pixel[0] == 0 && pixel[1] == 0 && pixel[2] == 0) || pixel[3] == 0) {
|
if ((pixel[0] == 0 && pixel[1] == 0 && pixel[2] == 0) || pixel[3] == 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
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;
|
||||||
}
|
}
|
Reference in New Issue
Block a user