mirror of
https://github.com/lospec/pixel-editor.git
synced 2023-08-10 21:12:51 +03:00
Reorganized file tree, made Checkerboard a child of Layer
This commit is contained in:
81
js/layers/Checkerboard.js
Normal file
81
js/layers/Checkerboard.js
Normal file
@ -0,0 +1,81 @@
|
||||
class Checkerboard extends Layer {
|
||||
// Checkerboard color 1
|
||||
firstCheckerBoardColor = 'rgba(179, 173, 182, 1)';
|
||||
// Checkerboard color 2
|
||||
secondCheckerBoardColor = 'rgba(204, 200, 206, 1)';
|
||||
// Square size for the checkerboard
|
||||
checkerBoardSquareSize = 16;
|
||||
|
||||
// Setting current colour (each square has a different colour
|
||||
currentColor = undefined;
|
||||
// Saving number of squares filled until now
|
||||
nSquaresFilled = 0;
|
||||
|
||||
constructor(width, height, canvas, menuEntry) {
|
||||
super(width, height, document.getElementById('checkerboard'), menuEntry);
|
||||
this.initialize();
|
||||
}
|
||||
|
||||
initialize() {
|
||||
super.initialize();
|
||||
console.log("Square size: " + this.checkerBoardSquareSize);
|
||||
this.currentColor = this.firstCheckerBoardColor;
|
||||
this.fillCheckerboard();
|
||||
}
|
||||
|
||||
/** Fills the checkerboard canvas with squares with alternating colours
|
||||
*
|
||||
*/
|
||||
fillCheckerboard() {
|
||||
this.context.clearRect(0, 0, this.canvasSize[0], this.canvasSize[1]);
|
||||
|
||||
// Cycling through the canvas (using it as a matrix)
|
||||
for (let i=0; i<this.canvasSize[0] / this.checkerBoardSquareSize; i++) {
|
||||
this.nSquaresFilled = 0;
|
||||
|
||||
for (let j=0; j<this.canvasSize[1] / this.checkerBoardSquareSize; j++) {
|
||||
let rectX;
|
||||
let rectY;
|
||||
|
||||
// Managing the not perfect squares (the ones at the sides if the canvas' sizes are not powers of checkerBoardSquareSize
|
||||
if (i * this.checkerBoardSquareSize < this.canvasSize[0]) {
|
||||
rectX = i * this.checkerBoardSquareSize;
|
||||
}
|
||||
else {
|
||||
rectX = this.canvasSize[0];
|
||||
}
|
||||
|
||||
if (j * this.checkerBoardSquareSize < this.canvasSize[1]) {
|
||||
rectY = j * this.checkerBoardSquareSize;
|
||||
}
|
||||
else {
|
||||
rectY = this.canvasSize[1];
|
||||
}
|
||||
|
||||
// Selecting the colour
|
||||
this.context.fillStyle = this.currentColor;
|
||||
this.context.fillRect(rectX, rectY, this.checkerBoardSquareSize, this.checkerBoardSquareSize);
|
||||
|
||||
// Changing colour
|
||||
this.changeCheckerboardColor();
|
||||
this.nSquaresFilled++;
|
||||
}
|
||||
|
||||
// If the number of filled squares was even, I change colour for the next column
|
||||
if ((this.nSquaresFilled % 2) == 0) {
|
||||
this.changeCheckerboardColor();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Simply switches the checkerboard colour
|
||||
changeCheckerboardColor() {
|
||||
if (this.currentColor == this.firstCheckerBoardColor) {
|
||||
this.currentColor = this.secondCheckerBoardColor;
|
||||
} else if (this.currentColor == this.secondCheckerBoardColor) {
|
||||
this.currentColor = this.firstCheckerBoardColor;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
281
js/layers/Layer.js
Normal file
281
js/layers/Layer.js
Normal file
@ -0,0 +1,281 @@
|
||||
// TODO: add undo for layer drag n drop
|
||||
|
||||
/** Handler class for a single canvas (a single layer)
|
||||
*
|
||||
* @param width Canvas width
|
||||
* @param height Canvas height
|
||||
* @param canvas HTML canvas element or the ID of the canvas related to the layer
|
||||
*/
|
||||
class Layer {
|
||||
static layerCount = 1;
|
||||
static maxZIndex = 3;
|
||||
|
||||
static unusedIDs = [];
|
||||
static currentID = 1;
|
||||
|
||||
static layerOptions = document.getElementById("layer-properties-menu");
|
||||
|
||||
// TODO: this is simply terrible. menuEntry can either be an HTML element, a string or undefined.
|
||||
// If it's an HTML element it is added, if it's a string nothing happens, if it's undefined the
|
||||
// first menuEntry is used (the one that appears on load)
|
||||
constructor(width, height, canvas, menuEntry) {
|
||||
// REFACTOR: this could just be an attribute of a Canvas class
|
||||
this.canvasSize = [width, height];
|
||||
// REFACTOR: the canvas should actually be a Canvas instance
|
||||
this.canvas = Util.getElement(canvas);
|
||||
// REFACTOR: the context could be an attribute of the canvas class, but it's a bit easier
|
||||
// to just type layer.context, we should discuss this
|
||||
this.context = this.canvas.getContext('2d');
|
||||
this.isSelected = false;
|
||||
this.isVisible = true;
|
||||
this.isLocked = false;
|
||||
|
||||
this.oldLayerName = null;
|
||||
|
||||
if (typeof menuEntry == "string")
|
||||
this.menuEntry = document.getElementById("layers-menu").firstElementChild;
|
||||
else if (menuEntry !== undefined)
|
||||
this.menuEntry = menuEntry;
|
||||
|
||||
let id = Layer.unusedIDs.pop();
|
||||
|
||||
if (id == null) {
|
||||
id = Layer.currentID;
|
||||
Layer.currentID++;
|
||||
}
|
||||
|
||||
this.id = "layer" + id;
|
||||
|
||||
// Binding the events
|
||||
if (this.menuEntry !== undefined) {
|
||||
this.name = this.menuEntry.getElementsByTagName("p")[0].innerHTML;
|
||||
this.menuEntry.id = "layer" + id;
|
||||
this.menuEntry.onmouseover = () => this.hover();
|
||||
this.menuEntry.onmouseout = () => this.unhover();
|
||||
this.menuEntry.onclick = () => this.selectLayer();
|
||||
this.menuEntry.getElementsByTagName("button")[0].onclick = () => this.toggleLock();
|
||||
this.menuEntry.getElementsByTagName("button")[1].onclick = () => this.toggleVisibility();
|
||||
|
||||
this.menuEntry.addEventListener("mouseup", this.openOptionsMenu, false);
|
||||
this.menuEntry.addEventListener("dragstart", this.layerDragStart, false);
|
||||
this.menuEntry.addEventListener("drop", this.layerDragDrop, false);
|
||||
this.menuEntry.addEventListener("dragover", this.layerDragOver, false);
|
||||
this.menuEntry.addEventListener("dragleave", this.layerDragLeave, false);
|
||||
this.menuEntry.addEventListener("dragend", this.layerDragEnd, false);
|
||||
|
||||
this.menuEntry.getElementsByTagName("canvas")[0].getContext('2d').imageSmoothingEnabled = false;
|
||||
}
|
||||
|
||||
this.initialize();
|
||||
}
|
||||
|
||||
// Initializes the canvas
|
||||
initialize() {
|
||||
//resize canvas
|
||||
this.canvas.width = this.canvasSize[0];
|
||||
this.canvas.height = this.canvasSize[1];
|
||||
this.canvas.style.width = (this.canvas.width*zoom)+'px';
|
||||
this.canvas.style.height = (this.canvas.height*zoom)+'px';
|
||||
|
||||
//show canvas
|
||||
this.canvas.style.display = 'block';
|
||||
|
||||
//center canvas in window
|
||||
this.canvas.style.left = 64+canvasView.clientWidth/2-(this.canvasSize[0]*zoom/2)+'px';
|
||||
this.canvas.style.top = 48+canvasView.clientHeight/2-(this.canvasSize[1]*zoom/2)+'px';
|
||||
|
||||
this.context.imageSmoothingEnabled = false;
|
||||
this.context.mozImageSmoothingEnabled = false;
|
||||
}
|
||||
|
||||
rename() {
|
||||
this.menuEntry.getElementsByTagName("p")[0].setAttribute("contenteditable", false);
|
||||
|
||||
if (this.oldLayerName != null) {
|
||||
let name = this.menuEntry.getElementsByTagName("p")[0].innerHTML;
|
||||
|
||||
for (let i=0; i<layers.length; i++) {
|
||||
if (name === layers[i].name) {
|
||||
name += ' (1)';
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
this.name = name;
|
||||
this.menuEntry.getElementsByTagName("p")[0].innerHTML = name;
|
||||
|
||||
new HistoryState().RenameLayer(this.oldLayerName, name, currentLayer);
|
||||
this.oldLayerName = null;
|
||||
}
|
||||
}
|
||||
|
||||
hover() {
|
||||
// Hides all the layers but the current one
|
||||
for (let i=1; i<layers.length - nAppLayers; i++) {
|
||||
if (layers[i] !== this) {
|
||||
layers[i].canvas.style.opacity = 0.3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unhover() {
|
||||
// Shows all the layers again
|
||||
for (let i=1; i<layers.length - nAppLayers; i++) {
|
||||
if (layers[i] !== this) {
|
||||
layers[i].canvas.style.opacity = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setID(id) {
|
||||
this.id = id;
|
||||
if (this.menuEntry != null) {
|
||||
this.menuEntry.id = id;
|
||||
}
|
||||
}
|
||||
|
||||
// Resizes canvas
|
||||
resize() {
|
||||
let newWidth = (this.canvas.width * zoom) + 'px';
|
||||
let newHeight = (this.canvas.height *zoom)+ 'px';
|
||||
|
||||
this.canvas.style.width = newWidth;
|
||||
this.canvas.style.height = newHeight;
|
||||
}
|
||||
|
||||
setCanvasOffset (offsetLeft, offsetTop) {
|
||||
//horizontal offset
|
||||
var minXOffset = -this.canvasSize[0] * zoom;
|
||||
var maxXOffset = window.innerWidth - 300;
|
||||
|
||||
if (offsetLeft < minXOffset)
|
||||
this.canvas.style.left = minXOffset +'px';
|
||||
else if (offsetLeft > maxXOffset)
|
||||
this.canvas.style.left = maxXOffset +'px';
|
||||
else
|
||||
this.canvas.style.left = offsetLeft +'px';
|
||||
|
||||
//vertical offset
|
||||
var minYOffset = -this.canvasSize[1] * zoom + 164;
|
||||
var maxYOffset = window.innerHeight - 100;
|
||||
|
||||
if (offsetTop < minYOffset)
|
||||
this.canvas.style.top = minYOffset +'px';
|
||||
else if (offsetTop > maxYOffset)
|
||||
this.canvas.style.top = maxYOffset +'px';
|
||||
else
|
||||
this.canvas.style.top = offsetTop +'px';
|
||||
}
|
||||
|
||||
// Copies the otherLayer's position and size
|
||||
copyData(otherLayer) {
|
||||
this.canvas.style.width = otherLayer.canvas.style.width;
|
||||
this.canvas.style.height = otherLayer.canvas.style.height;
|
||||
|
||||
this.canvas.style.left = otherLayer.canvas.style.left;
|
||||
this.canvas.style.top = otherLayer.canvas.style.top;
|
||||
}
|
||||
|
||||
selectLayer(layer) {
|
||||
if (layer == null) {
|
||||
// Deselecting the old layer
|
||||
currentLayer.deselectLayer();
|
||||
|
||||
// Selecting the current layer
|
||||
this.isSelected = true;
|
||||
this.menuEntry.classList.add("selected-layer");
|
||||
currentLayer = LayerList.getLayerByName(this.menuEntry.getElementsByTagName("p")[0].innerHTML);
|
||||
}
|
||||
else {
|
||||
currentLayer.deselectLayer();
|
||||
|
||||
layer.isSelected = true;
|
||||
layer.menuEntry.classList.add("selected-layer");
|
||||
currentLayer = layer;
|
||||
}
|
||||
}
|
||||
|
||||
toggleLock() {
|
||||
if (this.isLocked) {
|
||||
this.unlock();
|
||||
}
|
||||
else {
|
||||
this.lock();
|
||||
}
|
||||
}
|
||||
|
||||
toggleVisibility() {
|
||||
if (this.isVisible) {
|
||||
this.hide();
|
||||
}
|
||||
else {
|
||||
this.show();
|
||||
}
|
||||
}
|
||||
|
||||
deselectLayer() {
|
||||
this.isSelected = false;
|
||||
this.menuEntry.classList.remove("selected-layer");
|
||||
}
|
||||
|
||||
lock() {
|
||||
this.isLocked = true;
|
||||
this.menuEntry.getElementsByClassName("layer-button")[0].style.visibility = "visible";
|
||||
|
||||
this.menuEntry.getElementsByClassName("default-icon")[0].style.display = "none";
|
||||
this.menuEntry.getElementsByClassName("edited-icon")[0].style.display = "inline-block";
|
||||
}
|
||||
|
||||
unlock() {
|
||||
this.isLocked = false;
|
||||
this.menuEntry.getElementsByClassName("layer-button")[0].style.visibility = "hidden";
|
||||
|
||||
this.menuEntry.getElementsByClassName("default-icon")[0].style.display = "inline-block";
|
||||
this.menuEntry.getElementsByClassName("edited-icon")[0].style.display = "none";
|
||||
}
|
||||
|
||||
show() {
|
||||
this.isVisible = true;
|
||||
this.canvas.style.visibility = "visible";
|
||||
this.menuEntry.getElementsByClassName("layer-button")[1].style.visibility = "hidden";
|
||||
|
||||
// Changing icon
|
||||
this.menuEntry.getElementsByClassName("default-icon")[1].style.display = "inline-block";
|
||||
this.menuEntry.getElementsByClassName("edited-icon")[1].style.display = "none";
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.isVisible = false;
|
||||
this.canvas.style.visibility = "hidden";
|
||||
this.menuEntry.getElementsByClassName("layer-button")[1].style.visibility = "visible";
|
||||
|
||||
// Changing icon
|
||||
this.menuEntry.getElementsByClassName("default-icon")[1].style.display = "none";
|
||||
this.menuEntry.getElementsByClassName("edited-icon")[1].style.display = "inline-block";
|
||||
}
|
||||
|
||||
updateLayerPreview() {
|
||||
// Getting the canvas
|
||||
let destination = this.menuEntry.getElementsByTagName("canvas")[0];
|
||||
let widthRatio = this.canvasSize[0] / this.canvasSize[1];
|
||||
let heightRatio = this.canvasSize[1] / this.canvasSize[0];
|
||||
|
||||
// Computing width and height for the preview image
|
||||
let previewWidth = destination.width;
|
||||
let previewHeight = destination.height;
|
||||
|
||||
// If the sprite is rectangular, I apply the ratio to the preview as well
|
||||
if (widthRatio < 1) {
|
||||
previewWidth = destination.width * widthRatio;
|
||||
}
|
||||
else if (widthRatio > 1) {
|
||||
previewHeight = destination.height * heightRatio;
|
||||
}
|
||||
|
||||
// La appiccico sulla preview
|
||||
destination.getContext('2d').clearRect(0, 0, destination.width, destination.height);
|
||||
destination.getContext('2d').drawImage(this.canvas,
|
||||
// This is necessary to center the preview in the canvas
|
||||
(destination.width - previewWidth) / 2, (destination.height - previewHeight) / 2,
|
||||
previewWidth, previewHeight);
|
||||
}
|
||||
}
|
0
js/layers/PixelGrid.js
Normal file
0
js/layers/PixelGrid.js
Normal file
Reference in New Issue
Block a user