Heres my codepen: https://codepen.io/apasric4/pen/qBWyqqb
This is the code that's causing me issues:
const moveBlock=()=> {
let x=myBlock.offsetLeft;
let y=myBlock.offsetTop;
keyArray.forEach(key=> {
if (key==='ArrowRight') {
myBlock.style.left=`${x+50}px`
} else if (key==='ArrowLeft') {
myBlock.style.left=`${x-50}px`
} else if (key==='ArrowDown') {
myBlock.style.top=`${y+50}px`
} else if (key==='ArrowUp') {
myBlock.style.top=`${y-50}px`
}
})
}
I'll briefly explain my program:
The program will start you pressing left, right, up, or down--those commands appear as a list at the top of the document. Then you'll press enter
When enter is pressed, the program goes through the array of keyboard directions and the block moves in the specified direction depending on the command in the array. There's a condition for each arrow direction to move the block 50px which way. This is a sequence so we're supposed to see happening.
However, the block only move 50px right or left, etc only once. Why is that? Am I not iterating my x and y variables correctly?
You have to calculate the value of x and y again after moving once.
Because when you are moving the block from second time, you are just using original value of x and y computed before the for loop began.
Add this in your forEach() method.
x = myBlock.offsetLeft;
y = myBlock.offsetTop;
You have later mentioned in the comments about having a delay between each move.
You can do that by creating another animate block function, having a timeout within a loop and and calling that animate block function in the timeout.
const animateBlock = (key)=> {
let x = myBlock.offsetLeft;
let y = myBlock.offsetTop;
if (key === 'ArrowRight') {
myBlock.style.left = `${x + 50}px`
} else if (key === 'ArrowLeft') {
myBlock.style.left = `${x - 50}px`
} else if (key === 'ArrowDown') {
myBlock.style.top = `${y + 50}px`
} else if (key === 'ArrowUp') {
myBlock.style.top = `${y - 50}px`
}
}
const moveBlock = () => {
keyArray.forEach((key, i) => {
setTimeout(() => {
animateBlock(key);
}, i * 1000);
})
}
Working demo: https://codepen.io/nikhilmopidevi/pen/gOYjgYd
Related
I have array of slides
sliderItem.forEach((slide, index) => {
slide.addEventListener('mousedown', (e) =>{
touchStart(e)
})
slide.addEventListener('mousemove', (e) =>{
touchEND(e)
})
})
and i have function for take position clientX
function getPositionX(e){
return e.type.includes('mouse') ? e.pageX : e.touches[0].clientX
}
and 2 functions for take positions
function touchStart(e) {
const startPos = getPositionX(e)
}
function touchEnd(e) {
const endPos = getPositionX(e)
}
It take the same positions for start and for end, i need to take at first position start, after take position of end and then compare them to understand for which way to turn the slides
Get element at specified position - JavaScript
This question only answers for a particular x and y point, but I would like to select all elements within a rectangle defined by 4 x and y points and be then able to only select elements with a certain classname, the classname could be "selectable". Is there a way to do this in regular javascript? What's the most efficient way to do this?
I was thinking about calling document.elementsFromPoint(x, y) repeatedly, but because the x and y arguments are not integers, I would have to call it an infinite number of times.
I think i made an answer. Though I haven't tested it much.
First a description of the program. The program recursively goes through the dom starting from the body element and tests to see if it is entirely inside the query rectangle. My code assumes that elements wont be found outside of there parents, but if you don't want to assume this delete the marked lines.
// first some helper functions
function isRecPartialyInside(inside, outside){
if((between(inside.xmin, outside.xmin,outside.xmax) || between(inside.xmax, outside.xmin,outside.xmax ) && (between(inside.ymin, outside.ymin,outside.ymax) || between(inside.ymax, outside.ymin,outside.ymax )))){
return true;
}
return isRecFullyInside(outside, inside);
}
function between(x, min, max){
return x > min && x < max;
}
function clientRecToRec(clientRec){
return {xmin: clientRec.x, xmax:clientRec.right, ymin: clientRec.y, ymax: clientRec.bottom};
}
function isRecFullyInside(possiblyInside, possiblyOutside){
return (possiblyInside.xmin > possiblyOutside.xmin && possiblyInside.xmax < possiblyOutside.xmax && possiblyInside.ymin > possiblyOutside.ymin && possiblyInside.ymax < possiblyOutside.ymax);
}
function isClientRecFullyInside(clientRec, rec){
let crec = clientRecToRec(clientRec);
return isRecFullyInside(crec, rec);
}
function isClientRecPartiallyInside(clientRec,rec) {
let crec = clientRecToRec(clientRec);
return isRecPartialyInside(crec, rec);
}
// here is the real deal
function elementsFromBox(x1,x2,y1,y2){
let output = [];
let boundsRec = {xmin:x1,xmax:x2,ymin:y1,ymax:y2};
let curr = document.body;
let stack = [];
let frame = {index:0,list:[curr]};
stack.push(frame);
while(stack.length > 0){
let currFrame = stack.at(-1);
if(currFrame.index >= currFrame.list.length){
stack.pop();
continue;
}
let currEl = currFrame.list[currFrame.index];
currFrame.index +=1;
// Check if the element is fully inside. If so report
if(isClientRecFullyInside(currEl.getBoundingClientRect(), boundsRec)){
output.push(currEl);
}
if(isClientRecPartiallyInside(currEl.getBoundingClientRect(), boundsRec)){ // del
stack.push({index:0,list:currEl.children});
continue;
} // del
}
return output;
}
I've been working on writing a minimax algorithm for my tictactoe game, but it doesn't seem to be choosing the correct move. It is possible to win against the computer, so I must have something mixed up here.
I based my algorithm off this article (I had to alter my minimax code since my code was different from the example in the article)
// Checking if there are any moves remaining on the board. Returns false if there are no moves left to play
const isMovesLeft = function () {
if (gameBoard._moves === 9) {
return false;
} else {
return true;
}
};
// Evaluates if the maximiser or minimiser has won or draw - returns a number if there is a win or draw
const evaluate = function () {
if (gameBoard._roundWon && gameBoard._currentPlayer === gameBoard._player1)
return +10;
if (gameBoard._roundWon && gameBoard._currentPlayer === gameBoard._player2)
return -10;
if (gameBoard._roundTie) return 0;
};
// Minimax function - considers all the possible ways the game can go and returns the value of the board
const minimax = function (board, depth, isMax) {
let score = evaluate();
// If maximiser has won the game - return evaluated score
if (score === 10) return score;
// If minimiser has won the game - return evaluated score
if (score === -10) return score;
// If there are no moves left and there is no winner - then return as a tie
if (isMovesLeft() === false) return 0;
// If it is the maximiser's turn
if (isMax) {
let bestScore = -1000;
let availableTiles = [];
// Looping through all tiles
tile.forEach((tile, index) => {
// Check if cell is empty
if (tile.textContent === '') {
availableTiles.push(index);
}
});
// Make the move
const chosenEmptyTile =
availableTiles[Math.floor(Math.random() * availableTiles.length)];
renderAITile(tile, chosenEmptyTile);
addPlayerMoves(chosenEmptyTile);
// Calling minimax recursively and choose the maximum value
bestScore = Math.max(bestScore, minimax(board, depth + 1, false));
console.log(bestScore);
// Undo the move
undoAIMove(tile, chosenEmptyTile);
return bestScore;
} else {
let bestScore = 1000;
let availableTiles = [];
// Looping through all tiles
tile.forEach((tile, index) => {
// Check if cell is empty
if (tile.textContent === '') {
availableTiles.push(index);
}
});
// Make the move
const chosenEmptyTile =
availableTiles[Math.floor(Math.random() * availableTiles.length)];
renderAITile(tile, chosenEmptyTile);
addPlayerMoves(chosenEmptyTile);
// Calling minimax recursively and choose the maximum value
bestScore = Math.min(bestScore, minimax(board, depth + 1, false));
// Undo the move
undoAIMove(tile, chosenEmptyTile);
return bestScore;
}
};
const undoAIMove = function (tile, chosenEmptyTile) {
tile[chosenEmptyTile].textContent = '';
tile[chosenEmptyTile].classList.remove(`playerX`);
tile[chosenEmptyTile].classList.remove(`playerO`);
gameBoard._board[chosenEmptyTile] = '';
gameBoard._moves -= 1;
};
// Return the best possible move for the AI
const findBestMove = function (board) {
let bestVal = -1000;
let bestMove = [];
let availableTiles = [];
// Traverse all cells, evaluate minimax function for all empty cells. And return the cell with optimal value.
tile.forEach((tile, index) => {
if (tile.textContent === '') {
availableTiles.push(index);
}
});
// Make the move
console.log(`Empty Tile: ${availableTiles}`);
const chosenEmptyTile =
availableTiles[Math.floor(Math.random() * availableTiles.length)];
console.log(`Chosen tile: ${chosenEmptyTile}`);
renderAITile(tile, chosenEmptyTile);
addPlayerMoves(chosenEmptyTile);
console.log(gameBoard._board);
// Compute evaluation function for this move
let moveVal = minimax(board, 0, false);
// Undo the move
undoAIMove(tile, chosenEmptyTile);
// If value of the current move is more than the best value, then update best
if (moveVal > bestVal) {
bestVal = moveVal;
bestMove.push(chosenEmptyTile);
gameBoard._board[chosenEmptyTile] = 'X';
renderAITile(tile, bestMove);
}
return bestMove;
};
const startGameAI = function () {
if (gameBoard._aiTurn && gameBoard._opponent === 'AI') {
// Finding best move
findBestMove(gameBoard._board);
// Store AI moves in data
addPlayerMoves();
console.log(gameBoard._moves);
// Check if game over or tie
checkGameOver();
// Switch players
switchPlayers();
renderScoreBoard();
}
};
// Renders the tile AI selects
const renderAITile = function (tile, randomEmptyTileIndex) {
if (gameBoard._roundWon || gameBoard._roundTie || !gameBoard._isGameActive)
return;
if (gameBoard._aiTurn) {
tile[randomEmptyTileIndex].textContent = 'X';
tile[randomEmptyTileIndex].classList.add(
`player${gameBoard._currentPlayer}`
);
}
};
Full code on Codepen
Im trying to make an infinite loop for a carousel when i click a desired div it will be centered at a desired position, if i click the item that is adjacent to the centered position, everything works fine, but if he is more than 1 position away from the center it triggers an effect that doesnt follow the usual logic.
I have tried to solve the problem by checking the distance from the center and than moving the items 1 by 1 n times, but i guess because the loop doesn't wait for the animations to finish im getting this weird effect.
The final outcome should be making an infinite feel to the carousel when you click an item that is 5 positions away from the center it would center it and the ones that are out of view will slide from the respective direction to create a loop
any help will be appreciated, Im relatively new to web dev so a well explained answer will be highly appreciated
JS:
const serviceList = document.querySelectorAll('.service__block');
serviceList.forEach(service => {
service.addEventListener('click', () => {
markSelectedService(service);
moveService(checkDistance(service));
});
});
//Adds the class to the clicked service
function markSelectedService(service) {
removeSelectedClass();
service.classList.add('selected');
}
//Removes the selected class from all the services
function removeSelectedClass() {
serviceList.forEach(service => {
service.classList.remove('selected');
});
}
//Check distance from center
function checkDistance(service) {
let distance = service.dataset.order - 4;
return distance;
}
//Move the service 1 by 1 n times
function moveService(distance) {
if (distance > 0) {
for (var i = 0; i < distance; i++) {
serviceList.forEach(service => {
service.dataset.order = parseInt(service.dataset.order) - 1;
if (parseInt(service.dataset.order) === -1) {
service.dataset.order = 11;
}
});
}
} else if (distance < 0) {
distance = distance * -1;
for (var i = 0; i < distance; i++) {
serviceList.forEach(service => {
service.dataset.order = parseInt(service.dataset.order) + 1;
if (parseInt(service.dataset.order) === 12) {
service.dataset.order = 0;
}
});
}
}
}
Link to codepen:
https://codepen.io/tomyshoam/pen/yLLLYyQ
If you want to wait for the animation's end to trigger the next one you insert a setInterval and set the interval time to your animation's time.
Example:
var i = 0;
var animationInterval = setInterval(function () {
//YOUR LOGIC HERE
if (++i === distance) {
window.clearInterval(animationInterval);
}
}, 500);
Update
After some tries I think I got it working how you wanted, although it's not the better way to do it.
I copied the icons list, one to the right side and one to the left, this way the icons will only move to the other side when they are far far away from the user's view and will not trigger that weir behavior.
Code below:
https://codepen.io/diogoperes/pen/oNNNBGY
Im trying to finetune the controls of a javascript game (p5 library).
keyIsDown() is the type of control/feel im looking for but it's timing is too fast.
By timing is too fast, I mean when I hold down the key, the key repeats too fast. Im trying to control the timing speed of the key repeat when holding down the key.
I tried to make my own vertion with keytyped() and setInterval to time my move function. Then stopping it with keyReleased(). But it jams up.
I have also tried setTimeout but could not get it to work on keyboard input like this.
var controlInterval;
function keyReleased() {
if (key === 'a') {
clearInterval(controlInterval);
} else if (key === 'd') {
clearInterval(controlInterval);
}
//return false; // prevent any default behavior
}
function keyTyped() {
if (key === 'a') {
controlInterval = setInterval(left, 50);
} else if (key === 'd') {
controlInterval = setInterval(right, 50);
}
}
function left(){
var x = -1;
move(x);
}
function right(){
var x = 1;
move(x);
}
code I prefer to use:
if (keyIsDown(LEFT_ARROW)){
var west = -1;
move(west);
}
if (keyIsDown(RIGHT_ARROW)){
var east = 1;
move(east);
Take a look at debouncing and throttling principles which I think is what you are looking for here - limit the execution count of event.
The best explanation IMHO related to this subject is this article.
There are already libraries to help you like lodash. Go to their documentation page and search the functions - debounce or throttle and it there will be examples how to use them.
someDiv.addEventListener('keyup', _.debounce(handleKeyUp, 300));
function handleKeyUp(event) {
if (event.keyCode === 65 /* A */) left();
...
}
Arrow keys can be used to signal a direction, frame rate can be used to control speed and a delta variable used to control the amount of movement.
var xPos = 0;
var xDelta = 1;
function setup(){
createCanvas(200,200);
frameRate(10); // 10 frames per second, increase to move faster
}
function draw(){
background(100);
if (keyIsDown(LEFT_ARROW)){
var west = -1;
move(west);
}
if (keyIsDown(RIGHT_ARROW)){
var east = 1;
move(east);
}
rect(xPos, 100, 10,10);
}
function move(dir){
if (dir == -1){
xPos = xPos - xDelta;
}
else {
xPos = xPos + xDelta;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.8.0/p5.min.js"></script>