I am trying to code a Conway Game of Life app in a canvas on a webpage. However after writing the code, I cannot get the cells to appear on my canvas element. Can you tell what I am doing wrong?
My guess is that is has to do with the functions for the buttons - the functions I wrote are supposed to initialize the game, but it does not happen.
<!DOCTYPE html>
<html>
<head>
<script>
var CONWAY = (function() {
var my = {};
var Board = function() { //create a game board object
this.cells = {}; //collection object to hold cell coordinates
};
var Cell = function(x, y, alive) { //create a cell object
this.x = x; //the x coordinate for the cell
this.y = y; //the y coordinate for the cell
this.alive = alive; //state to show if the cell is alive or dead
};
Cell.prototype = { //add isAlive method to the cell object
isAlive: function() {
return this.alive; //return cell's state
}
};
Board.prototype = {
addCell: function(cell) { //the method to add a cell to the board
this.cells[getCellRepresentation(cell.x, cell.y)] = cell;
},
getCellAt: function(x,y) { //returns the value of the specified coordinates
return this.cells[getCellRepresentation(x,y)];
},
getAliveNeighbors: function(cell) { //check nearby cells for states according to the rules
var x = cell.x;
var y = cell.y;
var aliveCells = 0;
for(var i = -1; i < 2; i++) { //look for live cells in the vicinity
for(var j = -1; j < 2; j++) {
if(i === 0 && i === j) {
continue;
}
var currentCell = this.getCellAt(x + i, y + j);
if(currentCell && currentCell.isAlive()) {
aliveCells++;
}
}
}
return aliveCells; //return the number of live cells
},
calculateNextState: function(cell) {
var tempCell = new Cell(cell.x, cell.y, cell.alive);
var livingNeighbors = this.getAliveNeighbors(cell);
if(cell.alive) {
if(livingNeighbors === 2 || livingNeighbors === 3) {
tempCell.alive = true;
}
else {
tempCell.alive = false;
}
}
else {
if(livingNeighbors === 3) {
tempCell.alive = true;
}
}
return tempCell;
},
step: function() {
var cells = this.cells;
var tempBoard = {};
for(var c in this.cells) {
var cell = this.cells[c];
var newCell = this.calculateNextState(cell);
tempBoard[c] = newCell;
}
this.cells = tempBoard;
}
};
function getCellPrepresentation (x,y) {
return "x" + x + "y" + y;
}
var CONWAY_GLOB_MAX_X = 99;
var CONWAY_GLOB_MAX_Y = 23;
var randomInit = function(board) {
for (var y = 0; y <= CONWAY_GLOB_MAX_Y; y++) {
for (var x = 0; x <= CONWAY_GLOB_MAX_X; x++) {
board.addCell(new Cell(x, y, (Math.random() > 0.8)));
}
}
};
var draw = function(board, canvas) {
var output = ' ';
for(var y = 0; y <= CONWAY_GLOB_MAX_X; y++) {
for(var x = 0; x <= CONWAY_GLOB_MAX_Y; x++) {
var cell = board.getCellAt(x,y);
output += cell && cell.isAlive() ? 'o' : ' ';
}
output += '\n\
';
}
canvas.html(output);
};
var doConway = function(body) {
var board = new Board(); //create new board
randomInit(board); //initialize board with random dead and alive cells
draw(board, body); //draw the board
return setInterval(function() { //update every 130 ms
board.step();
draw(board, body);
}, 130);
};
my.initConway = function(id, body) {
clearInterval(id);
return doConway(body);
};
my.resetConway = function(id, body) {
body.empty();
clearInterval(id);
};
var conwayRandIntervalId = null;
var conwayBody = getElementbyId('conway');
var resetGame = function() {
this.resetConway(conwayRandIntervalId, conwayBody);
};
var startGame = function() {
conwayRandIntervalId = this.initConway(conwayRandIntervalId, conwayBody);
}
return my;
}());
</script>
<style type="text/css">
div.conway {
overflow: hidden;
font-family: courier;
float: left;
width: 500px;
height: 288px;
background-color: #000;
font-size: 10px;
color: #fff;
}
</style>
</head>
<body>
<div>
<button id='startCW' onclick="startGame();">Random Conway's Game of Life</button> | <button id='resetCW' onclick="resetGame();">Reset</button>
<div class='conway' id='conway'></div>
</div>
</body>
</html>
I had a little bit of time, so I took the liberty of fixing this application.
The initial problem is you were not exposing the function startGame properly, the button has no way to trigger it. I exposed it through your CONWAY object by setting it on my. Then you were missing the document part of document.getElementById. Next you were trying to call getCellRepresentation, but it should have been getCellPrepresentation.
The next issue was related to having your script execute before the dom has been loaded. There are a few ways around this, such as using jquerys document load function and placing your logic in there, but the easiest way was to just put your script tag as the LAST tag in your body. As the browser iterates through the html, its going to build the dom elements, and then execute your code, which allows your document.getElementById to return the proper element. Doing this before the dom loads will always return null.
I changed your canvas.html(output) to canvas.innerHTML=output, as this was a dependency on jquery that I did not have on my machine. Either way is acceptable.
Finally I cleaned up your output. In your version the output was falling all on one line because you were doing things such as using spaces and \n's. Since we were in an html block I changed the output to 's and <br />'s. Another solution would be to use a <pre> element instead of a div, but I haven't tested this.
I may have gone a little too far with the help, so sorry if you were just looking for the reason there was no output. Post a comment if you have any questions :-)
<!DOCTYPE html>
<html>
<head>
<style type="text/css">
div.conway {
overflow: hidden;
font-family: courier;
float: left;
width: 500px;
height: 288px;
background-color: #000;
font-size: 10px;
color: #fff;
}
</style>
</head>
<body>
<div>
<button id='startCW' onclick="CONWAY.startGame();">Random Conway's Game of Life</button> | <button id='resetCW' onclick=" CONWAY.resetGame();">Reset</button>
<div class='conway' id='conway'></div>
</div>
<script type="text/javascript">
var CONWAY = (function () {
var my = {};
var Board = function () { //create a game board object
this.cells = {}; //collection object to hold cell coordinates
};
var Cell = function (x, y, alive) { //create a cell object
this.x = x; //the x coordinate for the cell
this.y = y; //the y coordinate for the cell
this.alive = alive; //state to show if the cell is alive or dead
};
Cell.prototype = { //add isAlive method to the cell object
isAlive: function () {
return this.alive; //return cell's state
}
};
Board.prototype = {
addCell: function (cell) { //the method to add a cell to the board
this.cells[getCellPrepresentation(cell.x, cell.y)] = cell;
},
getCellAt: function (x, y) { //returns the value of the specified coordinates
return this.cells[getCellPrepresentation(x, y)];
},
getAliveNeighbors: function (cell) { //check nearby cells for states according to the rules
var x = cell.x;
var y = cell.y;
var aliveCells = 0;
for (var i = -1; i < 2; i++) { //look for live cells in the vicinity
for (var j = -1; j < 2; j++) {
if (i === 0 && i === j) {
continue;
}
var currentCell = this.getCellAt(x + i, y + j);
if (currentCell && currentCell.isAlive()) {
aliveCells++;
}
}
}
return aliveCells; //return the number of live cells
},
calculateNextState: function (cell) {
var tempCell = new Cell(cell.x, cell.y, cell.alive);
var livingNeighbors = this.getAliveNeighbors(cell);
if (cell.alive) {
if (livingNeighbors === 2 || livingNeighbors === 3) {
tempCell.alive = true;
}
else {
tempCell.alive = false;
}
}
else {
if (livingNeighbors === 3) {
tempCell.alive = true;
}
}
return tempCell;
},
step: function () {
var cells = this.cells;
var tempBoard = {};
for (var c in this.cells) {
var cell = this.cells[c];
var newCell = this.calculateNextState(cell);
tempBoard[c] = newCell;
}
this.cells = tempBoard;
}
};
function getCellPrepresentation(x, y) {
return "x" + x + "y" + y;
}
var CONWAY_GLOB_MAX_X = 99;
var CONWAY_GLOB_MAX_Y = 23;
var randomInit = function (board) {
for (var y = 0; y <= CONWAY_GLOB_MAX_Y; y++) {
for (var x = 0; x <= CONWAY_GLOB_MAX_X; x++) {
board.addCell(new Cell(x, y, (Math.random() > 0.8)));
}
}
};
var draw = function (board, canvas) {
var output = ' ';
for (var y = 0; y <= CONWAY_GLOB_MAX_X; y++) {
for (var x = 0; x <= CONWAY_GLOB_MAX_Y; x++) {
var cell = board.getCellAt(x, y);
output += cell && cell.isAlive() ? 'o' : ' ';
}
output += '<br/>';
}
canvas.innerHTML=(output);
};
var doConway = function (body) {
var board = new Board(); //create new board
randomInit(board); //initialize board with random dead and alive cells
draw(board, body); //draw the board
return setInterval(function () { //update every 130 ms
board.step();
draw(board, body);
}, 130);
};
my.initConway = function (id, body) {
clearInterval(id);
return doConway(body);
};
my.resetConway = function (id, body) {
body.empty();
clearInterval(id);
};
var conwayRandIntervalId = null;
var conwayBody = document.getElementById('conway');
my.resetGame = function () {
this.resetConway(conwayRandIntervalId, conwayBody);
};
my.startGame = function () {
conwayRandIntervalId = this.initConway(conwayRandIntervalId, conwayBody);
}
return my;
}());
</script>
</body>
</html>
Related
This is the code of the paint bucket tool in my drawing app using the p5.js library. The function self.floodFill always get "Maximum Call Stack Size Exceeded" because of recursion and I want to know the way to fix it. I am thinking if changing the function to a no recursion function would help or not. Any help would be appreciated.
function BucketTool(){
var self = this;
//set an icon and a name for the object
self.icon = "assets/bucket.jpg";
self.name = "Bucket";
var d = pixelDensity();
var oldColor;
var searchDirections = [[1,0],[-1,0],[0,1],[0,-1]];
var pixelsToFill = [];
var positionArray = new Array(2);
self.checkBoundary = function(currentX, currentY, localOldColor) {
if (self.getPixelAtXYPosition(currentX,currentY).toString() != localOldColor.toString() || currentX < 0 || currentY < 0 || currentX > width || currentY > height || pixelsToFill.indexOf(currentX+" "+currentY) != -1) {
return false;
}
return true;
};
self.floodFill = function(currentX, currentY, localOldColor, localSearchDirections) {
if (self.checkBoundary(currentX, currentY, localOldColor)){
pixelsToFill.push(currentX+" "+currentY);
} else {
return;
}
for (var i = 0; i < searchDirections.length; i++){
self.floodFill(currentX + searchDirections[i][0], currentY + searchDirections[i][1], localOldColor, localSearchDirections);
}
};
self.getPixelAtXYPosition = function(x, y) {
var colour = [];
for (var i = 0; i < d; i++) {
for (var j = 0; j < d; j++) {
// loop over
index = 4 * ((y * d + j) * width * d + (x * d + i));
colour[0] = pixels[index];
colour[1] = pixels[index+1];
colour[2] = pixels[index+2];
colour[3] = pixels[index+3];
}
}
return colour;
}
self.drawTheNeededPixels = function(){
for(var i = 0; i < pixelsToFill.length; i++){
positionArray = pixelsToFill[i].split(" ");
point(positionArray[0],positionArray[1]);
}
}
self.draw = function () {
if(mouseIsPressed){
pixelsToFill = [];
loadPixels();
oldColor = self.getPixelAtXYPosition(mouseX, mouseY);
self.floodFill(mouseX, mouseY, oldColor, searchDirections);
self.drawTheNeededPixels();
}
};
}
This problem is well documented on the wikipedia page and the shortfalls of the different types of algorithms to perform flood filling. You've gone for the stack-based recursive implementation.
To prevent a stackoverflow — Maximum Call Stack Exceeded — the first step would be to use a data structure. Using queues/stacks rather than having the function call itself.
The code below creates an empty stack where we put a new object containing the x and y where the user has chosen to fill. This is then added to the pixelsToFill array. We then loop the stack until it's completely empty, at which point we are ready to display the filled pixels.
In the while loop we pop an element off the stack and then find its children — the directions up, down, left, right denoted by the searchDirections array you created. If we've not seen the child before and it's within the boundary we add it to the pixelsToFill array and add it to the stack to repeat the process:
self.floodFill = function (currentX, currentY, localOldColor, localSearchDirections) {
let stack = [];
stack.push({ x: currentX, y: currentY });
pixelsToFill.push(currentX + " " + currentY);
while (stack.length > 0) {
let current = stack.pop();
for (var i = 0; i < searchDirections.length; i++) {
let child = {
x: current.x + searchDirections[i][0],
y: current.y + searchDirections[i][1],
localOldColor,
};
if (self.checkBoundary(child.x, child.y, localOldColor)) {
pixelsToFill.push(child.x + " " + child.y);
stack.push(child);
}
}
}
};
This code may stop the stackoverflow but there are still a lot of optimisations that can be made. Once again, it's worth checking out the Wikipedia page and potentially take a look at Span filling.
let bucketTool;
function setup() {
createCanvas(400, 400);
bucketTool = new BucketTool();
}
function draw() {
background(220);
strokeWeight(5);
circle(width / 2, height / 2, 100);
frameRate(1);
bucketTool.draw();
}
function BucketTool() {
var self = this;
//set an icon and a name for the object
// self.icon = "assets/bucket.jpg";
// self.name = "Bucket";
var d = pixelDensity();
var oldColor;
var searchDirections = [
[1, 0],
[-1, 0],
[0, 1],
[0, -1],
];
var pixelsToFill = [];
var positionArray = new Array(2);
self.checkBoundary = function (currentX, currentY, localOldColor) {
if (
self.getPixelAtXYPosition(currentX, currentY).toString() !=
localOldColor.toString() ||
currentX < 0 ||
currentY < 0 ||
currentX > width ||
currentY > height ||
pixelsToFill.indexOf(currentX+" "+currentY) != -1
) {
return false;
}
return true;
};
self.floodFill = function (currentX, currentY, localOldColor, localSearchDirections) {
let stack = [];
stack.push({ x: currentX, y: currentY });
pixelsToFill.push(currentX + " " + currentY);
while (stack.length > 0) {
let current = stack.pop();
for (var i = 0; i < searchDirections.length; i++) {
let child = {
x: current.x + searchDirections[i][0],
y: current.y + searchDirections[i][1],
localOldColor,
};
if (self.checkBoundary(child.x, child.y, localOldColor)) {
pixelsToFill.push(child.x + " " + child.y);
stack.push(child);
}
}
}
};
self.getPixelAtXYPosition = function (x, y) {
var colour = [];
for (var i = 0; i < d; i++) {
for (var j = 0; j < d; j++) {
// loop over
index = 4 * ((y * d + j) * width * d + (x * d + i));
colour[0] = pixels[index];
colour[1] = pixels[index + 1];
colour[2] = pixels[index + 2];
colour[3] = pixels[index + 3];
}
}
return colour;
};
self.drawTheNeededPixels = function () {
for (var i = 0; i < pixelsToFill.length; i++) {
positionArray = pixelsToFill[i].split(" ");
point(positionArray[0], positionArray[1]);
}
};
self.draw = function () {
if (mouseIsPressed) {
pixelsToFill = [];
loadPixels();
oldColor = self.getPixelAtXYPosition(mouseX, mouseY);
self.floodFill(mouseX, mouseY, oldColor, searchDirections);
console.log(pixelsToFill.length);
self.drawTheNeededPixels();
}
};
}
html, body {
margin: 0;
padding: 0;
}
canvas {
display: block;
}
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.1/p5.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.1/addons/p5.sound.min.js"></script>
<link rel="stylesheet" type="text/css" href="style.css">
<meta charset="utf-8" />
</head>
<body>
<main>
</main>
<script src="sketch.js"></script>
</body>
</html>
Shameless plug, but relevant: I've created a blog comparing the different flood fill algorithms using p5.js.
I'm trying to make Tic-Tac-Toe for my school in pure JS and based on CSS and HTML GUI. I know it's probably not the best approach, but I thought it may work. Unfortunately, it doesn't work at all.
I'm trying to generate whole board by JS in Board.generate(), because I want to make my board resizeable, and then add event listeners to every generated cell, but this isn't working. Onclick is called only on the last element and not on the previous ones. I think I could bypass this problem by making a function, but I want to understand why my code doesn't work.
Here's my code:
class Board {
constructor(size) {
this.size = size;
this.cells = [];
for (let x = 0; x < size; x++) {
this.cells[x] = [];
for (let y = 0; y < size; y++) {
this.cells[x][y] = new Cell(x, y, null);
}
}
}
generate() {
document.getElementById('game').innerHTML = '<div id="board"></div>';
let boardEl = document.getElementById('board');
for (let y = 0; y < this.size; y++) {
boardEl.innerHTML += '<div id="r' + y + '" class="board-row"></div>';
let rowEl = document.getElementById('r' + y);
for (let x = 0; x < this.size; x++)
this.cells[x][y].addElement(rowEl);
}
}
}
class Cell {
constructor(x, y, mark) {
this.x = x;
this.y = y;
this.mark = mark;
this.id = 'c' + x + y;
this.element = null;
}
addElement(rowEl) {
rowEl.innerHTML += '<div id="' + this.id + '" class="board-cell"> CELL </div>';
this.element = document.getElementById(this.id);
this.element.addEventListener('click', () => this.clicked(), false);
}
clicked() {
console.log(this.x, this.y, ' CLICKED!');
}
}
window.onload = function () {
var game = new Board(3);
game.generate();
};
<style>
.board-cell {
width: 10%;
height: 10%;
background: white;
color:black;
}
</style>
<div id="container" class="fullscreen">
<div id="game" class="fullscreen">
<!-- Generated board -->
</div>
</div>
I know my code is a mess right now, but I was just frustrated, and it's only a sketch, I'll work on it.
Also, sorry for my English if I made some mistakes.
Any time you set the innerHTML property you are overwriting any previous HTML that was set there. This includes concatenation assignment, because
element.innerHTML += '<b>Hello</b>';
is the same as writing
element.innerHTML = element.innerHTML + '<b>Hello</b>';
This means all handlers not attached via HTML attributes will be "detached", since the elements they were attached to no longer exist, and a new set of elements has taken their place. To keep all your previous event handlers, you have to append elements without overwriting any previous HTML. The best way to do this is to use DOM creation functions such as createElement and appendChild:
let divEl = document.createElement("div");
divEl.innerHTML = "CELL";
divEl.className = 'board-cell'
divEl.id = this.id;
rowEl.appendChild(divEl);
The same is for row div elements:
let divRowEl = document.createElement("div");
divRowEl.className = 'board-row'
divRowEl.id = 'r' + y;
boardEl.appendChild(divRowEl);
class Board {
constructor(size) {
this.size = size;
this.cells = [];
for (let x = 0; x < size; x++) {
this.cells[x] = [];
for (let y = 0; y < size; y++) {
this.cells[x][y] = new Cell(x, y, null);
}
}
}
generate() {
document.getElementById('game').innerHTML = '<div id="board"></div>';
let boardEl = document.getElementById('board');
for (let y = 0; y < this.size; y++) {
let divRowEl = document.createElement("div");
divRowEl.className = 'board-row'
divRowEl.id = 'r' + y;
boardEl.appendChild(divRowEl);
let rowEl = document.getElementById('r' + y);
for (let x = 0; x < this.size; x++)
this.cells[x][y].addElement(rowEl);
}
}
}
class Cell {
constructor(x, y, mark) {
this.x = x;
this.y = y;
this.mark = mark;
this.id = 'c' + x + y;
this.element = null;
}
addElement(rowEl) {
let divEl = document.createElement("div");
divEl.innerHTML = "CELL" + this.id;
divEl.className = 'board-cell'
divEl.id = this.id;
divEl.addEventListener('click', () => this.clicked(), false);
rowEl.appendChild(divEl);
}
clicked() {
console.log(this.x, this.y, ' CLICKED!');
}
}
window.onload = function () {
var game = new Board(3);
game.generate();
};
<style>
.board-cell {
width: 10%;
height: 10%;
background: white;
color:black;
}
</style>
<div id="container" class="fullscreen">
<div id="game" class="fullscreen">
<!-- Generated board -->
</div>
</div>
For a fun little project I decided to program conway's game of life in javascript. My logic seems to make sense and each individual function does its job however I still dont get my intended result. I have an array called grid, which stores the value of all the cells, and if they are alive or dead. I check each individual cell, then check all 8 surrounding cells to count neighbors, repeat for every other cell. At some point my grid no longer stores the correct value and resets. At this point I'm starting to think it's a javascript problem.
<body>
<style>
* {
padding: 0;
margin: 0;
}
body {
overflow: hidden;
}
canvas {
background: #FFFFFF;
display: block;
margin: 0 auto;
}
</style>
<canvas id="canvas" style="border:1px solid #000000;"></canvas>
</body>
<script>
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var Game = {};
var nextGrid, emptyGrid, grid;
Game.horCells = 30;
Game.cellSize = canvas.width / Game.horCells;
Game.verCells = Math.floor(canvas.height / Game.cellSize);
Game.startLives = 80;
config();
//setInterval(run, 1000);
function config() {
console.log("in config");
emptyGrid = newGrid(Game.horCells, Game.verCells);
grid = emptyGrid;
nextGrid = emptyGrid;
//Manual Setup
for (var i = 0; i < Game.startLives; i++) {
//grid[getRandomInt(0, Game.horCells - 1)][getRandomInt(0, Game.verCells - 1)] = true;
}
grid[0][3] = true;
grid[1][3] = true;
grid[2][3] = true;
}
function run() {
console.log("gread" + grid[3][3]);
draw();
update();
}
function draw() {
console.log("Draw");
ctx.fillStyle = "#FFFFFF";
ctx.fillRect(0, 0, canvas.width, canvas.height);
for (var i = 0; i < Game.horCells; i++) {
for (var j = 0; j < Game.verCells; j++) {
if (grid[i][j] === false) {
ctx.fillStyle = "#FFFFFF";
} else if (grid[i][j] === true) {
console.log("drawing live");
ctx.fillStyle = "#000000";
}
ctx.fillRect(i * Game.cellSize, j * Game.cellSize, Game.cellSize, Game.cellSize);
}
}
}
function update() {
for (var p = 0; p < Game.horCells; p++) {
for (var k = 0; k < Game.verCells; k++) {
nextGrid[p][k] = survival(p, k);
}
}
}
function survival(x, y) {
var neighbours = 0;
for (var l = 0; l < 3; l++) {
for (var m = 0; m < 3; m++) {
var sx = (x - 1) + l;
var sy = (y - 1) + m;
//Check bounds
if (inBounds(sx, sy) === true && grid[sx][sy]) {
neighbours++;
}
}
}
if (grid[x][y]) {
neighbours--;
if (neighbours === 2 || neighbours === 3) {
return true;
} else if (neighbours < 2 || neighbours > 3) {
console.log("DIED");
return false;
}
} else if (grid[x][y] === false && neighbours === 3) {
return true;
} else {
console.log("DIED");
return false;
}
}
function inBounds(x, y) {
return (x >= 0 && x < Game.horCells && y >= 0 && y < Game.horCells);
}
function newGrid(xCells, yCells) {
var gridd = new Array(xCells);
for (var i = 0; i < xCells; i++) {
gridd[i] = new Array(yCells);
}
for (var j = 0; j < xCells; j++) {
for (var k = 0; k < yCells; k++) {
gridd[j][k] = false;
}
}
return gridd;
}
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
</script>
emptyGrid = newGrid(Game.horCells, Game.verCells);
grid = emptyGrid;
nextGrid = emptyGrid
Creates 1 grid, then assigns grid and nextGrid to the same object. Again, grid and nextGrid are the same object. Any changes you make to 1 will happen to the other.
Create 2 separate grids so they can be changed independently:
grid = newGrid(Game.horCells, Game.verCells);
nextGrid = newGrid(Game.horCells, Game.verCells);
Or, to neaten it up slightly:
var g = () => newGrid(Game.horCells, Game.verCells);
grid = g();
nextGrid = g();
Just to clear up the question in the comments:
var g = () => newGrid(Game.horCells, Game.verCells);
Is (basically) the same thing as:
var g = function() {
return newGrid(Game.horCells, Game.verCells);
}
It's known as a "fat-arrow" function.
Both bits do the same thing though: they create a function that returns a new grid. This has the benefit of not needing to write newGrid(Game.horCells, Game.verCells); twice.
I used an arrow function instead of the function keyword because the latter is giant and ugly, which takes away from the goal of cleaning up the code.
This js is to create "lyricLine" and showing strings on random position of the page.
While creating an object of oLyricLine and calling drawText, it supposed to display each string of array lyricLines one by one until last line is shown. When last line is time-out, the timer should stop and the created div.lyricline should be deleted.
While different objects are created, (in this case LyricGen1 and LyricGen2) their timer should be ticking simultaneously.
But currently only timer of LyricGen2 is working.
Code is attached below.
<script src="jquery-min.js"></script>
<div id="container">
<div class="lyricline" id="0">
demo
</div>
</div>
<style>
body{
background: #000;
color: #fff;
font-family: sans-serif;
}
#container{
position: relative;
width: 100%;
height: 100%;
}
.lyricline{
position: absolute;
text-align: center;
}
</style>
<script>
var idarray = [];
//Object oLyricLine
function oLyricLine(obj){
this.lyricLines = [];
this.textSize = 30;
this.toppx = -1;
this.leftpx = -1;
this.unixtime = -1;
this.widthpx = -1;
this.colorhex = "#fff";
this.obj = obj;
}
oLyricLine.prototype.drawText = function(){
if (this.toppx != -1 && this.leftpx != -1 && this.unixtime != -1 && this.widthpx != -1){
var transitionInTime = 10;
var transitionOutTime = 10;
var tickfactor = 5;
$("#"+this.unixtime).css({
"color": this.colorhex,
"left": this.leftpx,
"top": this.toppx,
"width": this.widthpx,
"font-size":this.textSize
}).attr({"class":"lyricline","id":this.unixtime}).text(this.lyricLines[0]);
var tickCount = 0;
lyricLinesCount = 0;
var lyricLinesTick = [];
for (var i =0; i <= this.lyricLines.length - 1; i++) {
lyricLinesTick[i] = this.lyricLines[i].length * tickfactor;
if(i>0){lyricLinesTick[i]+=lyricLinesTick[i-1];}
};
var nLyricLines = this.lyricLines;
var nUnixtime = this.unixtime;
idarray[nUnixtime] = setInterval(function () {
tickCount += 1;
if (tickCount == lyricLinesTick[lyricLinesCount]){
lyricLinesCount +=1;
if(lyricLinesCount != lyricLinesTick.length){
$("#"+nUnixtime).text(nLyricLines[lyricLinesCount]);
}else{
$("#"+nUnixtime).remove();
clearInterval(idarray[nUnixtime]);
}
}
},100);
}
};
oLyricLine.prototype.widthGen = function() {
this.widthpx = maxWidth(this.lyricLines, this.textSize);
};
var unixtime1=Date.now();
$("#container").append($("<div></div>").attr("id",unixtime1));
var obj1=$("#"+unixtime1);
var LyricGen1 = new oLyricLine(obj1);
LyricGen1.lyricLines = ["gen1 line1","1.line2","1-------LINE#3"];
LyricGen1.textSize = 50;
LyricGen1.toppx = Math.floor(Math.random()*($(window).height()-LyricGen1.textSize));
LyricGen1.widthGen();
LyricGen1.leftpx = Math.floor(Math.random()*($(window).width()-parseInt(LyricGen1.widthpx)));
LyricGen1.unixtime = unixtime1;
LyricGen1.drawText();
$("#container").append($("<div></div>").attr("id","Datenow"));
var obj2=$("#Datenow");
var LyricGen2 = new oLyricLine(obj2);
LyricGen2.lyricLines = ["2.1","TWO=two","2........3","gen2 line number 4","2>>line5"];
LyricGen2.textSize = 80;
LyricGen2.toppx = Math.floor(Math.random()*($(window).height()-LyricGen1.textSize));
LyricGen2.widthGen();
LyricGen2.leftpx = Math.floor(Math.random()*($(window).width()-parseInt(LyricGen1.widthpx)));
LyricGen2.unixtime = "Datenow";
LyricGen2.drawText();
function strlen(str){ var len = 0; for (var i=0; i<str.length; i++) { var c = str.charCodeAt(i); if ((c >= 0x0001 && c <= 0x007e) || (0xff60<=c && c<=0xff9f)) { len++; } else { len+=2; } } return len; }
function maxWidth (lyricLines, textSize) {
var maxStringLength=0, maxStringId=-1;
for (var i = lyricLines.length - 1; i >= 0; i--) {
if (maxStringLength < strlen(lyricLines[i])){
maxStringLength = strlen(lyricLines[i]);
maxStringId = i;
};
};
$("#container").append($("<div></div>").css({
"background":"#fff",
"color":"#000",
"visibility":"hidden",
"font-size":textSize
}).attr({"class":"lyricline","id":"widgen"}).text(lyricLines[maxStringId]));
var maxPxLength = $("#widgen").css("width");
$("#widgen").remove();
return maxPxLength;
}
</script>
lyricLinesCount = 0;
You're missing a var here, making lyricLinesCount an implicit global variable. You want to have it local to each timer, though.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
Html
<html>
<head>
<script type="text/javascript" src="/test.js"></script>
<script type="text/javascript">
start();
</script>
</head>
<body>
</body>
</html>
Java script
var points = 1;
var points1;
var DELAY = 30;
var SPEED = 5;
var MAX_DY = 12;
var OBSTACLE_WIDTH = 30;
var OBSTACLE_HEIGHT = 100;
var TERRAIN_WIDTH = 10;
var MIN_TERRAIN_HEIGHT = 20;
var MAX_TERRAIN_HEIGHT = 50;
var POINTS_PER_ROUND = 5;
var DUST_RADIUS = 3;
var DUST_BUFFER = 10;
var NUM_OBSTACLES = 3;
var copter;
var dy = 0;
var clicking = false;
var score; // text you see on the screen
var obstacles = [];
var top_terrain = [];
var bottom_terrain = [];
var dust = [];
function start(){
starta();
}
function starta() {
setup();
setTimer(game, DELAY);
mouseDownMethod(onMouseDown);
mouseUpMethod(onMouseUp);
setTimer(points2, 10)
}
function points2(){
points1 = points/100
return points1;
}
function setup() {
setBackgroundColor(Color.black);
copter = new WebImage("image.png");
copter.setSize(25, 50);
copter.setPosition(getWidth()/3, getHeight()/2);
copter.setColor(Color.blue);
add(copter);
addObstacles();
addTerrain();
score = new Text("0");
score.setColor(Color.white);
score.setPosition(10, 30);
add(score);
}
function updateScore() {
points += POINTS_PER_ROUND;
score.setText(points);
}
function game() {
updateScore();
if (hitWall()) {
lose();
return;
}
var collider = getCollider();
if (collider != null) {
if (collider != copter) {
lose();
return;
}
}
if (clicking) {
dy -= 1;
if (dy < -MAX_DY) {
dy = -MAX_DY;
}
} else {
dy += 1;
if (dy > MAX_DY) {
dy = MAX_DY;
}
}
copter.move(0, dy);
moveObstacles();
moveTerrain();
moveDust();
addDust();
}
function onMouseDown(e) {
clicking = true;
}
function onMouseUp(e) {
clicking = false;
}
function addObstacles() {
for (var i = 0; i < NUM_OBSTACLES; i++) {
var obstacle = new WebImage("image.jpg");
obstacle.setSize(50, 100);
obstacle.setColor(Color.green);
obstacle.setPosition(getWidth() + i * (getWidth()/NUM_OBSTACLES),
Randomizer.nextInt(0, getHeight() - OBSTACLE_HEIGHT));
obstacles.push(obstacle);
add(obstacle);
}
}
function moveObstacles() {
for (var i=0; i < obstacles.length; i++) {
var obstacle = obstacles[i];
obstacle.move(-points1, 0);
if(obstacle.getX() < 0) {
obstacle.setPosition(getWidth(),
Randomizer.nextInt(0, getHeight() - OBSTACLE_HEIGHT));
}
}
}
function hitWall() {
var hit_top = copter.getY() < 0;
var hit_bottom = copter.getY() + copter.getHeight() > getHeight();
return hit_top || hit_bottom;
}
function lose() {
stopTimer(game);
var text = new Text("You Lose!");
text.setColor(Color.red);
text.setPosition(getWidth()/2 - text.getWidth()/2,
getHeight()/2);
add(text);
}
function getCollider() {
var topLeft = getElementAt(copter.getX()-1, copter.getY()-1);
if (topLeft != null) {
return topLeft;
}
var topRight = getElementAt(copter.getX() + copter.getWidth() + 1,
copter.getY() - 1);
if (topRight != null) {
return topRight;
}
var bottomLeft = getElementAt(copter.getX()-1,
copter.getY() + copter.getHeight() + 1);
if (bottomLeft != null) {
return bottomLeft;
}
var bottomRight = getElementAt(copter.getX() + copter.getWidth() + 1,
copter.getY() + copter.getHeight() + 1);
if (bottomRight != null) {
return bottomRight;
}
return null;
}
function addTerrain() {
for (var i=0; i <= getWidth() / TERRAIN_WIDTH; i++) {
var height = Randomizer.nextInt(MIN_TERRAIN_HEIGHT, MAX_TERRAIN_HEIGHT);
var terrain = new Rectangle(TERRAIN_WIDTH, height);
terrain.setPosition(TERRAIN_WIDTH * i, 0);
terrain.setColor(Color.green);
top_terrain.push(terrain);
add(terrain);
height = Randomizer.nextInt(MIN_TERRAIN_HEIGHT, MAX_TERRAIN_HEIGHT);
var bottomTerrain = new Rectangle(TERRAIN_WIDTH, height);
bottomTerrain.setPosition(TERRAIN_WIDTH * i,
getHeight() - bottomTerrain.getHeight());
bottomTerrain.setColor(Color.green);
bottom_terrain.push(bottomTerrain);
add(bottomTerrain);
}
}
function moveTerrain() {
for (var i=0; i < top_terrain.length; i++) {
var obj = top_terrain[i];
obj.move(-points1, 0);
if (obj.getX() < -obj.getWidth()) {
obj.setPosition(getWidth(), 0);
}
}
for (var i=0; i < bottom_terrain.length; i++) {
var obj = bottom_terrain[i];
obj.move(-points1, 0);
if (obj.getX() < -obj.getWidth()) {
obj.setPosition(getWidth(), getHeight() - obj.getHeight());
}
}
}
function addDust() {
var d = new Circle(DUST_RADIUS);
d.setColor("#ffd700");
d.setPosition(copter.getX() - d.getWidth(),
copter.getY() + DUST_BUFFER);
dust.push(d);
add(d);
}
function moveDust() {
for (var i=0; i < dust.length; i++) {
var d = dust[i];
d.move(-points1, 0);
d.setRadius(d.getRadius() - 0.1);
if(d.getX() < 0) {
remove(d);
dust.remove(i);
i--;
}
}
}
Okay so here is my script. The script works perfectly fine on a codehs sandbox, but now that I want to set it on my own website it is not working. Could some one please help me out. Thank you.
Could some one please tell me how I would execute this code from test.js. Thank you.
You need to include the .js to your HTML page, like this:
<script type="text/javascript" src="/test.js"></script>
<script type="text/javascript">
start(); //starts your program
</script>