Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dynamic canvas scaling? #108

Merged
merged 10 commits into from
Dec 18, 2022
69 changes: 69 additions & 0 deletions css/ui/layers.css
Original file line number Diff line number Diff line change
Expand Up @@ -132,3 +132,72 @@
flex: 1;
height: 25px;
}

/* Resizing buttons */
.expand-button {
display: flex;

align-items: center;
justify-content: center;

margin: 0;
padding: 0;
border: 0;

background-color: transparent;

cursor: pointer;

transition-duration: 300ms;

border: 2px solid #293d3d30;
}

.expand-button::after {
content: "";

background-color: #293d3d77;

mask-image: url("/res/icons/chevron-up.svg");
mask-size: contain;

width: 60px;
height: 60px;
}

.expand-button:hover::after {
background-color: #466;
}

.expand-button.right::after {
transform: rotate(90deg);
}

.expand-button.bottom::after {
transform: rotate(180deg);
}

.expand-button.left::after {
transform: rotate(270deg);
}

.expand-button.left {
border-top-left-radius: 10px;
border-bottom-left-radius: 10px;
}
.expand-button.top {
border-top-left-radius: 10px;
border-top-right-radius: 10px;
}
.expand-button.right {
border-top-right-radius: 10px;
border-bottom-right-radius: 10px;
}
.expand-button.bottom {
border-bottom-right-radius: 10px;
border-bottom-left-radius: 10px;
}

.expand-button:hover {
background-color: #293d3d77;
}
19 changes: 12 additions & 7 deletions js/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -343,17 +343,14 @@ function newImage(evt) {
clearPaintedMask();
uil.layers.forEach(({layer}) => {
commands.runCommand("eraseImage", "Clear Canvas", {
x: 0,
y: 0,
w: layer.canvas.width,
h: layer.canvas.height,
...layer.bb,
ctx: layer.ctx,
});
});
}

function clearPaintedMask() {
maskPaintCtx.clearRect(0, 0, maskPaintCanvas.width, maskPaintCanvas.height);
maskPaintLayer.clear();
}

function march(bb, options = {}) {
Expand Down Expand Up @@ -558,8 +555,16 @@ function drawBackground() {
// Checkerboard
let darkTileColor = "#333";
let lightTileColor = "#555";
for (var x = 0; x < bgLayer.canvas.width; x += 64) {
for (var y = 0; y < bgLayer.canvas.height; y += 64) {
for (
var x = -bgLayer.origin.x - 64;
x < bgLayer.canvas.width - bgLayer.origin.x;
x += 64
) {
for (
var y = -bgLayer.origin.y - 64;
y < bgLayer.canvas.height - bgLayer.origin.y;
y += 64
) {
bgLayer.ctx.fillStyle =
(x + y) % 128 === 0 ? lightTileColor : darkTileColor;
bgLayer.ctx.fillRect(x, y, 64, 64);
Expand Down
13 changes: 13 additions & 0 deletions js/initalize/debug.populate.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,19 @@ mouse.listen.world.onmousemove.on((evn) => {
canvasYInfo.textContent = evn.y;
snapXInfo.textContent = evn.x + snap(evn.x);
snapYInfo.textContent = evn.y + snap(evn.y);

if (debug) {
debugLayer.clear();
debugCtx.fillStyle = "#F0F";
debugCtx.beginPath();
debugCtx.arc(viewport.cx, viewport.cy, 5, 0, Math.PI * 2);
debugCtx.fill();

debugCtx.fillStyle = "#0FF";
debugCtx.beginPath();
debugCtx.arc(evn.x, evn.y, 5, 0, Math.PI * 2);
debugCtx.fill();
}
});

/**
Expand Down
164 changes: 120 additions & 44 deletions js/initalize/layers.populate.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,44 +53,97 @@ uiCanvas.width = uiCanvas.clientWidth;
uiCanvas.height = uiCanvas.clientHeight;
const uiCtx = uiCanvas.getContext("2d", {desynchronized: true});

debugLayer.hide(); // Hidden by default

layers.registerCollection("mask", {name: "Mask Layers", requiresActive: true});

// Where CSS and javascript magic happens to make the canvas viewport work
/**
* Ended up using a CSS transforms approach due to more flexibility on transformations
* and capability to automagically translate input coordinates to layer space.
* Here we setup canvas dynamic scaling
*/
mouse.registerContext(
"world",
(evn, ctx) => {
// Fix because in chrome layerX and layerY simply doesnt work
ctx.coords.prev.x = ctx.coords.pos.x;
ctx.coords.prev.y = ctx.coords.pos.y;

// Get element bounding rect
const bb = imageCollection.element.getBoundingClientRect();

// Get element width/height (css, cause I don't trust client sizes in chrome anymore)
const w = imageCollection.size.w;
const h = imageCollection.size.h;

// Get cursor position
const x = evn.clientX;
const y = evn.clientY;

// Map to layer space
const layerX = ((x - bb.left) / bb.width) * w;
const layerY = ((y - bb.top) / bb.height) * h;
(() => {
let expandSize = localStorage.getItem("expand-size") || 1024;
expandSize = parseInt(expandSize, 10);

const askSize = () => {
const by = prompt("How much do you want to expand by?", expandSize);

if (!by) return null;
else {
const len = parseInt(by, 10);
localStorage.setItem("expand-size", len);
expandSize = len;
return len;
}
};

const leftButton = makeElement("button", -64, 0);
leftButton.classList.add("expand-button", "left");
leftButton.style.width = "64px";
leftButton.style.height = `${imageCollection.size.h}px`;
leftButton.addEventListener("click", () => {
let size = null;
if ((size = askSize())) {
imageCollection.expand(size, 0, 0, 0);
drawBackground();
const newLeft = -imageCollection.inputOffset.x - imageCollection.origin.x;
leftButton.style.left = newLeft - 64 + "px";
topButton.style.left = newLeft + "px";
bottomButton.style.left = newLeft + "px";
topButton.style.width = imageCollection.size.w + "px";
bottomButton.style.width = imageCollection.size.w + "px";
}
});

const rightButton = makeElement("button", imageCollection.size.w, 0);
rightButton.classList.add("expand-button", "right");
rightButton.style.width = "64px";
rightButton.style.height = `${imageCollection.size.h}px`;
rightButton.addEventListener("click", () => {
let size = null;
if ((size = askSize())) {
imageCollection.expand(0, 0, size, 0);
drawBackground();
rightButton.style.left =
parseInt(rightButton.style.left, 10) + size + "px";
topButton.style.width = imageCollection.size.w + "px";
bottomButton.style.width = imageCollection.size.w + "px";
}
});

const topButton = makeElement("button", 0, -64);
topButton.classList.add("expand-button", "top");
topButton.style.height = "64px";
topButton.style.width = `${imageCollection.size.w}px`;
topButton.addEventListener("click", () => {
let size = null;
if ((size = askSize())) {
imageCollection.expand(0, size, 0, 0);
drawBackground();
const newTop = -imageCollection.inputOffset.y - imageCollection.origin.y;
topButton.style.top = newTop - 64 + "px";
leftButton.style.top = newTop + "px";
rightButton.style.top = newTop + "px";
leftButton.style.height = imageCollection.size.h + "px";
rightButton.style.height = imageCollection.size.h + "px";
}
});

const bottomButton = makeElement("button", 0, imageCollection.size.h);
bottomButton.classList.add("expand-button", "bottom");
bottomButton.style.height = "64px";
bottomButton.style.width = `${imageCollection.size.w}px`;
bottomButton.addEventListener("click", () => {
let size = null;
if ((size = askSize())) {
imageCollection.expand(0, 0, 0, size);
drawBackground();
bottomButton.style.top =
parseInt(bottomButton.style.top, 10) + size + "px";
leftButton.style.height = imageCollection.size.h + "px";
rightButton.style.height = imageCollection.size.h + "px";
}
});
})();

//
ctx.coords.pos.x = Math.round(layerX);
ctx.coords.pos.y = Math.round(layerY);
},
{target: imageCollection.inputElement}
);
debugLayer.hide(); // Hidden by default

// Where CSS and javascript magic happens to make the canvas viewport work
/**
* The global viewport object (may be modularized in the future). All
* coordinates given are of the center of the viewport
Expand Down Expand Up @@ -158,6 +211,31 @@ let worldInit = null;

viewport.transform(imageCollection.element);

/**
* Ended up using a CSS transforms approach due to more flexibility on transformations
* and capability to automagically translate input coordinates to layer space.
*/
mouse.registerContext(
"world",
(evn, ctx) => {
// Fix because in chrome layerX and layerY simply doesnt work
ctx.coords.prev.x = ctx.coords.pos.x;
ctx.coords.prev.y = ctx.coords.pos.y;

// Get cursor position
const x = evn.clientX;
const y = evn.clientY;

// Map to layer space
const layerCoords = viewport.viewToCanvas(x, y);

// Set coords
ctx.coords.pos.x = Math.round(layerCoords.x);
ctx.coords.pos.y = Math.round(layerCoords.y);
},
{target: imageCollection.inputElement}
);

mouse.listen.window.onwheel.on((evn) => {
if (evn.evn.ctrlKey) {
evn.evn.preventDefault();
Expand All @@ -176,14 +254,6 @@ mouse.listen.window.onwheel.on((evn) => {
viewport.transform(imageCollection.element);

toolbar.currentTool.redraw();

if (debug) {
debugCtx.clearRect(0, 0, debugCanvas.width, debugCanvas.height);
debugCtx.fillStyle = "#F0F";
debugCtx.beginPath();
debugCtx.arc(viewport.cx, viewport.cy, 5, 0, Math.PI * 2);
debugCtx.fill();
}
}
});

Expand All @@ -197,8 +267,14 @@ mouse.listen.window.btn.middle.onpaint.on((evn) => {
viewport.cy = worldInit.y + (evn.iy - evn.y) / viewport.zoom;

// Limits
viewport.cx = Math.max(Math.min(viewport.cx, imageCollection.size.w), 0);
viewport.cy = Math.max(Math.min(viewport.cy, imageCollection.size.h), 0);
viewport.cx = Math.max(
Math.min(viewport.cx, imageCollection.size.w - imageCollection.origin.x),
-imageCollection.origin.x
);
viewport.cy = Math.max(
Math.min(viewport.cy, imageCollection.size.h - imageCollection.origin.y),
-imageCollection.origin.y
);

// Draw Viewport location
}
Expand Down
47 changes: 47 additions & 0 deletions js/lib/layers.d.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/**
* A layer
*
* @typedef {object} Layer
* @property {string} id The id of the layer
* @property {string} key A identifier for the layer
* @property {string} name The display name of the layer
* @property {BoundingBox} bb The current bounding box of the layer, in layer coordinates
* @property {Size} resolution The resolution of the layer (canvas)
* @property {boolean} full If the layer is a full layer (occupies the full collection)
* @property {number} x The x coordinate of the layer
* @property {number} y The y coordinate of the layer
* @property {number} width The width of the layer
* @property {number} w The width of the layer
* @property {number} height The height of the layer
* @property {number} h The height of the layer
* @property {Point} origin The location of the origin ((0, 0) point) of the layer (If canvas goes from -64, -32 to 128, 512, it's (64, 32))
* @property {HTMLCanvasElement} canvas The canvas element of the layers
* @property {CanvasRenderingContext2D} ctx The context of the canvas of the layer
* @property {function} clear Clears the layer contents
* @property {function} moveAfter Moves this layer to another level (after given layer)
* @property {function} moveBefore Moves this layer to another level (before given layer)
* @property {function} moveTo Moves this layer to another location
* @property {function} resize Resizes the layer in place
* @property {function} hide Hides the layer
* @property {function} unhide Unhides the layer
*/

/**
* A layer collection
*
* @typedef {object} LayerCollection
* @property {string} id The id of the collection
* @property {string} key A identifier for the collection
* @property {string} name The display name of the collection
* @property {HTMLDivElement} element The base element of the collection
* @property {HTMLDivElement} inputElement The element used for input handling for the collection
* @property {Point} inputOffset The offset for calculating layer coordinates from input element input information
* @property {Point} origin The location of the origin ((0, 0) point) of the collection (If canvas goes from -64, -32 to 128, 512, it's (64, 32))
* @property {BoundingBox} bb The current bounding box of the collection, in layer coordinates
* @property {{[key: string]: Layer}} layers An object for quick access to named layers of the collection
* @property {Size} size The size of the collection (CSS)
* @property {Size} resolution The resolution of the collection (canvas)
* @property {function} expand Expands the collection and its full layers by the specified amounts
* @property {function} registerLayer Registers a new layer
* @property {function} deleteLayer Deletes a layer from the collection
*/
Loading