mirror of
https://github.com/lospec/pixel-editor.git
synced 2023-08-10 21:12:51 +03:00
Implemented merge, rename and flatten options
This commit is contained in:
parent
50b962a7f5
commit
6ad27323e5
@ -56,7 +56,6 @@ body {
|
|||||||
li button:hover {
|
li button:hover {
|
||||||
background-color:color(menu, background);
|
background-color:color(menu, background);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.preview-canvas {
|
.preview-canvas {
|
||||||
|
@ -1,27 +1,15 @@
|
|||||||
var spacePressed = false;
|
var spacePressed = false;
|
||||||
|
|
||||||
/**
|
|
||||||
Copy / paste / cut logic:
|
|
||||||
- The user selects an area
|
|
||||||
- Pressing ctrl+c copies the selection
|
|
||||||
- Pressing ctrl+v ends the current selection and copies the clipboard in the tmp layer:
|
|
||||||
the editor enters move mode and lets the user move the copied selection around.
|
|
||||||
Pressing ctrl+v while moving a copy has the same effect of pressing ctrl+v after a ctrl+c
|
|
||||||
- The behaviour of ctrl+v is the same and doesn't depend on how the selected area was obtained
|
|
||||||
(with ctrl+c or with ctrl+v)
|
|
||||||
- Selecting a different tool while moving the copied or cut selection has the same effect of selecting
|
|
||||||
a different tool while moving a standard selection
|
|
||||||
- You can paste at any other time
|
|
||||||
|
|
||||||
BUGS:
|
|
||||||
-
|
|
||||||
*/
|
|
||||||
|
|
||||||
function KeyPress(e) {
|
function KeyPress(e) {
|
||||||
var keyboardEvent = window.event? event : e;
|
var keyboardEvent = window.event? event : e;
|
||||||
|
|
||||||
//if the user is typing in an input field, ignore these hotkeys
|
//if the user is typing in an input field or renaming a layer, ignore these hotkeys, unless it's an enter key
|
||||||
if (document.activeElement.tagName == 'INPUT') return;
|
if (document.activeElement.tagName == 'INPUT' || isRenamingLayer) {
|
||||||
|
if (e.keyCode == 13) {
|
||||||
|
currentLayer.closeOptionsMenu();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
//if no document has been created yet,
|
//if no document has been created yet,
|
||||||
//orthere is a dialog box open
|
//orthere is a dialog box open
|
||||||
|
150
js/_layer.js
150
js/_layer.js
@ -1,13 +1,6 @@
|
|||||||
/** TODO LIST FOR LAYERS
|
/** TODO LIST FOR LAYERS
|
||||||
|
|
||||||
GENERAL REQUIREMENTS:
|
GENERAL REQUIREMENTS:
|
||||||
- The user shouldn't be able to draw on a hidden or locked layer
|
|
||||||
- Must delete the selected layer when right clicking on a layer and selecting that option
|
|
||||||
* We should think about selecting more than one layer at once.
|
|
||||||
* Rename layer
|
|
||||||
* Merge with bottom layer option
|
|
||||||
* Flatten visible option
|
|
||||||
* Flatten everything option
|
|
||||||
- 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
|
||||||
@ -38,6 +31,9 @@
|
|||||||
|
|
||||||
- Resize canvas (must make a simple editor to preview the changes)
|
- Resize canvas (must make a simple editor to preview the changes)
|
||||||
- Resize sprite
|
- Resize sprite
|
||||||
|
- Refactor the code so that every instance of "canvas" and "context" is replaced by currentLayer.canvas
|
||||||
|
and currentLayer.context
|
||||||
|
- Refactor and replace the merge layer algorithm with a general function in _pixelEditorUtility.js
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Instead of saving the whole entry, just save their IDs and swap the elements at the end of the drop
|
// Instead of saving the whole entry, just save their IDs and swap the elements at the end of the drop
|
||||||
@ -58,6 +54,8 @@ let currentID = layerCount;
|
|||||||
let idToDelete;
|
let idToDelete;
|
||||||
let layerOptions = document.getElementById("layer-properties-menu");
|
let layerOptions = document.getElementById("layer-properties-menu");
|
||||||
|
|
||||||
|
let isRenamingLayer = false;
|
||||||
|
|
||||||
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");
|
||||||
@ -241,6 +239,8 @@ class Layer {
|
|||||||
|
|
||||||
closeOptionsMenu(event) {
|
closeOptionsMenu(event) {
|
||||||
layerOptions.style.visibility = "hidden";
|
layerOptions.style.visibility = "hidden";
|
||||||
|
currentLayer.menuEntry.getElementsByTagName("p")[0].setAttribute("contenteditable", false);
|
||||||
|
isRenamingLayer = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
selectLayer(layer) {
|
selectLayer(layer) {
|
||||||
@ -260,6 +260,9 @@ class Layer {
|
|||||||
layer.menuEntry.classList.add("selected-layer");
|
layer.menuEntry.classList.add("selected-layer");
|
||||||
currentLayer = layer;
|
currentLayer = layer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
canvas = currentLayer.canvas;
|
||||||
|
context = currentLayer.context;
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleLock() {
|
toggleLock() {
|
||||||
@ -348,6 +351,122 @@ class Layer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function flatten(onlyVisible) {
|
||||||
|
if (!onlyVisible) {
|
||||||
|
// Selecting the first layer
|
||||||
|
let firstLayer = layerList.firstElementChild;
|
||||||
|
getLayerByID(firstLayer.id).selectLayer();
|
||||||
|
|
||||||
|
for (let i = 0; i < layerList.childElementCount - 1; i++) {
|
||||||
|
merge();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Getting all the visible layers
|
||||||
|
let visibleLayers = [];
|
||||||
|
|
||||||
|
for (let i=0; i<layers.length; i++) {
|
||||||
|
if (layers[i].menuEntry != null && layers[i].isVisible) {
|
||||||
|
visibleLayers.push(layers[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sorting them by z-index
|
||||||
|
visibleLayers.sort((a, b) => (a.canvas.zIndex > b.canvas.zIndex) ? 1 : -1);
|
||||||
|
// Selecting the last visible layer (the only one that won't get deleted)
|
||||||
|
visibleLayers[visibleLayers.length - 1].selectLayer();
|
||||||
|
|
||||||
|
// Merging all the layer but the last one
|
||||||
|
for (let i=0; i<visibleLayers.length - 1; i++) {
|
||||||
|
// Copying the above content on the layerBelow
|
||||||
|
let belowImageData = visibleLayers[i].context.getImageData(0, 0, canvas.width, canvas.height);
|
||||||
|
let toMergeImageData = visibleLayers[i + 1].context.getImageData(0, 0, canvas.width, canvas.height);
|
||||||
|
|
||||||
|
for (let i=0; i<belowImageData.data.length; i+=4) {
|
||||||
|
let currentMovePixel = [
|
||||||
|
toMergeImageData.data[i], toMergeImageData.data[i+1],
|
||||||
|
toMergeImageData.data[i+2], toMergeImageData.data[i+3]
|
||||||
|
];
|
||||||
|
|
||||||
|
let currentUnderlyingPixel = [
|
||||||
|
belowImageData.data[i], belowImageData.data[i+1],
|
||||||
|
belowImageData.data[i+2], belowImageData.data[i+3]
|
||||||
|
];
|
||||||
|
|
||||||
|
if (isPixelEmpty(currentMovePixel)) {
|
||||||
|
if (!isPixelEmpty(belowImageData)) {
|
||||||
|
toMergeImageData.data[i] = currentUnderlyingPixel[0];
|
||||||
|
toMergeImageData.data[i+1] = currentUnderlyingPixel[1];
|
||||||
|
toMergeImageData.data[i+2] = currentUnderlyingPixel[2];
|
||||||
|
toMergeImageData.data[i+3] = currentUnderlyingPixel[3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
visibleLayers[i + 1].context.putImageData(toMergeImageData, 0, 0);
|
||||||
|
|
||||||
|
// Deleting the above layer
|
||||||
|
visibleLayers[i].canvas.remove();
|
||||||
|
visibleLayers[i].menuEntry.remove();
|
||||||
|
layers.splice(layers.indexOf(visibleLayers[i]), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Updating the layer preview
|
||||||
|
currentLayer.updateLayerPreview();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function merge(event) {
|
||||||
|
// Saving the layer that should be merged
|
||||||
|
let toMerge = currentLayer;
|
||||||
|
let toMergeIndex = layers.indexOf(toMerge);
|
||||||
|
// Getting layer below
|
||||||
|
let layerBelow = getLayerByID(currentLayer.menuEntry.nextElementSibling.id);
|
||||||
|
|
||||||
|
console.log("YEEEEUUE");
|
||||||
|
console.log(layerBelow);
|
||||||
|
|
||||||
|
// If I have something to merge with
|
||||||
|
if (layerBelow != null) {
|
||||||
|
// Selecting that layer
|
||||||
|
layerBelow.selectLayer();
|
||||||
|
|
||||||
|
// Copying the above content on the layerBelow
|
||||||
|
let belowImageData = currentLayer.context.getImageData(0, 0, canvas.width, canvas.height);
|
||||||
|
let toMergeImageData = toMerge.context.getImageData(0, 0, canvas.width, canvas.height);
|
||||||
|
|
||||||
|
for (let i=0; i<belowImageData.data.length; i+=4) {
|
||||||
|
let currentMovePixel = [
|
||||||
|
toMergeImageData.data[i], toMergeImageData.data[i+1],
|
||||||
|
toMergeImageData.data[i+2], toMergeImageData.data[i+3]
|
||||||
|
];
|
||||||
|
|
||||||
|
let currentUnderlyingPixel = [
|
||||||
|
belowImageData.data[i], belowImageData.data[i+1],
|
||||||
|
belowImageData.data[i+2], belowImageData.data[i+3]
|
||||||
|
];
|
||||||
|
|
||||||
|
if (isPixelEmpty(currentMovePixel)) {
|
||||||
|
if (!isPixelEmpty(belowImageData)) {
|
||||||
|
toMergeImageData.data[i] = currentUnderlyingPixel[0];
|
||||||
|
toMergeImageData.data[i+1] = currentUnderlyingPixel[1];
|
||||||
|
toMergeImageData.data[i+2] = currentUnderlyingPixel[2];
|
||||||
|
toMergeImageData.data[i+3] = currentUnderlyingPixel[3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
currentLayer.context.putImageData(toMergeImageData, 0, 0);
|
||||||
|
|
||||||
|
// Deleting the above layer
|
||||||
|
toMerge.canvas.remove();
|
||||||
|
toMerge.menuEntry.remove();
|
||||||
|
layers.splice(toMergeIndex, 1);
|
||||||
|
|
||||||
|
// Updating the layer preview
|
||||||
|
currentLayer.updateLayerPreview();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
function deleteLayer(event) {
|
function deleteLayer(event) {
|
||||||
// Cannot delete all the layers
|
// Cannot delete all the layers
|
||||||
if (layers.length != 4) {
|
if (layers.length != 4) {
|
||||||
@ -362,19 +481,34 @@ function deleteLayer(event) {
|
|||||||
}
|
}
|
||||||
// or the previous one if the next one doesn't exist
|
// or the previous one if the next one doesn't exist
|
||||||
else {
|
else {
|
||||||
layers[layerIndex - 1].selectLayer();
|
layers[layerIndex - 1].selectLayer();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deleting canvas and entry
|
// Deleting canvas and entry
|
||||||
toDelete.canvas.remove();
|
toDelete.canvas.remove();
|
||||||
toDelete.menuEntry.remove();
|
toDelete.menuEntry.remove();
|
||||||
|
|
||||||
|
// Removing the layer from the list
|
||||||
layers.splice(layerIndex, 1);
|
layers.splice(layerIndex, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Closing the menu
|
||||||
currentLayer.closeOptionsMenu();
|
currentLayer.closeOptionsMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renameLayer(event) {
|
||||||
|
let layerIndex = layers.indexOf(currentLayer);
|
||||||
|
let toRename = currentLayer;
|
||||||
|
let p = currentLayer.menuEntry.getElementsByTagName("p")[0];
|
||||||
|
|
||||||
|
p.setAttribute("contenteditable", true);
|
||||||
|
p.classList.add("layer-name-editable");
|
||||||
|
p.focus();
|
||||||
|
|
||||||
|
simulateInput(65, true, false, false);
|
||||||
|
|
||||||
|
isRenamingLayer = true;
|
||||||
|
}
|
||||||
|
|
||||||
// Swap two layer entries in the layer menu
|
// Swap two layer entries in the layer menu
|
||||||
function swapLayerEntries(id1, id2) {
|
function swapLayerEntries(id1, id2) {
|
||||||
|
@ -68,7 +68,7 @@ window.addEventListener("mouseup", function (mouseEvent) {
|
|||||||
|
|
||||||
closeMenu();
|
closeMenu();
|
||||||
|
|
||||||
if (!isChildOfByClass(mouseEvent.target, "layers-menu-entry")) {
|
if (currentLayer != null && !isChildOfByClass(mouseEvent.target, "layers-menu-entry")) {
|
||||||
currentLayer.closeOptionsMenu();
|
currentLayer.closeOptionsMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,3 +1,22 @@
|
|||||||
|
function simulateInput(keyCode, ctrl, alt, shift) {
|
||||||
|
let keyboardEvent = document.createEvent("KeyboardEvent");
|
||||||
|
let initMethod = typeof keyboardEvent.initKeyboardEvent !== 'undefined' ? "initKeyboardEvent" : "initKeyEvent";
|
||||||
|
|
||||||
|
keyboardEvent[initMethod](
|
||||||
|
"keydown", // event type: keydown, keyup, keypress
|
||||||
|
true, // bubbles
|
||||||
|
true, // cancelable
|
||||||
|
window, // view: should be window
|
||||||
|
ctrl, // ctrlKey
|
||||||
|
alt, // altKey
|
||||||
|
shift, // shiftKey
|
||||||
|
false, // metaKey
|
||||||
|
keyCode, // keyCode: unsigned long - the virtual key code, else 0
|
||||||
|
keyCode // charCode: unsigned long - the Unicode character associated with the depressed key, else 0
|
||||||
|
);
|
||||||
|
document.dispatchEvent(keyboardEvent);
|
||||||
|
}
|
||||||
|
|
||||||
function isPixelEmpty(pixel) {
|
function isPixelEmpty(pixel) {
|
||||||
if (pixel == null || pixel === undefined) {
|
if (pixel == null || pixel === undefined) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -156,10 +156,19 @@
|
|||||||
|
|
||||||
<ul id = "layer-properties-menu">
|
<ul id = "layer-properties-menu">
|
||||||
<li>
|
<li>
|
||||||
<button id = "delete-layer" onclick = "deleteLayer()">Delete</button>
|
<button onclick = "deleteLayer()">Delete</button>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<button id = "rename-layer">Rename</button>
|
<button onclick = "renameLayer()">Rename</button>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<button onclick = "merge()">Merge below</button>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<button onclick = "flatten(true)">Flatten visible</button>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<button onclick = "flatten(false)">Flatten all</button>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</ul>
|
</ul>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user