Is there a way to select elements of an array every second? - javascript

What I'm trying to achieve is to loop and toggle a class on every individual div every second.
Below is the code to create 4 divs with colour from an array.
const container = document.querySelector(".container");
const colors = ["#FDB10B", "#FE8535", "#FD292F", "#B20000"];
const ball = container.querySelectorAll("div");
for (let i = 0; i < colors.length; i++) {
const balls = document.createElement("div");
balls.style.backgroundColor = colors[i];
container.appendChild(balls);
}
Now I want to loop through the containers element every second and Google a class on the elements.
I tried using the setInterval() method

I've added a class "ball" you could as well refer to element by container > div, but easier to understand this way.
const container = document.querySelector(".container");
const colors = ["#FDB10B", "#FE8535", "#FD292F", "#B20000"];
const ball = container.querySelectorAll("div");
for (let i = 0; i < colors.length; i++) {
const balls = document.createElement("div");
balls.style.backgroundColor = colors[i];
balls.classList.add('ball');
container.appendChild(balls);
}
setInterval(function() {
toggle();
}, 1000)
function toggle() {
const tog = document.querySelector('.class-to-toggle');
if (tog) {
tog.classList.remove('class-to-toggle');
const nextTog = tog.nextElementSibling;
if (nextTog) {
nextTog.classList.add('class-to-toggle');
} else {
document.querySelector('.ball').classList.add('class-to-toggle');
}
} else {
document.querySelector('.ball').classList.add('class-to-toggle');
}
}
<div class="container">
</div>
if you inspect in dev tools you'll see class changed

Here is another example of the way you could do this.
Your question lacks some details about what you need to do so the example is quite generic.
const container = document.querySelector(".container");
const colors = ["#FDB10B", "#FE8535", "#FD292F", "#B20000"];
colors.forEach(color => {
const ball = document.createElement("div");
ball.classList.add("ball");
// Added some stylnig value for the example
ball.style.width = "20px";
ball.style.height = "20px";
ball.style.backgroundColor = color;
container.appendChild(ball);
});
const balls = container.querySelectorAll(".ball");
let cursor = 0;
const int_id = setInterval(selectNextBall, 1000);
function selectNextBall() {
const selectedBall = balls[cursor];
// I do this just to illustrate the example,
// could be anything you need to do with your ball.
for (const ball of balls) {
ball.style.border = ball === selectedBall ? "1px solid black" : "none";
}
// for an endless loop
cursor = cursor === balls.length - 1 ? 0 : cursor + 1;
// Or if the loop need to iterate just once
/*
if (cursor === balls.length - 1) {
clearInterval(int_id);
}
cursor++
*/
}
<div class="container">
</div>

Related

fillRect not working when I have a lot of canvas elements

I have a Vue app where I want to perform fillRect on 10000 canvas elements.
The code does work but it breaks when it has a big number sometimes.
What can I do about this?
for (let key = 1; key <= 10000; key++) {
let card = document.getElementById(key);
if (this.scratched.scratchedValue.includes(key) == false) {
let cardCanvas = card.getContext('2d');
cardCanvas.beginPath();
cardCanvas.fillStyle = "#b6b6b4";
cardCanvas.fillRect(0, 0, card.width, card.height);
card.style.opacity = '1';
} else {
card.style.opacity = '1';
card.style.outline = '2px solid #ef7200';
}
}
How it looks when it works
How it looks when it isn't working

How to wait for place ship click event to complete before calling next place ship function in JavaScript Battleship game loop

Problem: I call playerPlaceShip (from a DOM interaction module) inside a game loop module. This lets the user hover the board, click to place the ship, and finally in the event handler call board.placeShip() (from the gameBoard module). This works in isolation, but when I add another playerPlaceShip call to place an additional ship, it executes immediately before the first ship can be placed by clicking.
Desired outcome: A way to wait until the click event from the first function call completes before the next function call begins.
What I've tried: Hours of unsuccessfully trying to write and use promises. Hours of reading about promises. Spent a lot of time unsuccessfully trying to rethink how the code is structured. It seems like the click event should be driving the next action, but I don't see how to do that without writing more and more function calls inside the click event handler, which would seem to take control of the game flow away from the game loop module and put it in the DOM interaction module.
Full modules on GitHub: https://github.com/Pete-Fowler/battleship/tree/player-place-ships/src/modules
Code excerpts:
// In game loop module after creating ships, players, and board objects:
// Render Board
renderBoard(p1Board, p1Box);
renderBoard(p2Board, p2Box);
// Player place ships - lets user hover and click board to place
playerPlaceShip(p1Board, p1Carrier);
playerPlaceShip(p1Board, p1Battleship); // this gets called too soon before click event from the first call completes
// In DOM module:
const clickToPlace = (e, board, ship) => {
let { x, y } = e.target.dataset;
x = parseInt(x, 10);
y = parseInt(y, 10)
board.place(ship, x, y, axis);
renderShadow(e, 'place', ship.length);
removeListeners();
}
// Main function for player to place ship
const playerPlaceShip = (board, ship) => {
const squares = document.querySelectorAll('#p1 .board .square');
narrative.textContent = `Lead your ${ship.type} into battle. Press X to steer.`;
squares.forEach(square => {
square.addEventListener('mouseover', (e) => renderShadow(e, 'fill', ship.length));
square.addEventListener('mouseout', (e) => renderShadow(e, 'clear', ship.length));
square.addEventListener('click', (e) => clickToPlace(e, board, ship));
});
window.addEventListener('keydown', (e) => {
if(e.key === 'x') {
switchAxis();
squares.forEach(square => square.classList.remove('hovered'));
renderShadow(lastCoords, 'fill', ship.length);
}
});
}
Thanks!
I wasn't able to checkout the branch, got a strange error: invalid path 'src/images/background.jpg:Zone.Identifier', maybe because of the colon : after jpg. So I downloaded the zip.
Otherwise I would have done a pull request, that would be easier for you to merge.
I added logic so that the ship is always inside the board, and created a custom event to trigger after place ship. There are comments, see if this will help you move on.
game.js
import gameBoard from "./gameBoard";
import player from "./player";
import makeShip from "./ship";
import { p1Box, p2Box, playerPlaceShip, placeShipEventName, AIPlaceShip, renderBoard, UIAttack } from "./DOM";
const startingShipCount = 5;
// SETUP
// Make game boards
const p1Board = gameBoard();
p1Board.init();
const p2Board = gameBoard();
p2Board.init();
// Make players
const p1 = player("Gustav", p1Board, "human");
const p2 = player("Terminator", p2Board, "AI");
// Make p1 ships
const p1Ptb = makeShip("patrolBoat");
const p1Sub = makeShip("sub");
const p1Destroyer = makeShip("destroyer");
const p1Battleship = makeShip("battleship");
const p1Carrier = makeShip("carrier");
// Make AI ships
const p2Ptb = makeShip("patrolBoat");
const p2Sub = makeShip("sub");
const p2Destroyer = makeShip("destroyer");
const p2Battleship = makeShip("battleship");
const p2Carrier = makeShip("carrier");
// Render Board
renderBoard(p1Board, p1Box);
renderBoard(p2Board, p2Box);
// AI place ships
p2Board.place(p2Ptb, 0, 1, "y");
p2Board.place(p2Sub, 2, 6, "y");
p2Board.place(p2Destroyer, 4, 2, "y");
p2Board.place(p2Battleship, 6, 6, "y");
p2Board.place(p2Carrier, 8, 4, "y");
renderBoard(p1Board, p1Box);
renderBoard(p2Board, p2Box);
//################################################
//###################### HANDLE placeShipPhase
//################################################
let countShipsPlaced = 0;
const handlePlaceShipPhase = () => {
countShipsPlaced++;
if (countShipsPlaced == startingShipCount) {
startGame();
} else {
playerPlaceShip(p1Board, p1Carrier);
}
};
//######################################################
//####### LISTENING to the custom event Place Ship
//######################################################
window.addEventListener(placeShipEventName, handlePlaceShipPhase);
// Player places ships
playerPlaceShip(p1Board, p1Carrier);
const startGame = () => {
alert("Game started, battle!");
};
// MAIN GAME LOOP - will need loop
// Player attack
// UIAttack(p2Board);
// AI attack
// Gameover - after exit loop
// The game loop should set up a new game by creating Players and Gameboards.
// For now just populate each Gameboard with predetermined coordinates. You can
// implement a system for allowing players to place their ships later.
// The game loop should step through the game turn by turn using only methods
// from other objects. If at any point you are tempted to write a new function
// inside the game loop, step back and figure out which class or module that
// function should belong to.
// Create conditions so that the game ends once one players ships have all
// been sunk. This function is appropriate for the Game module.
DOM.js
/* eslint-disable no-unused-expressions */
const p1Box = document.querySelector("#p1");
const p2Box = document.querySelector("#p2");
const narrative = document.querySelector("#narrative");
let axis = "y"; // used to render shadow in playerPlaceShip
let selectedSquares = [];
let lastCoords;
const boardSize = 10;
//save the current ship to be used in the "x" key event listender
let currentShip;
//moved outside of the placeship otherwise will add duplicated events
window.addEventListener("keydown", (e) => {
if (e.key.toLocaleLowerCase() === "x") {
const squares = document.querySelectorAll("#p1 .board .square");
switchAxis();
squares.forEach((square) => square.classList.remove("hovered"));
renderShadow(lastCoords, "fill", currentShip.length);
}
});
//#############################################
//##### CREATING the custom event Place Ship
//#############################################
const placeShipEventName = "playerplaceship";
const placeShipEvent = new Event(placeShipEventName);
// Helper functions for playerPlaceShip
const switchAxis = () => {
axis === "x" ? (axis = "y") : (axis = "x");
};
const renderShadow = (e, fill, length) => {
let { x, y } = e.target.dataset;
x = parseInt(x, 10);
y = parseInt(y, 10);
selectedSquares = [];
let count = countOfSquaresOutOfBoard(x, y, length);
//#### LOGIC TO RENDER SHIP ALWAYS INSIDE BOARD
for (let i = -count; i < length - count; i++) {
setSelectedSquares(x, y, i);
}
for (const el of selectedSquares) {
fill === "fill" ? el.classList.add("hovered") : el.classList.remove("hovered");
if (fill === "place") {
el.classList.add("placed");
}
}
lastCoords = e;
};
const removeListeners = () => {
const squares = document.querySelectorAll("#p1 .board .square");
squares.forEach((square) => {
square.replaceWith(square.cloneNode());
});
};
const clickToPlace = (shipSquare, board, ship) => {
let { x, y } = shipSquare.dataset;
x = parseInt(x, 10);
y = parseInt(y, 10);
board.place(ship, x, y, axis);
renderShadow(lastCoords, "place", ship.length);
removeListeners();
//#######################################################
//############# TRIGGERING the custom event place ship
//#########################################################
window.dispatchEvent(placeShipEvent);
console.log(board.getMap());
};
// Main function for player to place ship
const playerPlaceShip = (board, ship) => {
currentShip = ship;
const squares = document.querySelectorAll("#p1 .board .square");
narrative.textContent = `Lead your ${ship.type} into battle. Press X to steer.`;
squares.forEach((square) => {
square.addEventListener("mouseover", (e) => renderShadow(e, "fill", ship.length));
square.addEventListener("mouseout", (e) => renderShadow(e, "clear", ship.length));
square.addEventListener("click", (e) => clickToPlace(selectedSquares[0], board, ship));
});
};
const countOfSquaresOutOfBoard = (x, y, length) => {
let count = 0;
if (axis === "x") {
count = x + length - boardSize;
}
if (axis === "y") {
count = y + length - boardSize;
}
return count < 0 ? 0 : count;
};
const setSelectedSquares = (x, y, i) => {
if (axis === "x") {
selectedSquares.push(document.querySelector(`#p1 .square[data-x="${x + i}"][data-y="${y}"]`));
} else {
selectedSquares.push(document.querySelector(`#p1 .square[data-x="${x}"][data-y="${y + i}"]`));
}
};
// Lets AI place ship
const AIPlaceShip = (board) => {};
const renderBoard = (board, box) => {
// Clear old content prior to re-render if needed
let grid = document.querySelector(`#${box.id} .board`);
if (grid) {
grid.textContent = "";
} else {
grid = document.createElement("div");
grid.className = "board";
}
// Individual squares on board
for (let i = 0; i <= 9; i += 1) {
for (let j = 9; j >= 0; j -= 1) {
const square = document.createElement("div");
square.className = "square";
square.dataset.x = i;
square.dataset.y = j;
grid.append(square);
}
}
box.append(grid);
};
// Player attack phase - sends x, y from clicked square to board.incoming()
const attackCallback = (e, board) => {
const { x, y } = e.target.dataset;
board.incoming(x, y);
const squares = document.querySelectorAll("#p2 .square");
squares.forEach((el) => {
el.removeEventListener("click", attackCallback);
el.classList.remove("hoverable");
});
console.log(board.getMap());
};
// Player attack phase - adds click event listener and hover effect
const UIAttack = (board) => {
const squares = document.querySelectorAll("#p2 .square");
squares.forEach((el) => {
el.addEventListener("click", (e) => attackCallback(e, board));
el.classList.add("hoverable");
});
narrative.textContent = "Click to fire on the enemy fleet";
};
export { p1Box, p2Box, placeShipEventName, playerPlaceShip, AIPlaceShip, renderBoard, UIAttack };

JQuery - How can I check my cards in memory game?

I have a course where I learn JS+JQuery and I have to write a memory game. I want to check if the turned cards are in the same classes so I can delete them and count the points, but I just cannot do it. I tried a lot of ways but I cannot figure it out.
I tried with .is(), === but nothing...
Here's my code:
let gameArea;
let size = 6;
let card_size = 600 / size;
let images = ['arbalest', 'armored', 'arms', 'cannoner', 'cataphract', 'cavalier', 'centurion', 'champion', 'composite',
'conquistador', 'eagle', 'heavy', 'heavyhorse', 'hussar', 'knight', 'legion', 'paladin', 'skirmisher'
];
$(function() {
gameArea = $('<div></div>');
gameArea.appendTo('body');
gameArea.attr('id', 'gameArea');
drawMap();
gameArea.on("click", "div", function(e) {
if ($(e.target).is(".unturned")) {
$(e.target).removeClass("unturned");
}
let unturnedCardNum = $("#gameArea").find("div:not(.unturned)").length;
if (unturnedCardNum === 3) {
$("#gameArea").find("div:not(.unturned)").addClass("unturned");
}
//I'd like to check here
});
});
function generate() {
let generatedImage = images[Math.floor(Math.random() * images.length)];
if ($("#gameArea").find(`.${generatedImage}`).length === 2) {
images = images.filter(function(e) {
return e !== generatedImage
});
generatedImage = generate();
}
return generatedImage;
}
function drawMap() {
for (let i = 0; i < size; i++) {
for (let j = 0; j < size; j++) {
let pic = $('<div></div>');
let shuffledImages = generate();
pic.addClass(shuffledImages);
pic.addClass("unturned");
pic.css({
width: card_size,
height: card_size,
top: i * card_size,
left: j * card_size
});
pic.appendTo(gameArea);
}
}
}
Assuming that the string in the images array will have been assigned to the associated card as a class name and that, after removing the "unturned" class, this will be the only class name left then maybe the following might help you:
Inside the $("#gameArea").on("click"...) function replace the lines
let unturnedCardNum = $("#gameArea").find("div:not(.unturned)").length;
if (unturnedCardNum === 3) {
$("#gameArea").find("div:not(.unturned)").addClass("unturned");
}
with
let unturnedCards = $("#gameArea").find("div:not(.unturned)");
switch(unturnedCards.length){
case 3:
unturnedCards.addClass("unturned");
break;
case 2:
if (unturnedCards[0].className===
unturnedCards[1].className){
console.log("you found a pair!");
// score[currentPlayer]++;
}
}
Further hint:
Instead of calling the generate() function recursively, you could simply shuffle an array made up of two copies of the images array by using a Fisher-Yates algorithm:
function shuffle(a,n){ // shuffle array a in place (Fisher-Yates)
let m=a.length;
n=n||m-1;
for(let i=0,j;i<n;i++){
j=Math.floor(Math.random()*(m-i)+i);
if (j-i) [ a[i],a[j] ] = [ a[j],a[i] ]; // swap 2 array elements
}
}

How do you blur a single object in Matter.js?

I'm trying to create a fire effect for a game in Matter.js, and I need to blur a circle to make it look more realistic. However, I need to make it so it only blurs the fire, not the whole canvas. How can I do this?
This is the code I have so far:
function setOnFire(object) {
var fireX = object.position.x;
var fireY = object.position.y;
var fire = Bodies.circle(fireX, fireY, vw*1, {
isStatic: true,
render: {
fillStyle: "rgba(255,130,0,1)"
}
});
World.add(world, fire);
}
This is not exactly what I had in mind, but it is as close as you can get.
Start by going to matter.js and go to this section:
for (k = body.parts.length > 1 ? 1 : 0; k < body.parts.length; k++) {
part = body.parts[k];
if (!part.render.visible)
continue;
Add this code after the continue;:
if (body.bloom) {
c.shadowColor = body.render.fillStyle;
c.shadowOffsetX = 0;
c.shadowOffsetY = 0;
c.shadowBlur = body.bloom;
}
Then, go to the very end of the loop and add this:
if (body.bloom) {
c.shadowColor = "transparent";
c.shadowOffsetX = 0;
c.shadowOffsetY = 0;
c.shadowBlur = 0;
}
Then, just add the bloom while making your body. For instance:
let fireParticle = Bodies.circle(0, 0, {
bloom: 25
});

Can I add class to dynamically created two.js element?

I am using the code showed below to create 46 small circles within a wrapper (div) draw-shapes;
let elem = document.getElementById('draw-shapes');
let params = { width: 1024, height: 768 };
let two = new Two(params).appendTo(elem);
for (let i = 1; i < 47; i++) {
circle = two.makeCircle(x, y, radius);
circle.fill = 'green';
circle.stroke = 'white';
circle.linewidth = 1;
circle.id = i;
}
All drawings are made with the Two.js library. I read in the documentation I can change the id of the created element, but I also need to assign a class to each element. I have tried everything from pure js setAttribute to jQuery .attr and .addClass methods, but none of them worked, so I started to wonder if this is even possible to do? If someone knows a way, please let me know how. Thank.
There is not internal utility or property to get to the DOM node of each Two element.
But the id you specify is indeed added as two-<specified-id> to the actual node.
So you can use the normal document.getElementById.
So in your case
let elem = document.getElementById('draw-shapes');
let params = {
width: 300,
height: 300
};
let two = new Two(params).appendTo(elem);
for (let i = 1; i < 20; i++) {
const circle = two.makeCircle(i * 10, i * 10, 40);
circle.fill = 'green';
circle.stroke = 'white';
circle.linewidth = 1;
circle.id = `two-circle-${i}`;
}
two.update();
// add classname to every fifth element;
for (let i = 1; i < 20; i += 5) {
const circleNode = document.getElementById(`two-circle-${i}`);
circleNode.classList.add('classname');
circleNode.addEventListener('mouseover', function() {
const path = two.scene.getById(this.id)
path.scale = 1.2;
two.update();
});
circleNode.addEventListener('mouseout', function() {
const path = two.scene.getById(this.id)
path.scale = 1;
two.update();
});
}
.classname {
stroke-width: 5;
stroke: red;
fill:yellow;
cursor:pointer;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/two.js/0.6.0/two.js"></script>
<div id="draw-shapes"></div>

Categories

Resources