Now working scaling and transformation functionality using canvas drawing
This commit is contained in:
233
minesweeper.js
233
minesweeper.js
@@ -1,7 +1,7 @@
|
|||||||
const canvas = document.getElementById('minesweeper-game');
|
const canvas = document.getElementById('minesweeper-game');
|
||||||
const ctx = canvas.getContext('2d');
|
const ctx = canvas.getContext('2d');
|
||||||
|
|
||||||
const fieldSize = {x: 15, y: 10};
|
const fieldSize = {x: 16, y: 12};
|
||||||
let tileSize;
|
let tileSize;
|
||||||
const bombCount = 25;
|
const bombCount = 25;
|
||||||
const field = [];
|
const field = [];
|
||||||
@@ -10,6 +10,12 @@ let victory = false;
|
|||||||
const scaleFactor = .5;
|
const scaleFactor = .5;
|
||||||
let isFirstClick = true;
|
let isFirstClick = true;
|
||||||
|
|
||||||
|
let windowX = 0;
|
||||||
|
let windowY = 0;
|
||||||
|
let zoomFactor = 1;
|
||||||
|
|
||||||
|
let renderingConfig;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines all possible colors for the tile numbers
|
* Defines all possible colors for the tile numbers
|
||||||
* @type {{ 1: string, 2: string, 3: string, 4: string, 6: string }}
|
* @type {{ 1: string, 2: string, 3: string, 4: string, 6: string }}
|
||||||
@@ -33,8 +39,6 @@ function animateBackground(x, y, width, height, curOpacity, finalOpacity, startT
|
|||||||
|
|
||||||
const newOpacity = easeInOutCubic(time, 0, finalOpacity, duration);
|
const newOpacity = easeInOutCubic(time, 0, finalOpacity, duration);
|
||||||
|
|
||||||
console.log(newOpacity, finalOpacity, color);
|
|
||||||
|
|
||||||
if (newOpacity <= finalOpacity)
|
if (newOpacity <= finalOpacity)
|
||||||
curOpacity = newOpacity;
|
curOpacity = newOpacity;
|
||||||
else
|
else
|
||||||
@@ -76,7 +80,7 @@ function animateTile(x, y, curWidth, curHeight, finalWidth, finalHeight, curRadi
|
|||||||
else
|
else
|
||||||
curRadius = finalRadius;
|
curRadius = finalRadius;
|
||||||
|
|
||||||
drawRoundedRect(ctx, x + 1, y + 1, finalWidth - 2, finalHeight - 2, finalRadius, "#2e8cdd");
|
// drawRoundedRect(ctx, x + 1, y + 1, finalWidth - 2, finalHeight - 2, finalRadius, getColor(x, y));
|
||||||
|
|
||||||
drawRoundedRect(ctx, x + (finalWidth - curWidth) / 2, y + (finalHeight - curHeight) / 2, curWidth, curHeight, curRadius, color);
|
drawRoundedRect(ctx, x + (finalWidth - curWidth) / 2, y + (finalHeight - curHeight) / 2, curWidth, curHeight, curRadius, color);
|
||||||
|
|
||||||
@@ -102,19 +106,46 @@ function animateText(text, x, y, curFontSize, finalFontSize, startTime, duration
|
|||||||
curFontSize = finalFontSize;
|
curFontSize = finalFontSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const textDrawX = (x + .5 - renderingConfig.tiltX) * renderingConfig.sizeX;
|
||||||
|
const textDrawY = (y + .5 - renderingConfig.tiltY) * renderingConfig.sizeY + curFontSize * .33;
|
||||||
|
|
||||||
if (font === undefined)
|
if (font === undefined)
|
||||||
font = "Roboto";
|
font = "Roboto";
|
||||||
|
|
||||||
context.fillStyle = color;
|
context.fillStyle = color;
|
||||||
context.font = "bold " + curFontSize + "px " + font;
|
context.font = "bold " + curFontSize + "px " + font;
|
||||||
context.textAlign = "center";
|
context.textAlign = "center";
|
||||||
context.fillText(text, x, y + tileSize.y * .5 + curFontSize * .33);
|
context.fillText(text, textDrawX, textDrawY);
|
||||||
|
|
||||||
requestAnimFrame(function () {
|
requestAnimFrame(function () {
|
||||||
animateText(text, x, y, curFontSize, finalFontSize, startTime, duration, color, font, context);
|
animateText(text, x, y, curFontSize, finalFontSize, startTime, duration, color, font, context);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function applyScaling() {
|
||||||
|
renderingConfig = calcScaling();
|
||||||
|
|
||||||
|
drawGrid(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function calcScaling(field = fieldSize, tile = tileSize, zoom = zoomFactor) {
|
||||||
|
const width = Math.ceil(field.x * zoom);
|
||||||
|
const height = Math.ceil(field.y * zoom);
|
||||||
|
|
||||||
|
const offsetX = Math.floor(windowX * field.x);
|
||||||
|
const offsetY = Math.floor(windowY * field.y);
|
||||||
|
|
||||||
|
const tiltX = windowX * field.x - offsetX;
|
||||||
|
const tiltY = windowY * field.y - offsetY;
|
||||||
|
|
||||||
|
const sizeX = tile.x / zoom;
|
||||||
|
const sizeY = tile.y / zoom;
|
||||||
|
|
||||||
|
return {
|
||||||
|
width, height, offsetX, offsetY, tiltX, tiltY, sizeX, sizeY
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function countClickedTiles() {
|
function countClickedTiles() {
|
||||||
let count = 0;
|
let count = 0;
|
||||||
for (let x = 0; x < fieldSize.x; x++) {
|
for (let x = 0; x < fieldSize.x; x++) {
|
||||||
@@ -138,8 +169,9 @@ function countFlaggedBombs(x, y) {
|
|||||||
|
|
||||||
function drawGrid(animations = true) {
|
function drawGrid(animations = true) {
|
||||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
for (let x = 0; x < fieldSize.x; x++) {
|
|
||||||
for (let y = 0; y < fieldSize.y; y++) {
|
for (let x = 0; x < renderingConfig.width; x++) {
|
||||||
|
for (let y = 0; y < renderingConfig.height; y++) {
|
||||||
drawTile(x, y, animations);
|
drawTile(x, y, animations);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -160,32 +192,57 @@ function drawRoundedRect(context, x, y, w, h, r, color) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function drawTile(x, y, animations = true) {
|
function drawTile(x, y, animations = true) {
|
||||||
const fontSize = tileSize.y * scaleFactor;
|
const virtualX = renderingConfig.offsetX + x;
|
||||||
|
const virtualY = renderingConfig.offsetY + y;
|
||||||
|
|
||||||
|
const content = field[virtualX][virtualY];
|
||||||
|
|
||||||
|
const width = .8 * renderingConfig.sizeX;
|
||||||
|
const height = .8 * renderingConfig.sizeY;
|
||||||
|
const drawX = (x + .1 - renderingConfig.tiltX) * renderingConfig.sizeX;
|
||||||
|
const drawY = (y + .1 - renderingConfig.tiltY) * renderingConfig.sizeY;
|
||||||
|
const radius = renderingConfig.sizeX * .1;
|
||||||
|
|
||||||
|
let color = getColor(virtualX, virtualY);
|
||||||
|
const fontSize = renderingConfig.sizeY * .5;
|
||||||
|
let fontFamily = "Roboto";
|
||||||
|
let textColor = "white";
|
||||||
|
let text = "";
|
||||||
|
|
||||||
ctx.textAlign = "center";
|
ctx.textAlign = "center";
|
||||||
let duration = 150;
|
let duration = 0;
|
||||||
if (!animations)
|
if (animations)
|
||||||
duration = 0;
|
duration = 150;
|
||||||
|
|
||||||
if (!field[x][y].flagged && field[x][y].clicked) {
|
if (!content.flagged && content.clicked) {
|
||||||
animateTile(x * tileSize.x + .1 * tileSize.x, y * tileSize.y + .1 * tileSize.y,
|
color = "#ddd";
|
||||||
0, 0,
|
if (content.tileValue !== 0) {
|
||||||
tileSize.x - (.2 * tileSize.x), tileSize.y - (.2 * tileSize.y),
|
text = content.tileValue;
|
||||||
0, tileSize.x * .1,
|
textColor = colors[content.tileValue];
|
||||||
new Date().getTime(), duration, "#ddd");
|
// animateText(content.tileValue, (x + .5) * tileSize.x, y * tileSize.y, 0, fontSize, new Date().getTime(), duration, colors[content.tileValue]);
|
||||||
if (field[x][y].tileValue !== 0) {
|
|
||||||
animateText(field[x][y].tileValue, (x + .5) * tileSize.x, y * tileSize.y, 0, fontSize, new Date().getTime(), duration, colors[field[x][y].tileValue]);
|
|
||||||
}
|
}
|
||||||
} else if (field[x][y].flagged) {
|
} else if (content.flagged) {
|
||||||
animateTile(x * tileSize.x + .1 * tileSize.x, y * tileSize.y + .1 * tileSize.y,
|
color = "#ff0000";
|
||||||
0, 0,
|
fontFamily = "FontAwesome";
|
||||||
tileSize.x - (.2 * tileSize.x), tileSize.y - (.2 * tileSize.y),
|
text = "";
|
||||||
0, tileSize.x * .1,
|
// animateText("", (x + .5) * tileSize.x, y * tileSize.y, 0, fontSize, new Date().getTime(), duration, "white", "FontAwesome");
|
||||||
new Date().getTime(), duration, "#ff0000");
|
|
||||||
|
|
||||||
animateText("", (x + .5) * tileSize.x, y * tileSize.y, 0, fontSize, new Date().getTime(), duration, "white", "FontAwesome");
|
|
||||||
} else {
|
|
||||||
drawRoundedRect(ctx, x * tileSize.x + (.1 * tileSize.x), y * tileSize.y + (.1 * tileSize.y), tileSize.x - (.2 * tileSize.x), tileSize.y - (.2 * tileSize.y), tileSize.x * .1, "#2e8cdd");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
animateTile(drawX, drawY, 0, 0, width, height, 0, radius, new Date().getTime(), duration, color);
|
||||||
|
if(text !== "") {
|
||||||
|
animateText(text, x, y, 0, fontSize, new Date().getTime(), duration, textColor, fontFamily, ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getColor(x, y) {
|
||||||
|
x++;
|
||||||
|
y++;
|
||||||
|
const pos = x * y;
|
||||||
|
const limit = fieldSize.x * fieldSize.y;
|
||||||
|
|
||||||
|
let percentage = pos / limit * 360;
|
||||||
|
|
||||||
|
return `hsl(${percentage},100%,50%)`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function easeInOutCubic(t, b, c, d) {
|
function easeInOutCubic(t, b, c, d) {
|
||||||
@@ -200,13 +257,11 @@ function gameOverEvent() {
|
|||||||
animateText("Game Over", canvas.width / 2, canvas.height / 2, 0, tileSize.y * 1.33, new Date().getTime(), 200, "orange", "Roboto", overlay2Ctx);
|
animateText("Game Over", canvas.width / 2, canvas.height / 2, 0, tileSize.y * 1.33, new Date().getTime(), 200, "orange", "Roboto", overlay2Ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPositon(e) {
|
function getPosition(e) {
|
||||||
const x = e.x - canvas.offsetLeft - offsetX * scaleFactor;
|
const x = (e.x - canvas.offsetLeft) / W * zoomFactor + windowX;
|
||||||
const y = e.y - canvas.offsetTop - offsetY * scaleFactor;
|
const y = (e.y - canvas.offsetTop) / H * zoomFactor + windowY;
|
||||||
console.log(x, y);
|
const fieldX = Math.floor(x * fieldSize.x);
|
||||||
const fieldX = Math.floor(x / tileSize.x);
|
const fieldY = Math.floor(y * fieldSize.y);
|
||||||
const fieldY = Math.floor(y / tileSize.y);
|
|
||||||
console.log(fieldX, fieldY);
|
|
||||||
|
|
||||||
return {x: fieldX, y: fieldY};
|
return {x: fieldX, y: fieldY};
|
||||||
}
|
}
|
||||||
@@ -273,12 +328,16 @@ function initBombs(startX, startY) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function scaleCanvas() {
|
function scaleCanvas() {
|
||||||
tileSize = {x: window.innerWidth / fieldSize.x * .9 / scale, y: window.innerWidth / fieldSize.x * .9 / scale};
|
let size = window.innerWidth / fieldSize.x * .9;
|
||||||
|
|
||||||
W = fieldSize.x * tileSize.x;
|
if(size * fieldSize.y > window.innerHeight) {
|
||||||
H = fieldSize.y * tileSize.y;
|
size = window.innerHeight / fieldSize.y * .9;
|
||||||
|
}
|
||||||
|
|
||||||
console.log(canvas);
|
tileSize = {x: size, y: size};
|
||||||
|
|
||||||
|
W = fieldSize.x * size;
|
||||||
|
H = fieldSize.y * size;
|
||||||
|
|
||||||
canvas.width = W;
|
canvas.width = W;
|
||||||
canvas.height = H;
|
canvas.height = H;
|
||||||
@@ -287,13 +346,10 @@ function scaleCanvas() {
|
|||||||
overlay2Canvas.width = W;
|
overlay2Canvas.width = W;
|
||||||
overlay2Canvas.height = H;
|
overlay2Canvas.height = H;
|
||||||
|
|
||||||
offsetX = -W * scale * 10;
|
|
||||||
offsetY = -H * scale * 10;
|
|
||||||
|
|
||||||
initBalls();
|
initBalls();
|
||||||
|
|
||||||
drawGrid(false);
|
|
||||||
applyScaling();
|
applyScaling();
|
||||||
|
|
||||||
if (gameOver) {
|
if (gameOver) {
|
||||||
gameOverEvent();
|
gameOverEvent();
|
||||||
} else if (victory) {
|
} else if (victory) {
|
||||||
@@ -326,7 +382,11 @@ function tileFlag(x, y) {
|
|||||||
return;
|
return;
|
||||||
field[x][y].flagged = !field[x][y].flagged;
|
field[x][y].flagged = !field[x][y].flagged;
|
||||||
field[x][y].clicked = field[x][y].flagged;
|
field[x][y].clicked = field[x][y].flagged;
|
||||||
ctx.clearRect(x * tileSize.x + (1 / scaleFactor), y * tileSize.y + (1 / scaleFactor), tileSize.x - (2 / scaleFactor), tileSize.y - (2 / scaleFactor));
|
|
||||||
|
const drawX = (x - renderingConfig.tiltX) * renderingConfig.sizeX;
|
||||||
|
const drawY = (y - renderingConfig.tiltY) * renderingConfig.sizeY;
|
||||||
|
|
||||||
|
ctx.clearRect(drawX, drawY, renderingConfig.sizeX, renderingConfig.sizeY);
|
||||||
drawTile(x, y);
|
drawTile(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -344,7 +404,7 @@ function uncoverTile(x, y) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
field[x][y].clicked = true;
|
field[x][y].clicked = true;
|
||||||
drawTile(x, y);
|
drawTile(x, y, );
|
||||||
if (field[x][y].tileValue === true) {
|
if (field[x][y].tileValue === true) {
|
||||||
gameOverEvent();
|
gameOverEvent();
|
||||||
}
|
}
|
||||||
@@ -368,7 +428,7 @@ function victoryEvent() {
|
|||||||
play = false;
|
play = false;
|
||||||
const fontSize = tileSize.y * 1.33;
|
const fontSize = tileSize.y * 1.33;
|
||||||
animateBackground(0, 0, canvas.width, canvas.height, 0, .01, new Date().getTime(), 200, {r: 0, g: 0, b: 0, a: 0});
|
animateBackground(0, 0, canvas.width, canvas.height, 0, .01, new Date().getTime(), 200, {r: 0, g: 0, b: 0, a: 0});
|
||||||
animateText("Victory!", canvas.width / 2, canvas.height / 2 - fontSize / 2, 0, fontSize, new Date().getTime(), 200, "green", "Roboto", overlay2Ctx);
|
animateText("Victory!", W / 2, H / 2 - fontSize / 2, 0, fontSize, new Date().getTime(), 200, "green", "Roboto", overlay2Ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
Object.prototype.count = function (val) {
|
Object.prototype.count = function (val) {
|
||||||
@@ -396,7 +456,7 @@ Object.prototype.countFlagged = function (val) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
overlay2Canvas.addEventListener("click", (e) => {
|
overlay2Canvas.addEventListener("click", (e) => {
|
||||||
const pos = getPositon(e);
|
const pos = getPosition(e);
|
||||||
|
|
||||||
if (isFirstClick) {
|
if (isFirstClick) {
|
||||||
initBombs(pos.x, pos.y);
|
initBombs(pos.x, pos.y);
|
||||||
@@ -411,7 +471,7 @@ overlay2Canvas.addEventListener("click", (e) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
overlay2Canvas.addEventListener("dblclick", (e) => {
|
overlay2Canvas.addEventListener("dblclick", (e) => {
|
||||||
const pos = getPositon(e);
|
const pos = getPosition(e);
|
||||||
|
|
||||||
tileDoubleClick(pos.x, pos.y);
|
tileDoubleClick(pos.x, pos.y);
|
||||||
|
|
||||||
@@ -421,37 +481,47 @@ overlay2Canvas.addEventListener("dblclick", (e) => {
|
|||||||
overlay2Canvas.addEventListener("contextmenu", (e) => {
|
overlay2Canvas.addEventListener("contextmenu", (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
const pos = getPositon(e);
|
const pos = getPosition(e);
|
||||||
|
|
||||||
tileFlag(pos.x, pos.y);
|
tileFlag(pos.x, pos.y);
|
||||||
});
|
});
|
||||||
|
|
||||||
let scale = 1;
|
|
||||||
let offsetX = 0;
|
|
||||||
let offsetY = 0;
|
|
||||||
|
|
||||||
window.addEventListener("keyup", (e) => {
|
window.addEventListener("keyup", (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
if(e.keyCode === 171) {
|
if (e.code === "BracketRight") {
|
||||||
scale += .2;
|
zoomFactor -= .1;
|
||||||
} else if(e.keyCode === 173) {
|
} else if (e.code === "Slash") {
|
||||||
if(canvas.width * scale > window.innerWidth && canvas.height * scale > window.innerHeight)
|
zoomFactor += .1;
|
||||||
scale -= .2;
|
} else if (e.code === "ArrowLeft") {
|
||||||
|
windowX -= .1;
|
||||||
|
} else if (e.code === "ArrowRight") {
|
||||||
|
windowX += .1;
|
||||||
|
} else if (e.code === "ArrowUp") {
|
||||||
|
windowY -= .1;
|
||||||
|
} else if (e.code === "ArrowDown") {
|
||||||
|
windowY += .1;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
applyScaling();
|
zoomFactor = Math.min(zoomFactor, 1);
|
||||||
|
zoomFactor = Math.max(zoomFactor, .1);
|
||||||
|
|
||||||
console.log("Test");
|
windowX = Math.min(windowX, 1 - zoomFactor);
|
||||||
|
windowY = Math.min(windowY, 1 - zoomFactor);
|
||||||
|
windowX = Math.max(windowX, 0);
|
||||||
|
windowY = Math.max(windowY, 0);
|
||||||
|
|
||||||
|
applyScaling();
|
||||||
});
|
});
|
||||||
|
|
||||||
let startClientX = 0;
|
// let startClientX = 0;
|
||||||
let startClientY = 0;
|
// let startClientY = 0;
|
||||||
|
//
|
||||||
|
// let isDragging = false;
|
||||||
|
|
||||||
let isDragging = false;
|
/*document.addEventListener("mousedown", (e) => {
|
||||||
|
|
||||||
document.addEventListener("mousedown", (e) => {
|
|
||||||
console.log(e);
|
|
||||||
if(e.button === 0) {
|
if(e.button === 0) {
|
||||||
isDragging = true;
|
isDragging = true;
|
||||||
startClientX = e.clientX;
|
startClientX = e.clientX;
|
||||||
@@ -465,14 +535,28 @@ document.addEventListener("mouseup", (e) => {
|
|||||||
|
|
||||||
document.addEventListener("mousemove", (e) => {
|
document.addEventListener("mousemove", (e) => {
|
||||||
if(isDragging) {
|
if(isDragging) {
|
||||||
// console.log(e, e.clientX - startClientX, e.clientY - startClientY);
|
const deltaX = e.clientX - startClientX;
|
||||||
offsetX += (e.clientX - startClientX);
|
const deltaY = e.clientY - startClientY;
|
||||||
offsetY += (e.clientY - startClientY);
|
|
||||||
startClientX = e.clientX;
|
startClientX = e.clientX;
|
||||||
startClientY = e.clientY;
|
startClientY = e.clientY;
|
||||||
|
windowX -= 1 / deltaX / 10;
|
||||||
|
windowY -= 1 / deltaY / 10;
|
||||||
|
|
||||||
|
windowX = Math.min(windowX, 1 - zoomFactor);
|
||||||
|
windowY = Math.min(windowY, 1 - zoomFactor);
|
||||||
|
windowX = Math.max(windowX, 0);
|
||||||
|
windowY = Math.max(windowY, 0);
|
||||||
|
|
||||||
|
console.log(windowX, windowY);
|
||||||
|
|
||||||
|
// console.log(e, e.clientX - startClientX, e.clientY - startClientY);
|
||||||
|
// offsetX += (e.clientX - startClientX);
|
||||||
|
// offsetY += (e.clientY - startClientY);
|
||||||
|
// startClientX = e.clientX;
|
||||||
|
// startClientY = e.clientY;
|
||||||
applyScaling();
|
applyScaling();
|
||||||
}
|
}
|
||||||
});
|
});*/
|
||||||
|
|
||||||
// document.addEventListener("dragstart", (e) => {
|
// document.addEventListener("dragstart", (e) => {
|
||||||
// console.log(e);
|
// console.log(e);
|
||||||
@@ -484,13 +568,6 @@ document.addEventListener("mousemove", (e) => {
|
|||||||
// console.log(startClientX - e.clientX);
|
// console.log(startClientX - e.clientX);
|
||||||
// });
|
// });
|
||||||
|
|
||||||
function applyScaling() {
|
|
||||||
canvas.style.transform = "scale(" + scale + ") translate(" + offsetX + "px, " + offsetY + "px)";
|
|
||||||
overlayCanvas.style.transform = "scale(" + scale + ") translate(" + offsetX + "px, " + offsetY + "px)";
|
|
||||||
overlay2Canvas.style.transform = "scale(" + scale + ") translate(" + offsetX + "px, " + offsetY + "px)";
|
|
||||||
console.log("scale(" + scale + ") translate(" + offsetX + "px, " + offsetY + "px)");
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener("resize", () => {
|
window.addEventListener("resize", () => {
|
||||||
scaleCanvas();
|
scaleCanvas();
|
||||||
});
|
});
|
||||||
|
Reference in New Issue
Block a user