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 {
|
||||
background-color:color(menu, background);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.preview-canvas {
|
||||
|
@ -1,27 +1,15 @@
|
||||
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) {
|
||||
var keyboardEvent = window.event? event : e;
|
||||
|
||||
//if the user is typing in an input field, ignore these hotkeys
|
||||
if (document.activeElement.tagName == 'INPUT') return;
|
||||
//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' || isRenamingLayer) {
|
||||
if (e.keyCode == 13) {
|
||||
currentLayer.closeOptionsMenu();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
//if no document has been created yet,
|
||||
//orthere is a dialog box open
|
||||
|
148
js/_layer.js
148
js/_layer.js
@ -1,13 +1,6 @@
|
||||
/** TODO LIST FOR LAYERS
|
||||
|
||||
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
|
||||
- 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
|
||||
@ -38,6 +31,9 @@
|
||||
|
||||
- Resize canvas (must make a simple editor to preview the changes)
|
||||
- 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
|
||||
@ -58,6 +54,8 @@ let currentID = layerCount;
|
||||
let idToDelete;
|
||||
let layerOptions = document.getElementById("layer-properties-menu");
|
||||
|
||||
let isRenamingLayer = false;
|
||||
|
||||
on('click',"add-layer-button", function(){
|
||||
// Creating a new canvas
|
||||
let newCanvas = document.createElement("canvas");
|
||||
@ -241,6 +239,8 @@ class Layer {
|
||||
|
||||
closeOptionsMenu(event) {
|
||||
layerOptions.style.visibility = "hidden";
|
||||
currentLayer.menuEntry.getElementsByTagName("p")[0].setAttribute("contenteditable", false);
|
||||
isRenamingLayer = false;
|
||||
}
|
||||
|
||||
selectLayer(layer) {
|
||||
@ -260,6 +260,9 @@ class Layer {
|
||||
layer.menuEntry.classList.add("selected-layer");
|
||||
currentLayer = layer;
|
||||
}
|
||||
|
||||
canvas = currentLayer.canvas;
|
||||
context = currentLayer.context;
|
||||
}
|
||||
|
||||
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) {
|
||||
// Cannot delete all the layers
|
||||
if (layers.length != 4) {
|
||||
@ -369,12 +488,27 @@ function deleteLayer(event) {
|
||||
toDelete.canvas.remove();
|
||||
toDelete.menuEntry.remove();
|
||||
|
||||
// Removing the layer from the list
|
||||
layers.splice(layerIndex, 1);
|
||||
}
|
||||
|
||||
// Closing the menu
|
||||
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
|
||||
function swapLayerEntries(id1, id2) {
|
||||
|
@ -68,7 +68,7 @@ window.addEventListener("mouseup", function (mouseEvent) {
|
||||
|
||||
closeMenu();
|
||||
|
||||
if (!isChildOfByClass(mouseEvent.target, "layers-menu-entry")) {
|
||||
if (currentLayer != null && !isChildOfByClass(mouseEvent.target, "layers-menu-entry")) {
|
||||
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) {
|
||||
if (pixel == null || pixel === undefined) {
|
||||
return false;
|
||||
|
@ -156,10 +156,19 @@
|
||||
|
||||
<ul id = "layer-properties-menu">
|
||||
<li>
|
||||
<button id = "delete-layer" onclick = "deleteLayer()">Delete</button>
|
||||
<button onclick = "deleteLayer()">Delete</button>
|
||||
</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>
|
||||
</ul>
|
||||
</ul>
|
||||
|
Loading…
Reference in New Issue
Block a user