I have a question about resizing/drawing a gameboard based on the size of the window or browser that is being used. I am coding the game reversi using html/css/js. An image will be attached of what I want to achieve. The game board itself has the same height as the info that is displayed to the right of it. I would like it to be for example 70% of the window height so that I still have the remaining 30% to make a border etc. In HTML I defined a table with the 'board-table' id and then I tried making a variable 'size' to determine the height of this table. In CSS I specified that the height should be 70% so that the game board can be drawn afterwards. However, it always has a prefixed size when I reload the page in different dimensions and thus I was wondering how I could fix it. A section of my code is displayed below.
HTML:
<table id="board-table"></table>
CSS:
#board-table {
height: 70%;
}
Javascript:
function draw() {
var size = $('#board-table').height();
var square = (1/8) * size;
var half = (1/2) * square;
for (var y = 0; y < 8; y++) {
for (var x = 0; x < 8; x++) {
var canvas = $("#canv_" + x + "" + y)[0]
var ctx = canvas.getContext("2d")
if (game.board[x][y] == 1) {
ctx.fillStyle = "green"
ctx.fillRect(0, 0, square, square)
ctx.beginPath()
ctx.fillStyle = "white"
ctx.arc(half, half, half, 0, 2 * Math.PI)
ctx.fill()
} else if (game.board[x][y] == 2) {
ctx.fillStyle = "green"
ctx.fillRect(0, 0, square, square)
ctx.beginPath()
ctx.fillStyle = "black"
ctx.arc(half, half, half, 0, 2 * Math.PI)
ctx.fill()
} else {
ctx.fillStyle = "green"
ctx.fillRect(0, 0, square, square)
}
}
}
}
function generateBoard() {
for (var y = 0; y < 8; y++) {
$("#board-table").append("<tr id=row" + y + "" + "><tr")
for (var x = 0; x < 8; x++) {
$("#row" + y).append("<td id=cell_" + x + "" + y + "></td>")
$("#cell_" + x + "" + y).append("<canvas height=100% onclick=handleclick(" + x + "," + y + ") onmouseover=handlehover(" + x + "," + y + ") width =100% id=canv_" + x + "" + y + "></canvas>")
}
}
}
Example of what I am trying to achieve.
In css when you use height: 70%; this refers to 70% of the containing element.
This can be bypassed by using height: 70vh;
This is covered in detail here
Here is my code, which is very different from yours, but it scales and handles colors for both placed pieces and pieces being placed.
<!doctype html>
<html>
<head>
<title>Reversi</title>
<style>
body {
bottom: 0;
left: 0;
position: fixed;
right: 0;
top: 0;
}
</style>
</head>
<body>
<canvas class="board"></canvas>
<script>
const pi2 = 2 * Math.PI;
const pieceColor = {
1: '#FFF',
2: '#000',
11: '#fff5',
12: '#0005'
};
let board = [];
function resetBoard(canvas) {
board = [];
for(let i = 0; i < 8; i++) {
board.push([0,0,0,0,0,0,0,0]);
}
board[3][3] = 1;
board[4][4] = 1;
board[4][3] = 2;
board[3][4] = 2;
board[3][5] = 11;
board[2][3] = 12;
}
function draw(canvas) {
const body = document.body;
let canvasSize = (body.offsetWidth > body.offsetHeight ? body.offsetHeight : body.offsetWidth)-20;
if (canvasSize < 150) {
canvasSize = 150;
}
if (canvasSize > 550) {
canvasSize = 550;
}
console.log(canvasSize);
canvas.setAttribute('width', canvasSize+'px');
canvas.setAttribute('height', canvasSize+'px');
var ctx = canvas.getContext('2d')
var size = canvas.offsetWidth;
var square = (size-9)/8;
var half = (square/2)-2;
ctx.fillStyle = '#555';
ctx.fillRect(0, 0, size, size);
for (var y = 0; y < 8; y++) {
for (var x = 0; x < 8; x++) {
const piece = board[x][y];
ctx.fillStyle = '#449933';
const left = (x*(square+1))+1;
const top = (y*(square+1))+1;
ctx.fillRect(left, top, square, square);
if (piece !== 0) {
ctx.beginPath();
ctx.fillStyle = pieceColor[piece];
ctx.arc(half+left+2, half+top+2, half, 0, pi2);
ctx.fill();
}
}
}
}
const c = document.querySelector('.board');
resetBoard(c);
draw(c);
window.addEventListener('resize', () => {
draw(c);
})
</script>
</body>
</html>
Related
<canvas id="myCanvas" width="300" height="300" style="border:1px solid #d3d3d3;">
Mouse over <canvas> does not draw.
var TILE_WIDTH = 30;
var TILE_HEIGHT = 30;
document.getElementById("input").onchange = function() {
var reader = new FileReader();
reader.onload = function(e) {
// get loaded data and render thumbnail.
document.getElementById("image").src = e.target.result;
};
// read the image file as a data URL.
reader.readAsDataURL(this.files[0]);
};
// The first function call to create photomosaic
function photomosaic(image) {
// Dimensions of each tile
var tileWidth = TILE_WIDTH;
var tileHeight = TILE_HEIGHT;
// creating the canvas for photomosaic
var canvas = document.createElement('canvas');
var context = canvas.getContext("2d");
canvas.height = image.height;
canvas.width = image.width;
var imageData = context.getImageData(0, 0, image.width, image.height);
var pixels = imageData.data;
// The Number of mosaic tiles
var numTileRows = image.width / tileWidth;
var numTileCols = image.height / tileHeight;
// The canvas copy of image
var imageCanvas = document.createElement('canvas');
var imageCanvasContext = canvas.getContext('2d');
imageCanvas.height = image.height;
imageCanvas.width = image.width;
imageCanvasContext.drawImage(image, 0, 0);
// A function for finding the average color
function averageColor(row, column) {
var blockSize = 1, // we can set how many pixels to skip
data, width, height,
i = -4,
length,
rgb = {
r: 0,
g: 0,
b: 0
},
count = 0;
try {
data = imageCanvasContext.getImageData(column * TILE_WIDTH, row * TILE_HEIGHT, TILE_HEIGHT, TILE_WIDTH);
} catch (e) {
alert('Not happening this time!');
return rgb;
}
length = data.data.length;
while ((i += blockSize * 4) < length) {
++count;
rgb.r += data.data[i];
rgb.g += data.data[i + 1];
rgb.b += data.data[i + 2];
}
// ~~ used to floor values
rgb.r = ~~(rgb.r / count);
rgb.g = ~~(rgb.g / count);
rgb.b = ~~(rgb.b / count);
return rgb;
}
// Loop through each tile
for (var r = 0; r < numTileRows; r++) {
for (var c = 0; c < numTileCols; c++) {
// Set the pixel values for each tile
var rgb = averageColor(r, c)
var red = rgb.r;
var green = rgb.g;
var blue = rgb.b;
// Loop through each tile pixel
for (var tr = 0; tr < tileHeight; tr++) {
for (var tc = 0; tc < tileWidth; tc++) {
// Calculate the true position of the tile pixel
var trueRow = (r * tileHeight) + tr;
var trueCol = (c * tileWidth) + tc;
// Calculate the position of the current pixel in the array
var pos = (trueRow * (imageData.width * 4)) + (trueCol * 4);
// Assign the colour to each pixel
pixels[pos + 0] = red;
pixels[pos + 1] = green;
pixels[pos + 2] = blue;
pixels[pos + 3] = 255;
};
};
};
};
// Draw image data to the canvas
context.putImageData(imageData, 0, 0);
return canvas;
}
function create() {
var image = document.getElementById('image');
var canvas = photomosaic(image);
document.getElementById("output").appendChild(canvas);
};
#output,
.container {
text-align: center;
}
.inputDiv {
margin: 20px 0px;
}
#image {
width: 300px;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>test</title>
</head>
<body>
<div class="container">
<img id="image" src="https://via.placeholder.com/100" />
<div class="inputDiv">
<input id="input" type="file" accept="image/*">
<button onclick="create()">create</button>
</div>
<canvas id="myCanvas" width="300" height="300" style="border:1px solid #d3d3d3;">
</canvas>
<div id='output'></div>
</div>
</body>
</html>
How do I create a circle when I touch the mouse? I'm sorry for the poor translation. This is the only place to ask. I'm also sorry that the code is very long. Not applicable to script ...
I set an HTML5 canvas to a background that looks like interlinked nodes (link). The canvas element works as expected but when I set it to be the background of the page, I couldn't click on links or buttons on the page.
When I set the canvas background to the back of all elements by either declaring a z-index value or editing the HTML so that it's the first element, I can normally click on links, but every time a pointer-event is active on a button (such as a hover) it stops the canvas background from getting the mouse pointer's location.
I expect it's got something to do with the pointer-events, but I can't move past this because I'm unsure as to what to do.
Supposing that you put the content of your document in a div #wrap with a position absolute over the canvas, in order to interact with the particles on canvas you may use the mouse position over the #wrap. In your example I'm using wrap.addEventListener('mousemove', mousemoveHandler, false); instead of canvas.addEventListener('mousemove', mousemoveHandler, false);
(function() {
var canvas, ctx, circ, nodes, mouse, SENSITIVITY, SIBLINGS_LIMIT, DENSITY, NODES_QTY, ANCHOR_LENGTH, MOUSE_RADIUS;
// how close next node must be to activate connection (in px)
// shorter distance == better connection (line width)
SENSITIVITY = 100;
// note that siblings limit is not 'accurate' as the node can actually have more connections than this value that's because the node accepts sibling nodes with no regard to their current connections this is acceptable because potential fix would not result in significant visual difference
// more siblings == bigger node
SIBLINGS_LIMIT = 10;
// default node margin
DENSITY = 50;
// total number of nodes used (incremented after creation)
NODES_QTY = 0;
// avoid nodes spreading
ANCHOR_LENGTH = 20;
// highlight radius
MOUSE_RADIUS = 200;
circ = 2 * Math.PI;
nodes = [];
canvas = document.querySelector('canvas');
resizeWindow();
mouse = {
x: canvas.width / 2,
y: canvas.height / 2
};
ctx = canvas.getContext('2d');
if (!ctx) {
alert("Ooops! Your browser does not support canvas :'(");
}
function Node(x, y) {
this.anchorX = x;
this.anchorY = y;
this.x = Math.random() * (x - (x - ANCHOR_LENGTH)) + (x - ANCHOR_LENGTH);
this.y = Math.random() * (y - (y - ANCHOR_LENGTH)) + (y - ANCHOR_LENGTH);
this.vx = Math.random() * 2 - 1;
this.vy = Math.random() * 2 - 1;
this.energy = Math.random() * 100;
this.radius = Math.random();
this.siblings = [];
this.brightness = 0;
}
Node.prototype.drawNode = function() {
var color = "rgba(255, 0, 0, " + this.brightness + ")";
ctx.beginPath();
ctx.arc(this.x, this.y, 2 * this.radius + 2 * this.siblings.length / SIBLINGS_LIMIT, 0, circ);
ctx.fillStyle = color;
ctx.fill();
};
Node.prototype.drawConnections = function() {
for (var i = 0; i < this.siblings.length; i++) {
var color = "rgba(255, 0, 0, " + this.brightness + ")";
ctx.beginPath();
ctx.moveTo(this.x, this.y);
ctx.lineTo(this.siblings[i].x, this.siblings[i].y);
ctx.lineWidth = 1 - calcDistance(this, this.siblings[i]) / SENSITIVITY;
ctx.strokeStyle = color;
ctx.stroke();
}
};
Node.prototype.moveNode = function() {
this.energy -= 2;
if (this.energy < 1) {
this.energy = Math.random() * 100;
if (this.x - this.anchorX < -ANCHOR_LENGTH) {
this.vx = Math.random() * 2;
} else if (this.x - this.anchorX > ANCHOR_LENGTH) {
this.vx = Math.random() * -2;
} else {
this.vx = Math.random() * 4 - 2;
}
if (this.y - this.anchorY < -ANCHOR_LENGTH) {
this.vy = Math.random() * 2;
} else if (this.y - this.anchorY > ANCHOR_LENGTH) {
this.vy = Math.random() * -2;
} else {
this.vy = Math.random() * 4 - 2;
}
}
this.x += this.vx * this.energy / 100;
this.y += this.vy * this.energy / 100;
};
function initNodes() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
nodes = [];
for (var i = DENSITY; i < canvas.width; i += DENSITY) {
for (var j = DENSITY; j < canvas.height; j += DENSITY) {
nodes.push(new Node(i, j));
NODES_QTY++;
}
}
}
function calcDistance(node1, node2) {
return Math.sqrt(Math.pow(node1.x - node2.x, 2) + (Math.pow(node1.y - node2.y, 2)));
}
function findSiblings() {
var node1, node2, distance;
for (var i = 0; i < NODES_QTY; i++) {
node1 = nodes[i];
node1.siblings = [];
for (var j = 0; j < NODES_QTY; j++) {
node2 = nodes[j];
if (node1 !== node2) {
distance = calcDistance(node1, node2);
if (distance < SENSITIVITY) {
if (node1.siblings.length < SIBLINGS_LIMIT) {
node1.siblings.push(node2);
} else {
var node_sibling_distance = 0;
var max_distance = 0;
var s;
for (var k = 0; k < SIBLINGS_LIMIT; k++) {
node_sibling_distance = calcDistance(node1, node1.siblings[k]);
if (node_sibling_distance > max_distance) {
max_distance = node_sibling_distance;
s = k;
}
}
if (distance < max_distance) {
node1.siblings.splice(s, 1);
node1.siblings.push(node2);
}
}
}
}
}
}
}
function redrawScene() {
resizeWindow();
ctx.clearRect(0, 0, canvas.width, canvas.height);
findSiblings();
var i, node, distance;
for (i = 0; i < NODES_QTY; i++) {
node = nodes[i];
distance = calcDistance({
x: mouse.x,
y: mouse.y
}, node);
if (distance < MOUSE_RADIUS) {
node.brightness = 1 - distance / MOUSE_RADIUS;
} else {
node.brightness = 0;
}
}
for (i = 0; i < NODES_QTY; i++) {
node = nodes[i];
if (node.brightness) {
node.drawNode();
node.drawConnections();
}
node.moveNode();
}
requestAnimationFrame(redrawScene);
}
function initHandlers() {
document.addEventListener('resize', resizeWindow, false);
///////////////////////////////////
wrap.addEventListener('mousemove', mousemoveHandler, false);
///////////////////////////////////
}
function resizeWindow() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
function mousemoveHandler(e) {
mouse.x = e.clientX;
mouse.y = e.clientY;
}
initHandlers();
initNodes();
redrawScene();
})();
html,
body {
margin: 0;
background-color: #25003a;
overflow: hidden;
}
canvas {
position: relative;
top: 0;
bottom: 0;
width: 100%;
height: 100%;
background-color: #25003a;
overflow: hidden;
}
#wrap {
position: absolute;
top: 0;
left: 0;
height: 100vh;
width: 100vw;
line-height: 100vh;
}
#wrap p {
text-align: center;
border: 1px solid;
}
#wrap p a {
color: white;
font-size: 70px;
text-decoration: none;
}
#wrap p a:hover {
text-decoration: underline;
}
<canvas></canvas>
<div id="wrap">
<p>
this is a link</p>
</div>
I am now working with a Web Application, which needs to draw some cards on a fixed rectangle canvas. Below is the criteria:
The canvas size is fixed with width "w" and height "h" when the Web Application starts.
There are "n" no. of cards which won't be changed after started.
All cards must in the same size, which has a fixed ratio with width "cw" and height "ch", the cards are able to re-size within the canvas.
I would like to calculate the maximum width and height of each card in such cases. Can anybody help?
Your question is lacking a lot of information. please read the comments in the code. I hope my answer may help you.
// initiate the canvas
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
let cw = canvas.width = 300,
cx = cw / 2;
let ch = canvas.height = 300,
cy = ch / 2;
// number of cards
let n = 12;
// the horizontal and vertical ratio
let ratio = {x:.2,y:.3}
// the width and the height of a card
let w = cw*ratio.x;
let h = ch*ratio.y;
// a counter
let i = 0;
//a double for loop to draw the cards
for(let y = 0; y<ch; y+=h){
for(let x = 0; x<cw; x+=w){
if(i < n)
{drawCard(x,y);
i++}
}
}
function drawCard(x,y){
ctx.beginPath();
ctx.strokeRect(x,y,w,h);
}
canvas {
border:1px solid #d9d9d9;
}
<canvas id="canvas"></canvas>
Finally, I found the solution myself.
This performance may not is the optimal one, but the solution gives me the new card width, new card height, cards per rows and cards per column. So that the cards can almost cover the canvas.
function crwh(col, row, n, cw, ch, w, h, exactN) {
// Methods
this.fn_init = function(col, row) {
this.col = col;
this.row = row;
if (this.col > 0 && this.row > 0) {
// Calculate new card width & card height base on col
this.cw_new = (this.w / this.col);
this.ch_new = (this.cw_new / this.cw * this.ch);
if (this.fn_valid() == false) {
// Calculate new card height & card width base on row
this.ch_new = (this.h / this.row);
this.cw_new = (this.ch_new / this.ch * cw);
}
}
}
this.fn_area = function() {
// Get the size the rectangle
return this.cw_new * this.ch_new;
}
this.fn_valid = function() {
var valid = true;
// True if col * row must equal to no. of cards, False allow col * row greater than no. of cards
valid = valid && ((this.exactN == true && (this.col * this.row) == this.n) || (this.exactN == false && (this.col * this.row) >= this.n));
// col * card width (new) must be shorter than canvas width
valid = valid && ((this.col * this.cw_new) <= this.w);
// row * card height (new) must be shorter than canvas height
valid = valid && ((this.row * this.ch_new) <= this.h);
return valid;
}
// Properties
this.n = n;
this.cw = cw;
this.ch = ch;
this.w = w;
this.h = h;
this.exactN = exactN;
this.col = 0;
this.row = 0;
this.cw_new = 0;
this.ch_new = 0;
this.fn_init(col, row);
}
function fn_getCardDimensions(n, cw, ch, w, h, exactN) {
var crwh_max = new crwh(0, 0);
// Loop thru 1 to n for col & row to see which combination allow a maximum card size
for (var col = 1; col <= n; col++) {
for (var row = 1; row <= n; row++) {
if ((col * row) >= n) {
var crwh_cur = new crwh(col, row, n, cw, ch, w, h, exactN);
if (crwh_cur.fn_valid()) {
if (crwh_cur.fn_area() > crwh_max.fn_area()) {
crwh_max = crwh_cur;
}
}
}
}
}
return [crwh_max.col, crwh_max.row, crwh_max.cw_new, crwh_max.ch_new];
}
var n = 80; // No. of Cards
var cw = 344; // Card Width (orig)
var ch = 512; // Card Height (orig)
var w = 0; // Canvas Width
var h = 0; // Canvas Height
var exactN = true; // True if col * row must equal to no. of cards
function fn_drawCards() {
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
w = canvas.width;
h = canvas.height;
[col, row, cw_new, ch_new] = fn_getCardDimensions(n, cw, ch, w, h, exactN);
for (var i = 0; i < col; i++) {
for (var j = 0; j < row; j++) {
ctx.beginPath();
ctx.strokeRect(i * cw_new, j * ch_new, cw_new, ch_new);
}
}
}
fn_drawCards();
canvas {
border:1px solid #00FF00;
}
<canvas id="canvas"></canvas>
I've been searching everywhere and couldn't find how to draw a grid on an HTML5 Canvas. I'm new to HTML5 and canvas.
I know how to draw shapes but this drawing grid is taking forever to understand.
Can someone help me on this?
The answer is taken from here Grid drawn using a <canvas> element looking stretched
Just edited it a little, hope it helps
// Box width
var bw = 400;
// Box height
var bh = 400;
// Padding
var p = 10;
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
function drawBoard(){
for (var x = 0; x <= bw; x += 40) {
context.moveTo(0.5 + x + p, p);
context.lineTo(0.5 + x + p, bh + p);
}
for (var x = 0; x <= bh; x += 40) {
context.moveTo(p, 0.5 + x + p);
context.lineTo(bw + p, 0.5 + x + p);
}
context.strokeStyle = "black";
context.stroke();
}
drawBoard();
body {
background: lightblue;
}
#canvas {
background: #fff;
margin: 20px;
}
<div>
<canvas id="canvas" width="420px" height="420px"></canvas>
</div>
// Box width
var bw = 270;
// Box height
var bh = 180;
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
function drawBoard(){
context.lineWidth = 10;
context.strokeStyle = "rgb(2,7,159)";
for (var x = 0; x < bw; x += 90) {
for (var y = 0; y < bh; y += 90) {
context.strokeRect(x+10, y+10, 90, 90);
}
}
}
drawBoard();
This code allows for a scalable / resize grid
const canvas = document.getElementById('canvas')
const ctx = canvas.getContext('2d')
canvas.width = window.innerWidth
canvas.height = window.innerHeight
function drawBoard()
{
// canvas dims
const bw = window.innerWidth
const bh = window.innerHeight
const lw = 1 // box border
const boxRow = 10 // how many boxes
const box = bw / boxRow // box size
ctx.lineWidth = lw
ctx.strokeStyle = 'rgb(2,7,159)'
for (let x=0;x<bw;x+=box)
{
for (let y=0;y<bh;y+=box)
{
ctx.strokeRect(x,y,box,box)
}
}
}
let rTimeout = null
window.addEventListener('resize', (e) =>
{
clearTimeout(rTimeout)
ctx.clearRect(0, 0, window.innerWidth, window.innerHeight)
rTimeout = setTimeout(function(){drawBoard()}, 33)
})
drawBoard()
<canvas id="canvas"></canvas>
How can I align text within a html5 canvas to justify"? In the code below, text can be left/right/center aligned. I need to set align="justify". Please suggest how can this be done?
HTML:
<body onload="callMe()">
<canvas id="MyCanvas"></canvas>
</body>
JS:
function callMe() {
var canvas = document.getElementById("MyCanvas");
var ctx = canvas.getContext("2d");
var txt = "Welcome to the new hello world example";
cxt.font = "10pt Arial";
cxt.textAlign = "left";
/* code to text wrap*/
cxt.fillText(txt, 10, 20);
}
HTML5's canvas doesn't support multiline text drawing so there is no real effect to the alignment type.
If you want to support line-feeds, you have to support it yourself, you can see a previous discussion about it here: HTML5 Canvas - can I somehow use linefeeds in fillText()?
This is my implementation for word-wrap / line feeds:
function printAtWordWrap(context, text, x, y, lineHeight, fitWidth) {
fitWidth = fitWidth || 0;
lineHeight = lineHeight || 20;
var currentLine = 0;
var lines = text.split(/\r\n|\r|\n/);
for (var line = 0; line < lines.length; line++) {
if (fitWidth <= 0) {
context.fillText(lines[line], x, y + (lineHeight * currentLine));
} else {
var words = lines[line].split(' ');
var idx = 1;
while (words.length > 0 && idx <= words.length) {
var str = words.slice(0, idx).join(' ');
var w = context.measureText(str).width;
if (w > fitWidth) {
if (idx == 1) {
idx = 2;
}
context.fillText(words.slice(0, idx - 1).join(' '), x, y + (lineHeight * currentLine));
currentLine++;
words = words.splice(idx - 1);
idx = 1;
}
else
{ idx++; }
}
if (idx > 0)
context.fillText(words.join(' '), x, y + (lineHeight * currentLine));
}
currentLine++;
}
}
there is no support for alignment or justification there, you'll have to add it in