Adding images by combining cells with Javascript canvas - javascript

First of all, I have difficulty in explaining because my English is not very good. But I will try to explain as best I can.
I add random photos to the canvasta cells fields with Javascript.
Each plot is equivalent to 20pixels. What I want to do is:
if i and y in the map data are equal to 4;
I want to add the photo I want to a 4x4 area.
In other words, while adding a photo to 20 pixels;
I want to add a photo to a 320pixel area when it is 4x4.
As in the sample photo.
Check Photo
var map = [
[1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1],
[1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0],
[1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0],
[1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0],
[1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0],
[1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1],
[1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1],
[1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1],
[1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1],
[1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1],
[4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
];
window.onload = function() {
const canvas = document.getElementById("main");
const ctx = canvas.getContext("2d");
ctx.strokeStyle = "transparent";
ctx.lineWidth = 0;
//draw grid
for (let i = 0; i <= 300; i++) {
const x = i*20;
ctx.moveTo(x, 0);
ctx.lineTo(x, canvas.height);
ctx.stroke();
const y = i*20;
ctx.moveTo(0, y);
ctx.lineTo(canvas.width, y);
ctx.stroke();
}
//draw images
const p = ctx.lineWidth / 1; //padding
for (let xCell = 0; xCell < map.length; xCell++) {
for (let yCell = 0; yCell < map[xCell].length; yCell++) {
const x = xCell * 20;
const y = yCell * 20;
const img = new Image();
if (map[xCell][yCell] === 1) {
img.onload = function() {
ctx.drawImage(img, y+p, x+p, 20-p*2, 20-p*2);
};
//TODO: set img.src to your api url instead of the dummyimage url.
img.src = `https://picsum.photos/id/${Math.floor(Math.random() * 10)}${Math.floor(Math.random() * 10)}/200/300`;
}else if(map[xCell][yCell] == 4){
img.onload = function() {
ctx.drawImage(img, y+p, x+p, 20-p*2, 20-p*2);
};
//TODO: set img.src to your api url instead of the dummyimage url.
img.src = `https://picsum.photos/id/${Math.floor(Math.random() * 12)}${Math.floor(Math.random() * 12)}/200/300`;
}
}
}
};
<canvas id="main" width="600" height="630"></canvas>
</canvas>

I solved the problem as below. There are positional errors but I will completely fix them.
Thanks to Lajos Arpad for trying to help.
var defaultMap = [
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1],
[0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
[0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
[0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0],
[0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0],
[0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1],
[0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1],
[1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1],
[1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1],
[1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1],
[1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1],
];
var images = [
{
name: "player 1",
position: [
{
x: [0,4],
y: [4,4]
}
]
},
{
name: "player 2",
position: [
{
x: [5,7],
y: [5,2]
}
]
}
];
window.onload = function() {
const canvas = document.getElementById("main");
const ctx = canvas.getContext("2d");
ctx.strokeStyle = "transparent";
ctx.lineWidth = 0;
//draw grid
for (let i = 0; i <= 300; i++) {
const x = i*40;
ctx.moveTo(x, 0);
ctx.lineTo(x, canvas.height);
ctx.stroke();
const y = i*40;
ctx.moveTo(0, y);
ctx.lineTo(canvas.width, y);
ctx.stroke();
}
const p = ctx.lineWidth / 1;
for (let xCell = 0; xCell < defaultMap.length; xCell++) {
for (let yCell = 0; yCell < defaultMap[xCell].length; yCell++) {
const x = xCell * 20;
const y = yCell * 20;
const img = new Image();
if (defaultMap[xCell][yCell] === 1) {
img.onload = function() {
ctx.drawImage(img, y+p, x+p, 20-p*2, 20-p*2);
};
img.src = `https://besthqwallpapers.com/Uploads/5-6-2020/134999/thumb-black-ground-texture-macro-grunge-textures-black-backgrounds-ground-textures.jpg`;
}
}
}
for (let i = 0; i < images.length; i++) {
var width = 1, height = 1, x = images[i].position[0].x[0], y = images[i].position[0].y[0];
console.log(images[i].name + " x => " + images[i].position[0].x[0])
for(let w = images[i].position[0].x[0]; w < images[i].position[0].x[1]; w++){
width++
}
for(let h = 1; h < images[i].position[0].y[1]; h++){
height++
}
const img = new Image();
img.src = `https://picsum.photos/id/${Math.floor(Math.random() * 12)}${Math.floor(Math.random() * 12)}/200/300`;
img.onload = function() {
console.log(20*images[i].position[0].x[0]+p)
/* ctx.rect(20*i, 20*i, 20*width-p*2, 20*height-p*2)
ctx.fillStyle = "blue";
ctx.fill(); */
ctx.drawImage(img, 20*images[i].position[0].y[0]+p, 20*images[i].position[0].x[0]+p, 20*width-p*2, 20*height-p*2);
};
}
};
<canvas id="main" width="600" height="630"></canvas>
</canvas>

You can compute the value of a multiplier and use that to determine the picture size.
var map = [
[1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1],
[1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0],
[1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0],
[1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0],
[1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0],
[1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1],
[1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1],
[1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1],
[1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1],
[1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1],
[4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
];
window.onload = function() {
const canvas = document.getElementById("main");
const ctx = canvas.getContext("2d");
ctx.strokeStyle = "transparent";
ctx.lineWidth = 0;
//draw grid
for (let i = 0; i <= 300; i++) {
const x = i*20;
ctx.moveTo(x, 0);
ctx.lineTo(x, canvas.height);
ctx.stroke();
const y = i*20;
ctx.moveTo(0, y);
ctx.lineTo(canvas.width, y);
ctx.stroke();
}
//draw images
const p = ctx.lineWidth / 1; //padding
for (let xCell = 0; xCell < map.length; xCell++) {
for (let yCell = 0; yCell < map[xCell].length; yCell++) {
let multiplier = ((map[xCell][yCell] % 4) ? 1 : 4);
const x = xCell * 20;
const y = yCell * 20;
const img = new Image();
if (map[xCell][yCell] === 1) {
img.onload = function() {
ctx.drawImage(img, y+p, x+p, multiplier * 20-p*2, multiplier * 20-p*2);
};
//TODO: set img.src to your api url instead of the dummyimage url.
img.src = `https://picsum.photos/id/${Math.floor(Math.random() * 10)}${Math.floor(Math.random() * 10)}/200/300`;
}else if(map[xCell][yCell] == 4){
img.onload = function() {
ctx.drawImage(img, y+p, x+p, multiplier * 20-p*2, multiplier * 20-p*2);
};
//TODO: set img.src to your api url instead of the dummyimage url.
img.src = `https://picsum.photos/id/${Math.floor(Math.random() * 12)}${Math.floor(Math.random() * 12)}/200/300`;
}
}
}
};
<canvas id="main" width="600" height="630"></canvas>
</canvas>

Related

Displaying an 11x11 Matrix on a Canvas

I've been trying to create a canvas that displays an 11x11 matrix.
const canvas = document.getElementById('canvasGame');
const context = canvas.getContext('2d');
context.scale(10, 10);
context.fillstyle = '#000';
context.fillstyle(0,0, canvas.width, canvas.height);
const matrix = [
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
];
Depending on the number inside the matrix it will create a rectangle of a certain colour.
I've created a basic function that goes through every entry.
if = 0, white rectangle.
else, black rectangle.
function drawMatrix(matrix){
matrix.forEach((row, y) =>{
row.forEach((value, x) => {
if(value === 0) {
context.fillStyle = 'white';
context.fillRect(x, y, 1, 1);
}
else
{
context.fillStyle = 'black';
context.fillRect(x, y, 1, 1);
}
});
});
}
drawMatrix(matrix);
However when I load my html file with my .js file and my canvas set-up it doesnt load anything apart from the styling I've applied to my canvas.
Screenshot: What it loads.
My HTML, if that matters.
<html>
<head>
<title>Testing Grounds</title>
<style>
body {
background: #345;
color: #fff;
font-family: sans-serif;
font-size: 2em;
text-align: center;
}
canvas {
border: dashed .2em #fff;
height: 90vh;
}
</style>
</head>
<body>
<h1>Test Zone</h1>
<p>Using a canvas to display 11x11 matrix</p>
<canvas id="canvasGame" width="350" height="350"/>
<script src="app.js"></script>
</body>
</html>
Here is also another way...
const canvas1 = document.getElementById('canvas1');
const context1 = canvas1.getContext('2d');
const canvas2 = document.getElementById('canvas2');
const context2 = canvas2.getContext('2d');
//context.scale(canvas.height / 16, canvas.height / 16);
matrix = [
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1],
[1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1],
[1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1],
[1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1],
[1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1],
[1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1],
[1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1],
[1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
];
function drawMatrix(matrix) {
z = matrix.map((c) => c.map((c) => c == 0 ? [255, 255, 255, 255] : [0, 0, 0, 255]));
i = new ImageData(Uint8ClampedArray.from(z.flat(2)), 12)
context1.putImageData(i, 0, 0);
context2.scale(16, 16);
context2.webkitImageSmoothingEnabled = false;
context2.mozImageSmoothingEnabled = false;
context2.imageSmoothingEnabled = false;
context2.drawImage(canvas1, 0, 0);
}
drawMatrix(matrix);
<center><canvas hidden id="canvas1" width=12 height=12></canvas></center>
<center><canvas id="canvas2" width=192 height=192></canvas></center>
const canvas = document.getElementById('canvas');
const context = canvas.getContext('2d');
// every drawn pixel will be 16 times bigger
context.scale(canvas.height / 16, canvas.height / 16);
const matrix = [
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1],
[1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1],
[1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1],
[1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1],
[1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1],
[1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1],
[1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1],
[1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
];
function drawMatrix(matrix) {
// write all pixels cycling thru rows and columns
matrix.forEach((row, y) => {
row.forEach((value, x) => {
context.fillStyle = value && "black" || "white";
context.fillRect(x, y, 1, 1);
});
});
}
drawMatrix(matrix); // draw the matrix
<center><canvas id="canvas" width=192 height=192></canvas></center>
The rectangles you're creating are 1 by 1 pixel and always in the upper-left. You should calculate the width/height of the rectangle (width / 11, height / 11). Then translate the x and width using those values. Something like the following should work:
function drawMatrix(matrix){
var cellWidth = canvas.width / 11.0;
var cellHeight = vanvas.height / 11.0;
matrix.forEach((row, y) =>{
row.forEach((value, x) => {
context.fillStyle = cellColor(value);
context.fillRect(x * cellWidth, y * cellHeight, cellWidth, cellHeight);
});
});
}
function cellColor(val) {
if(value == 0)
{
return 'white';
}
return 'black';
}
drawMatrix(matrix);
This will calculate the width and height of the cell, loop through each element, and draw the rectangle with either white or black depending on the value.
You should also make sure that the drawMatrix function is called after the body is loaded.

Animation in Visual 2D Arrays

I'm currently making a Pacman ghost with visual 2D arrays. I want the ghost to move like this:
I think I would use CSS transitions, but I'm not sure how! If there's a way to do it in javascript, that would be just as helpful. Here is my code:
<!doctype html>
<html>
<head>
<title>Visualizing 2D arrays</title>
<style>
#stage {
position:relative;
}
.cell {
position:absolute;
width:30px;
height:30px;
border:1px solid black;
background-color:black;
}
</style>
</head>
<body>
<div id="stage"></div>
<script>
// GRAB A REFERENCE TO THE STAGE
var stage = document.querySelector("#stage");
// THE 2D ARRAY THAT DEFINES THE PATTERN
var pattern = [
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0],
[0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0],
[0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0],
[0,0,1,2,2,1,1,1,1,2,2,1,1,1,0,0],
[0,0,2,2,2,2,1,1,2,2,2,2,1,1,0,0],
[0,0,3,3,2,2,1,1,3,3,2,2,1,1,0,0],
[0,1,3,3,2,2,1,1,3,3,2,2,1,1,1,0],
[0,1,1,2,2,1,1,1,1,2,2,1,1,1,1,0],
[0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0],
[0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0],
[0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0],
[0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0],
[0,1,1,0,1,1,1,0,0,1,1,1,0,1,1,0],
[0,1,0,0,0,1,1,0,0,1,1,0,0,0,1,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
];
// THE SIZE OF EACH CELL
var SIZE = 30;
// THE SPACE BETWEEN EACH CELL
var SPACE = 10;
// DISPLAY THE ARRAY
var ROWS = pattern.length;
var COLS = pattern[0].length;
// CREATE THE DIVS and POSITION THEM IN THE STAGE... BUT DON'T WORRY ABOUT COLORING THEM HERE!!!!
for (var row = 0; row < ROWS; row++) {
for (var col = 0; col < COLS; col++) {
// CREATE A DIV HTML ELEMENT CALLED CELL
var cell = document.createElement("div");
// SET ITS CSS CLASS TO CELL
cell.setAttribute("class", "cell");
// GIVE EACH OF THE CREATED DIVS A UNIQUE ID
// BASED ON THE ROW# AND COL#
// EXAMPLE : <div id="c-1-2" class="cell"></div>
// In this example, row = 1 and col = 2
cell.setAttribute("id", "c-" + row + "-" + col);
// ADD A CLICK HANDLER TO EACH OF THE INDIVIDUAL DIVS
cell.addEventListener("click", cellClick, false);
// ADD THE DIV HTML ELEMENT TO THE STAGE
stage.appendChild(cell);
// POSITION THE CELL IN THE CORRECT PLACE
// WITH 10 PIXELS OF SPACE AROUND IT
cell.style.top = row * (SIZE + SPACE) + "px";
cell.style.left = col * (SIZE + SPACE) + "px";
}
}
colorizeDivs();
function cellClick() {
// RIP APART THE DIV ID THAT WAS CLICKED ON
// WERE HIDING THE ROW AND COLUMN IN THE ID
// THE FORMAT OF THE ID IS "C-ROW#-COL#"
// EXAMPLE : <div id="c-1-2" class="cell"></div>
// In this example, row = 1 and col = 2
var zpos;
// THE "this" KEYWORD RETURNS THE HTML ELEMENT THAT WAS CLICKED ON
var thisid = this.id;
zpos = thisid.indexOf("-");
thisid = thisid.substr(zpos+1);
zpos = thisid.indexOf("-");
var thisRow = thisid.substr(0,zpos);
var thisCol = thisid.substr(zpos+1);
// now that we have the row and column for this div... change the array
pattern[thisRow][thisCol] += 1;
if (pattern[thisRow][thisCol] > 7) {
pattern[thisRow][thisCol] = 0;
}
colorizeDivs();
}
function colorizeDivs() {
for (var row = 0; row < ROWS; row++) {
for (var col = 0; col < COLS; col++) {
var cell = document.querySelector("#c-" + row + "-" + col);
if (pattern[row][col] === 0) {
cell.style.backgroundColor = "black";
} else if (pattern[row][col] === 1) {
cell.style.backgroundColor = "orange";
} else if (pattern[row][col] === 2) {
cell.style.backgroundColor = "white";
} else if (pattern[row][col] === 3) {
cell.style.backgroundColor = "blue";
} else if(pattern[row][col] === 4) {
cell.style.backgroundColor = "green";
} else if(pattern[row][col] === 5) {
cell.style.backgroundColor = "red";
} else if(pattern[row][col] === 6) {
cell.style.backgroundColor = "lightblue";
} else if(pattern[row][col] === 7) {
cell.style.backgroundColor = "pink";
}
}
}
}
</script>
</body>
</html>
Thank you!
I don't believe you would be able to use CSS transitions in this case. Transitions are typically applied to an entire HTML element, while you are attempting to selectively change certain colours of the ghost. I have created a solution to your problem in this fiddle. What this solution does is add another visual pattern representing the second frame of the ghost movement, as shown below:
var patterns = [
[
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0],
[0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0],
[0, 0, 1, 2, 2, 1, 1, 1, 1, 2, 2, 1, 1, 1, 0, 0],
[0, 0, 2, 2, 2, 2, 1, 1, 2, 2, 2, 2, 1, 1, 0, 0],
[0, 0, 3, 3, 2, 2, 1, 1, 3, 3, 2, 2, 1, 1, 0, 0],
[0, 1, 3, 3, 2, 2, 1, 1, 3, 3, 2, 2, 1, 1, 1, 0],
[0, 1, 1, 2, 2, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
[0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0],
[0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
],
[
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0],
[0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0],
[0, 0, 1, 2, 2, 1, 1, 1, 1, 2, 2, 1, 1, 1, 0, 0],
[0, 0, 2, 2, 2, 2, 1, 1, 2, 2, 2, 2, 1, 1, 0, 0],
[0, 0, 3, 3, 2, 2, 1, 1, 3, 3, 2, 2, 1, 1, 0, 0],
[0, 1, 3, 3, 2, 2, 1, 1, 3, 3, 2, 2, 1, 1, 1, 0],
[0, 1, 1, 2, 2, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
[0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0],
[0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
]
];
The next important aspect for creating this animation is to create a render loop with the requestAnimationFrame function. This is simply used to run the render function as fast as possible,
var startTime = Date.now();
var SPEED = 5; // Runs 5 times per second
function render() {
requestAnimationFrame(render.bind(this));
var elapsed = (Date.now() - startTime) / 1000;
var index = Math.floor((elapsed * SPEED) % patterns.length);
colorizeDivs(patterns[index]);
}
render();
So what the render function first does is calculate how long the animation has run for in seconds, which is also known as the elapsed time. This value can then be used to retrieve the appropriate index from the patterns defined above. This calculation simply applies the floor and modulo operation so that the index is always from the bound 0 to patterns.length - 1. After the index is retrieved, the current pattern is passed into the colorizeDivs function so the cells can be updated.

detecting for off bounded squares on grid

I'm going through a tutorial, and apparently I'm suppose to be getting this. But I can't find wrong in the coding part, I'm going step by step as well. I'm geting nothing, but before I had a block falling from above. Apparently I'm suppose to be getting something like
var WIDTH = 300,
HEIGHT = 400,
c = document.getElementById('canvas'),
ctx = c.getContext('2d');
setInterval(function() {
clearCanvas();
generateNextSquare();
updatePosition();
drawOnCanvas();
}, 1000 / 50);
var clearCanvas = function() {
ctx.fillStyle = 'White';
ctx.beginPath();
ctx.rect(0, 0, WIDTH, HEIGHT);
ctx.closePath();
ctx.fill();
}
var drawLine = function() {
ctx.beginPath();
ctx.moveTo(200, 0);
ctx.lineTo(200, 400);
ctx.stroke();
}
var updatePosition = function() {
_square.fall();
}
var drawOnCanvas = function() {
drawLine();
_square.draw();
}
var squareLength = 20;
var speedLevels = [20, 16, 12, 10, 8],
currSpeed = speedLevels[0];
var gameGrid = [
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
];
var Square = function(speed) {
var self = this;
self.color = "Black";
self.vPosition = 0;
self.hPosition = 4;
self.speed = speed;
self.current = 0 ;
self.temp = 0;
self.fall = function() {
if (self.counter >= self.speed) {
if (self.checkFalling()) {
self.vPosition++;
} else {
gameGrid[self.vPosition][self.hPosition] = 1;
self.active = false;
}
self.counter = 0;
}
self.counter++;
}
self.checkFalling = function() {
if (gameGrid[self.vPosition + 1][self.hPosition] == 1)
return false;
else
return true;
}
}
return self;
}
var drawFixedSquares = function() {
for (var i = 0; i < 20; i++) {
for (var j = 0; j < 10; j++) {
if (gameGrid[i][j] == 1) {
ctx.fillStyle = "Black";
ctx.fillRect(j * squareLength, i * squareLength, squareLength, squareLength);
}
}
}
}
var generateNextSquare = function() {
if (!_square.active)
_square = new Square(currSpeed);
}
var _square = new Square(currSpeed);

How can I load these images

Though I have been able to create and use animations for small demos in the past using the CreateJS library, i'm currently stumped in trying to understand why Nothing is displaying to the canvas even though I have verified that my animation tiles are all being added. I'm going senile from stepping through the debugger and seeing everything work properly, but getting a completely blank page on reloads.
Any ideas are much appreciated!
var stage, loader;
function tick() {
stage.update();
}
function generateMap(rekeyed_array, spriteSheet, row_length, col_length, tilesize) {
for (var x = 0; x < col_length; x++) {
for (var y = 0; y < row_length; y++) {
// z is a onedimentional value mapped from x and y iterators
spriteInstance = new createjs.Sprite(spriteSheet, rekeyed_array[z]);
var z = x * row_length + y;
spriteInstance.x = tilesize * x;
spriteInstance.y = tilesize * y;
spriteInstance.gotoAndPlay(rekeyed_array[z]);
spriteInstance = spriteInstance.clone();
stage.addChild(spriteInstance);
}
}
console.groupEnd();
stage.update();
}
//Replace Tiled's map data numbers with the actual Game Object's Names
function rekeyTiledMapData(spritemappings, array_to_reindex, rows, cols) {
var reindexedArray = new Array();
for (var y = 0; y < cols; y++) {
for (var x = 0; x < rows; x++) {
// z is a onedimentional value mapped from x and y iterators
var z = y * rows + x;
var currentItem = array_to_reindex[z];
if (typeof spritemappings[currentItem] === "string") {
reindexedArray.push(spritemappings[currentItem]);
} else {
reindexedArray.push(currentItem);
}
}
}
return reindexedArray;
}
function getSpriteData(loadedimg) {
var data = {
framerate: 60,
images: [loadedimg],
frames: [
/*middlearth*/
[592, 654, 70, 70, 0, 0, 0],
/*water*/
[562, 434, 70, 70, 0, 0, 0],
/*doormid*/
[146, 290, 70, 70, 0, 0, 0],
/*doortop*/
[218, 290, 70, 70, 0, 0, 0],
/*grass*/
[736, 362, 70, 70, 0, 0, 0]
],
animations: {
"sand": {
frames: 0,
next: "sand",
speed: 1
},
"water": {
frames: 1,
next: "water",
speed: 1
},
"doormid": [2, 2, "doormid", 1],
"doortop": [3, 3, "doortop", 1],
"grass": [4, 4, "grass", 1],
}
};
return data;
}
var mapdata = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 7, 7, 7, 6, 6, 6, 6, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5];
var spritemappings = {
'0': "water",
'8': "water",
'7': "water",
'5': "sand",
'6': "grass",
'10': "doortop",
'11': "doormid"
};
function loadLevel(event) {
var tileimage = loader.getResult("tiles");
levelanimations = rekeyTiledMapData(spritemappings, mapdata, 16, 10);
var spritesheet = new createjs.SpriteSheet(getSpriteData(tileimage));
generateMap(levelanimations, spritesheet, 16, 10, 70);
}
function init() {
stage = new createjs.Stage("gameCanvas");
createjs.Ticker.on("tick", tick);
createjs.Ticker.setFPS(60);
loader = new createjs.LoadQueue(false);
loader.addEventListener("complete", loadLevel)
loader.loadManifest({
id: "tiles",
src: "assets/images/level_tiles.png"
});
}
init();
</script>
</head>
<body>
<canvas id="gameCanvas" width="1600" height="900"></canvas>
</body>
If that script is a copy and paste, the script in the head is executed before the body is being created. Thats why you are seeing that it is executing correctly, but nothing is actually happening.
I would copy and paste the script tag in the header down below the canvas element.

Multidimensional array fill

I'm trying to fill an area in a multidimensional array and not sure on the approach.
For example I have the following array:
var map = [
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 2, 2, 2, 2, 2, 2, 0, 0],
[0, 2, 0, 0, 0, 0, 2, 0, 0],
[0, 2, 0, 2, 0, 0, 2, 0, 0],
[0, 2, 0, 0, 2, 0, 2, 0, 0],
[0, 0, 2, 0, 0, 0, 2, 0, 0],
[0, 0, 0, 2, 2, 2, 2, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0]
];
And then I am trying to get the number from X and Y position and fill all those numbers (which is 0) with a number given such as 1, which will result in the following array:
var map = [
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 2, 2, 2, 2, 2, 2, 0, 0],
[0, 2, 1, 1, 1, 1, 2, 0, 0],
[0, 2, 1, 2, 1, 1, 2, 0, 0],
[0, 2, 1, 1, 2, 1, 2, 0, 0],
[0, 0, 2, 1, 1, 1, 2, 0, 0],
[0, 0, 0, 2, 2, 2, 2, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0]
];
Basically just replacing all numbers next to each other (0) with (1) within that area.
What is the correct way to do this with JavaScript?
Assuming you're given a starting position and you want to then fill all neighboring values up/down, left/right that contain the same value, you can do something like this:
var map = [
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 2, 2, 2, 2, 2, 2, 0, 0],
[0, 2, 0, 0, 0, 0, 2, 0, 0],
[0, 2, 0, 2, 0, 0, 2, 0, 0],
[0, 2, 0, 0, 2, 0, 2, 0, 0],
[0, 0, 2, 0, 0, 0, 2, 0, 0],
[0, 0, 0, 2, 2, 2, 2, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0]
];
function fill(data, x, y, newValue) {
// get target value
var target = data[x][y];
function flow(x,y) {
// bounds check what we were passed
if (x >= 0 && x < data.length && y >= 0 && y < data[x].length) {
if (data[x][y] === target) {
data[x][y] = newValue;
flow(x-1, y); // check up
flow(x+1, y); // check down
flow(x, y-1); // check left
flow(x, y+1); // check right
}
}
}
flow(x,y);
}
fill(map, 2, 2, 1);
Working demo: http://jsfiddle.net/jfriend00/C83AT/
Here's a version that doesn't use recursion and appears to work with large data sets. Your large test data set wasn't a very interesting test pattern so I wouldn't say this is tested conclusively, but it seems to work on both the small and large data set:
Large data example: http://jsfiddle.net/jfriend00/8mrhN/
Small data example: http://jsfiddle.net/jfriend00/BFTub/ (easier to see the result)
function fill(data, x, y, newValue) {
// get target value
var target = data[x][y];
// maintain list of cells to process
// put the starting cell in the queue
var queue = [{x:x, y:y}], item;
while (queue.length) {
item = queue.shift();
x = item.x;
y = item.y;
if (data[x][y] === target) {
data[x][y] = newValue;
// up
if (x > 0) {
queue.push({x:x-1, y:y})
}
// down
if (x + 1 < data.length) {
queue.push({x:x+1, y:y})
}
// left
if (y > 0) {
queue.push({x:x, y:y-1});
}
// right
if (y + 1 < data[x].length) {
queue.push({x:x, y:y+1});
}
}
}
}
This could be optimized further for performance by testing the value before putting it in the queue and by following a given direction until you find a non-matching value, if required.
This is an alternative implementation (queue-based) roughly translated, no optimisations performed. There are also others.
Javascript
var map = [
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 2, 2, 2, 2, 2, 2, 0, 0],
[0, 2, 0, 0, 0, 0, 2, 0, 0],
[0, 2, 0, 2, 0, 0, 2, 0, 0],
[0, 2, 0, 0, 2, 0, 2, 0, 0],
[0, 0, 2, 0, 0, 0, 2, 0, 0],
[0, 0, 0, 2, 2, 2, 2, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0]
];
/*
1. Set Q to the empty queue.
2. If the color of node is not equal to target-color, return.
3. Add node to Q.
4. For each element N of Q:
5. If the color of N is equal to target-color:
6. Set w and e equal to N.
7. Move w to the west until the color of the node to the west of w no longer matches target-color.
8. Move e to the east until the color of the node to the east of e no longer matches target-color.
9. For each node n between w and e:
10. Set the color of n to replacement-color.
11. If the color of the node to the north of n is target-color, add that node to Q.
12. If the color of the node to the south of n is target-color, add that node to Q.
13. Continue looping until Q is exhausted.
14. Return.
*/
function floodFill(data, node, targetValue, replacementValue) {
var Q;
if (data[node[0]][node[1]] === targetValue) {
Q = [node];
while (Q.length) {
var N = Q.shift(),
value,
index,
n,
e,
s,
w;
if (data.hasOwnProperty([N[0]]) && data[N[0]][N[1]] === targetValue) {
w = e = N[0];
do {
w -= 1;
} while (data.hasOwnProperty(w) && data[w][N[1]] === targetValue);
do {
e += 1;
} while (data.hasOwnProperty(e) && data[e][N[1]] === targetValue);
n = N[1] - 1;
s = N[1] + 1;
for (index = w + 1; index < e; index += 1) {
data[index][N[1]] = replacementValue;
if (data[index].hasOwnProperty(n) && data[index][n] === targetValue) {
Q.push([index, n]);
}
if (data[index].hasOwnProperty(s) && data[index][s] === targetValue) {
Q.push([index, s]);
}
}
}
}
}
}
floodFill(map, [2, 2], 0, 1);
map.forEach(function (m) {
console.log(JSON.stringify(m));
});
Output
[0,0,0,0,0,0,0,0,0]
[0,2,2,2,2,2,2,0,0]
[0,2,1,1,1,1,2,0,0]
[0,2,1,2,1,1,2,0,0]
[0,2,1,1,2,1,2,0,0]
[0,0,2,1,1,1,2,0,0]
[0,0,0,2,2,2,2,0,0]
[0,0,0,0,0,0,0,0,0]
On jsFiddle

Categories

Resources