I'm working on a little html5 game where the user has to click on a specified circle. I want the circles to change position every second randomly. I was able to make this. My only problem with it is that the circles often overlap one another. Is there any way I can prevent this? I've tried using margins, but that won't work because the circles have a position of absolute.
Here's the code for it:
//circles for game
var circle1 = document.getElementById('circle1');
var circle2 = document.getElementById('circle2');
var circle3 = document.getElementById('circle3');
//viewport height and width
var vh = 100;
var vw = 100;
//when the document loads, the circles change their position on screen
function changePosition() {
//Intercval that runs every second
setInterval ( function() {
//generates random numbers and assings them to the top and
//left properties of the circles
var circle1Top = Math.floor(Math.random() * vh ) + 1;
var circle2Top = Math.floor(Math.random() * vh ) + 1;
var circle3Top = Math.floor(Math.random() * vh ) + 1;
var circle1Left = Math.floor(Math.random() * vw) + 1;
var circle2Left = Math.floor(Math.random() * vw) + 1;
var circle3Left = Math.floor(Math.random() * vw) + 1;
//if the random number is greater than or equal to the device size, another number is generated
//this prevents the circles from appearing off screen
//circle1
while (circle1Top >= vh - 16 || circle1Top > vh) {
circle1Top = Math.floor(Math.random() * vh ) + 1;
};
while (circle1Left >= vw - 15 || circle1Top > vw) {
circle1Left = Math.floor(Math.random() * vw ) + 1;
};
//circle2
while (circle2Top >= vh - 16 || circle2Top > vh) {
circle2Top = Math.floor(Math.random() * vh ) + 1;
};
while (circle2Left >= vw - 15 || circle2Top > vw) {
circle2Left = Math.floor(Math.random() * vw ) + 1;
};
//circle3
while (circle3Top >= vh - 16 || circle3Top > vh) {
circle3Top = Math.floor(Math.random() * vh ) + 1;
};
while (circle3Left >= vw - 15 || circle3Top > vw) {
circle3Left = Math.floor(Math.random() * vw ) + 1;
};
//once the numbers are generated, they are assigned to the circles accordingly
circle1.style.top = circle1Top + 'vh';
circle1.style.left = circle1Left + 'vw';
circle2.style.top = circle2Top + 'vh';
circle2.style.left = circle2Left + 'vw';
circle3.style.top = circle3Top + 'vh';
circle3.style.left = circle3Left + 'vw';
}, 1000);
};
body {
background-color: aliceblue;
height: 100vh;
width: 100vw;
margin: 0;
overflow: hidden;
}
main {
width: 100%;
height: 100%;
margin: 0;
}
.circle {
width: 110px;
height: 110px;
background-color: blue;
border-radius: 50%;
position: absolute;
}
#circle1 {
background-color: red;
}
#circle2 {
background-color: blue;
}
#circle3 {
background-color: yellow;
}
/*media queries*/
#media only screen and (max-width: 435px) {
.circle {
width: 70px;
height:70px;
}
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link type="text/css" rel="stylesheet" href="test.css">
</head>
<body onload="changePosition()">
<main>
<div class="circle" id="circle1"></div>
<div class="circle" id="circle2"></div>
<div class="circle" id="circle3"></div>
</main>
<!-- Scripts -->
<script type="text/javascript" src="test.js"></script>
</body>
</html>
Any responses are greatly appreciated.
You have to check if the circles overlap and if they do, reload the script. To check if they overlap you have to do some math:
1) Find out what the center of the circle is:
var circle1centerX = circle1Left * document.documentElement.clientHeight * 0.65 + 55;
var circle1centerY = circle1Top * document.documentElement.clientHeight * 0.65 + 55;
document.documentElement.clientHeight * 0.65 is the factor you need to multiply with to convert vh or vw to px. 55 is half the radius of the circles.
2) Check if circles overlap:
If the circles overlap, then the distance between their centers must be lower than two times the radius. If the distance between the centers is equal to or larger than two times the radius, they don't overlap. (In your case 2 times the radius is 110px)
var distanceBetween1and2 = Math.sqrt(Math.pow(circle2centerX - circle1centerX, 2) + Math.pow(circle2centerY - circle1centerY));
var distanceBetween1and3 = Math.sqrt(Math.pow(circle3centerX - circle1centerX, 2) + Math.pow(circle3centerY - circle1centerY));
var distanceBetween2and3 = Math.sqrt(Math.pow(circle3centerX - circle2centerX, 2) + Math.pow(circle3centerY - circle2centerY));
(Pythagorean theorem)
if(distanceBetween1and2 < 2*radius || distanceBetween2and3 < 2*radius || distanceBetween1and3 < 2*radius) {
changePosition();
} else {
//place the circles
}
But there is one disadvantage of this method, when the area in which the circles are, is small enough, so there must be circles overlapping, there will be an infinite loop. You can prevent this by setting a minimum size on the screen in which the circles are placed.
Related
I have some sort of poll where you vote either YES and NO and based on the votes it creates a poll chart (by creating two divs inside another div that has a set width and setting the width of the first two divs the percentage of YES and NO votes out of the total votes). You can see the project for a better understanding by clicking HERE.
I want it to appear animated as if it were in CSS with transition: width 100ms linear; just like here:
<!DOCTYPE html>
<html>
<head>
<title>Document</title>
<style>
.poll{
height: 50px;
width: 300px;
background-color: black;
transition: all 300ms;
}
.poll:hover{
width: 500px;
}
</style>
</head>
<body>
<div class="poll"></div>
</body>
</html>
However, whenever I add something similar to the class of my divs I see no change. The divs in question are created in this function:
function renderPoll(){
container.innerHTML=''; //reset container
let poll1 = document.createElement('div');
let poll2 = document.createElement('div');
poll1.classList.add('poll-attr');
poll2.classList.add('poll-attr');
let innerTextPoll = Math.round(calcPerc()); //calcPerc() calculates the percent of YES votes with the equation percentage = (100*NumberOfYES)/NumberOfVotes
poll1.style.width = calcPerc() + '%';
poll2.style.width = 100-calcPerc() + '%';
poll1.innerText = innerTextPoll + '%';
poll2.innerText = 100-innerTextPoll + '%';
container.appendChild(poll1);
container.appendChild(poll2);
}
I am not nearly experienced enough to figure this out so any input is appreciated!
Bulding on your code and #Noel Maróti answer, indeed all you have to do is set interval for animating the polls after you add them to the container.
function renderPoll() {
container.innerHTML = ''; //reset container
let poll1 = document.createElement('div');
let poll2 = document.createElement('div');
poll1.classList.add('poll-attr');
poll2.classList.add('poll-attr');
let innerTextPoll = Math.round(calcPerc()); //calcPerc() calculates the percent of YES
poll1.innerText = innerTextPoll + '%';
poll2.innerText = 100 - innerTextPoll + '%';
container.appendChild(poll1);
container.appendChild(poll2);
var target_length = 300;
animation(poll1, 0, (calcPerc()) * target_length / 100);
animation(poll2, 0, (100 - calcPerc()) * target_length / 100);
}
function calcPerc() {
return 75;
}
function animation(elem, from, to) {
let id = null;
let width = from || 0;
var speed = 2.5;
requestAnimationFrame(frame);
function frame() {
if (width < to) {
width += speed;
elem.style.width = width + "px";
requestAnimationFrame(frame);
}
}
}
renderPoll();
.poll-attr {
border: 1px solid blue;
height: 50px;
background: lightyellow;
}
.poll {
height: 50px;
width: 300px;
background-color: black;
transition: all 300ms;
}
.poll:hover {
width: 500px;
}
<div class="poll"></div>
<div id="container"></div>
You can do it easily like this:
function animation () {
let id = null;
const elem = document.querySelector(".poll");
let width = 300; // default width
clearInterval(id);
id = setInterval(frame, 5); // changing the number will effect the speed of the animation
function frame() {
if (width == 500) { // if the width is 500px, then finish animation
clearInterval(id); // finish animation
} else {
width++;
elem.style.width = width + "px";
}
}
}
I'm trying to create a progress bar for a product card track so by any click of the user on the prev and next buttons (which would scroll back or forward) the progress bar would advance or backup.
here's the code I came up with. the problem is the first click doesn't show any result and the prev button acts like the next button for the first time. It's like the code is one step behind.
I'm very new to javaScript and I can't figure out how this could happen.
const productScroll = () => {
rightButton.onclick = function () {
let scrollLeft = document.querySelector('#ProductSlider').scrollLeft;
let scrollPercent = ((scrollLeft - 0) / (5033 - 0)) * (100 - 0) + 0;
document.querySelector('div.progress-bar').style.width = `${scrollPercent}%`;
};
leftButton.onclick = function () {
let scrollLeft = document.querySelector('#ProductSlider').scrollLeft;
let scrollPercent = ((scrollLeft - 0) / (5033 - 0)) * (100 - 0) + 0;
document.querySelector('div.progress-bar').style.width = `${scrollPercent}%`;
};
If youre always 1 step behind, it could be that your percentage calculation is wrong. For example, if you have 5 steps and want to show progress for each step, starting at 1 and ending at 5, your progress bar needs to have 4 steps instead:
1 > 2 > 3 > 4 > 5 = 4 steps (total - 1)
In percentages, it looks like this for a 5 step progress bar:
1: 0%
2: 25%
3: 50%
4: 75%
5: 100%
Notice each increase is 25% (1/4) and not 20% (1/5).
So in abstract shape, your calculation would need to be:
((scroll / max) * (steps - 1)) / (steps - 1) * 100%
Which means your scrollLeft / 5033 needs to be between 0 and 4, divided by 4, then turned into a percentage:
const percentage = ((scrollLeft / 5033) * 4) / 4 * 100;
To create a progress bar, first create two “div” tag elements named id Progress_Status and myprogressbar.
To add a numeric label to indicate how far the user is in the process, the element inside or outside the progress bar is required which will display the progress status which in this case is myprogressbar.
<div id="Progress_Status">
<div id="myprogressBar">1%</div>
</div>
Adding CSS:
#Progress_Status {
width: 50%;
background-color: #ddd;
}
#myprogressBar {
width: 1%;
height: 35px;
background-color: #4CAF50;
text-align: center;
line-height: 32px;
color: black;
}
Adding JavaScript:
Next, Code below creates a dynamic progress bar(animated) using JavaScript functions update and scene.
function update() {
var element = document.getElementById("myprogressBar");
var width = 1;
var identity = setInterval(scene, 10);
function scene() {
if (width >= 100) {
clearInterval(identity);
} else {
width++;
element.style.width = width + '%';
element.innerHTML = width * 1 + '%';
}
}
}
Firstly, you fetch the element by id and then set starting width to 1. For the purpose of working example, i used setintervel to show the progression of progress bar.
All we are doing here is calling the scene function which checks if width is less than 100. If yes, then stop loader by clearing the interval. If not, then increment the width and add it to the with and progress label div for showing the progress in percentenge.
Complete Code:
function update() {
var element = document.getElementById("myprogressBar");
var width = 1;
var identity = setInterval(scene, 10);
function scene() {
if (width >= 100) {
clearInterval(identity);
} else {
width++;
element.style.width = width + '%';
element.innerHTML = width * 1 + '%';
}
}
}
#Progress_Status {
width: 50%;
background-color: #ddd;
}
#myprogressBar {
width: 1%;
height: 35px;
background-color: #4CAF50;
text-align: center;
line-height: 32px;
color: black;
}
<!DOCTYPE html>
<html>
<body>
<h3>Example of Progress Bar Using JavaScript</h3>
<p>Download Status of a File:</p>
<div id="Progress_Status">
<div id="myprogressBar">1%</div>
</div>
<br>
<button onclick="update()">Start Download</button>
</body>
</html>
I wanna change the color of my circle between red and green when someone clicks on the circle. Both the colors should randomly switch. For Example when someone clicks on a red circle it can switch to green color or stay red and vice versa.(R,R,G,G,G,G,R.....)
The demo currently looks like this. https://jsfiddle.net/sidsingh29/591hfwLd/12/
HTML:
<div id="test"></div>
CSS:
#test {
position:absolute;
height: 55px;
width: 55px;
background-color: red;
border-radius: 50%;
display: inline-block;}
JavaScript + jQuery (edge):
$('#test').click(function() {
var docHeight = $(document).height(),
docWidth = $(document).width(),
$div = $('#test'),
divWidth = $div.width(),
divHeight = $div.height(),
heightMax = docHeight - divHeight,
widthMax = docWidth - divWidth;
$div.css({
left: Math.floor( Math.random() * widthMax ),
top: Math.floor( Math.random() * heightMax )
});
});
Just set background property also with left and top based on Math.random() > 0.5 or not:
$('#test').click(function() {
var docHeight = $(document).height(),
docWidth = $(document).width(),
$div = $('#test'),
divWidth = $div.width(),
divHeight = $div.height(),
heightMax = docHeight - divHeight,
widthMax = docWidth - divWidth;
$div.css({
left: Math.floor( Math.random() * widthMax ),
top: Math.floor( Math.random() * heightMax ),
background: Math.random() > 0.5 ? 'red' : 'green', // setting background here
});
});
#test {
position:absolute;
height: 55px;
width: 55px;
background-color: red;
border-radius: 50%;
display: inline-block;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<div id="test"></div>
This task cannot be done using entirely CSS, so I have used Javascript and jQuery to do it. Sometimes it can keep the same colour over and over, because I am using random generated numbers to change the colour. I have also put in a timer that times the time you take to click on the circle, in case you were making a reaction tester.
var colour = 1 + Math.floor(Math.random() * 10);
function oddOrEven(x) {
return ( x & 1 ) ? "odd" : "even";
}
function checkNumber(argNumber) {
var result = oddOrEven(argNumber);
if (result == "even") {
$("#circle").css("background-color", "red");
} else {
$("#circle").css("background-color", "green");
};
start = new Date().getTime();
}
$(document).ready(function() {
checkNumber(colour)
});
function makeShapeAppear() {
var docHeight = $(document).height(),
docWidth = $(document).width(),
circle = $('#circle'),
circleWidth = circle.width(),
circleHeight = circle.height(),
heightMax = docHeight - circleHeight,
widthMax = docWidth - circleWidth;
circle.css({
left: Math.floor( Math.random() * widthMax ),
top: Math.floor( Math.random() * heightMax ),
display: "block",
});
colour = 1 + Math.floor(Math.random() * 10);
checkNumber(colour)
};
$('#circle').click(function() {
makeShapeAppear();
});
document.getElementById("circle").onclick = function() {
document.getElementById("circle").style.display = "none";
var end = new Date().getTime();
var timeTaken = (end - start) / 1000;
document.getElementById("timeTaken").innerHTML = timeTaken + " seconds taken";
}
#circle {
height: 100px;
width: 100px;
border: 0x solid black;
border-radius: 50%;
position: absolute;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="circle" onclick="makeShapeAppear()"></div>
<h3>Time Taken = </h3><div id="timeTaken"></div>
I have created three circles and made it bounce off the wall without using HTML canvas. Now I want two circles to collide with each other and move those circles in the opposite direction. I tried to detect the collision by checking it's position but it doesn't work. I don't know where I went wrong.
Here's my code:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Bounce Ball</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
.circle{
height: 50px;
width: 50px;
position: absolute;
border-radius: 50%;
}
.container {
height: 300px;
width: 300px;
background-color: skyblue;
position: relative;
}
</style>
</head>
<body>
<div class ="container" id ="container">
<div class="circle" id = "circle1" style="background-color:black;
height: 50px; width: 50px;top:0; left:0"></div>
<div class="circle" id ="circle2" style="background-color:rgb(197, 100,
100);height: 50px; width: 50px;top:200px;left: 150px"></div>
<div class="circle" id ="circle3" style="background-color:brown;height:
50px;width: 50px;top:50px;left: 640px"></div>
</div>
<script>
var container = document.getElementById("container");
container.style.width="700px";
container.style.height = "300px";
var balls = document.getElementsByClassName("circle");
for(var i=0; i <balls.length; i++){
var speed={x:3,y:-3}
setInterval(draw, 50 , balls[i], speed);
}
function draw(ball, speed) {
if(parseInt(ball.style.left) > (parseInt(container.style.width)-
parseInt(ball.style.width)) || (parseInt(ball.style.left) <0) ){
speed.x = -speed.x;
}
ball.style.left = parseInt(ball.style.left) + speed.x + 'px';
if(parseInt(ball.style.top) > (parseInt(container.style.height)-
parseInt(ball.style.height)) || (parseInt(ball.style.top) <0)){
speed.y = -speed.y;
}
ball.style.top = parseInt(ball.style.top) + speed.y + 'px';
//for colliding two circles
for(var i =0 ; i <= balls.length-1; i++){
for(var j = i + 1; j < balls.length; j++){
if(parseInt(balls[i].style.left) +
parseInt(balls[i].style.width) ==
parseInt(balls[j].style.left) ||
parseInt(balls[j].style.left) +
parseInt(balls[j].style.width) ==
parseInt(balls[i].style.left) &&
parseInt(balls[i].style.top) +
parseInt(balls[i].style.height) ==
parseInt(balls[j].style.top) || parseInt(balls[j].style.top)
+ parseInt(balls[j].style.height) ==
parseInt(balls[i].style.top)) {
speed.x = - speed.x;
speed.y = -speed.y;
}
ball[i].style.left = parseInt(ball[i].style.left) +
speed.x + 'px';
ball[j].style.left = parseInt(ball[j].style.left) +
speed.x + 'px';
ball[i].style.top = parseInt(ball[i].style.top) +
speed.y + 'px';
ball[j].style.top = parseInt(ball[j].style.top) +
speed.y + 'px';
}
}
}
</script>
</body>
</html>
I would recommend moving as much as possible into javascript variables so you don't need to consult the HTML for every parameter.
You had quite the number of typos, among them speed.x = - speed.x; where you meant speed.x = -speed.x; and your code was difficult to read without any comments or helper functions to explain what's going on.
I have fixed your typos and restructured your code in the snippet below. Try checking the developer console, typically by pressing F12, as this will show you code errors with line number and severity rating.
In my snippet below i have tried to move the parameters into JavaScript to show how that would work, while still leaving some on the HTML nodes:
//Basic properties
var width = 700;
var height = 300;
//Get container
var container = document.getElementById("container");
// Set dimensions
container.style.width = width + "px";
container.style.height = height + "px";
//Load balls
var balls = Array.prototype.slice.call(document.getElementsByClassName("circle"))
.map(function(ball) {
return {
HTMLNode: ball,
xPos: parseInt(ball.style.left),
yPos: parseInt(ball.style.top),
xAcc: 3,
yAcc: -3,
size: 50
};
});
//Utility functions
function angleBetween(x1, y1, x2, y2) {
return Math.atan2(y2 - y1, x2 - x1);
}
function distanceBetween(x1, y1, x2, y2) {
return Math.abs(y2 - y1) + Math.abs(x2 - x1);
}
//Draw function
function draw() {
//Loop through balls
for (var ballIndex1 = 0; ballIndex1 < balls.length; ballIndex1++) {
var ball1 = balls[ballIndex1];
//Collide with horisontal wall
if (ball1.xPos > width - ball1.size || ball1.xPos < 0) {
ball1.xAcc = -ball1.xAcc;
}
//Collide with vertical wall
if (ball1.yPos > height - ball1.size || ball1.yPos < 0) {
ball1.yAcc = -ball1.yAcc;
}
//Collide with other balls
for (var ballIndex2 = ballIndex1 + 1; ballIndex2 < balls.length; ballIndex2++) {
var ball2 = balls[ballIndex2];
//Test within collision distance
if (distanceBetween(ball1.xPos, ball1.yPos, ball2.xPos, ball2.yPos) > ball1.size) {
continue;
}
//Get angle of collision
var angle = angleBetween(ball1.xPos, ball1.yPos, ball2.xPos, ball2.yPos);
//Apply force to acceleration
ball1.xAcc = -Math.cos(angle) * 3;
ball2.xAcc = -ball1.xAcc;
ball1.yAcc = -Math.sin(angle) * 3;
ball2.yAcc = -ball1.yAcc;
}
//Apply acceleration to position
ball1.yPos += ball1.yAcc;
ball1.xPos += ball1.xAcc;
//Apply to node
ball1.HTMLNode.style.left = ball1.xPos + "px";
ball1.HTMLNode.style.top = ball1.yPos + "px";
}
}
//Start simulation
setInterval(draw, 1000 / 60);
.circle {
position: absolute;
border-radius: 50%;
height: 50px;
width: 50px;
}
.container {
height: 300px;
width: 300px;
background-color: skyblue;
position: relative;
}
<div class="container" id="container">
<div class="circle" id="circle1" style="background-color:black;
top:0; left:0"></div>
<div class="circle" id="circle2" style="background-color:rgb(197, 100,
100);top:200px;left: 150px"></div>
<div class="circle" id="circle3" style="background-color:brown;top:50px;left: 640px"></div>
</div>
Every time I refresh the page, the positioning of the child div sometimes overflow or position itself outside the parent div. Any ideas of how to prevent this? I tried playing around with size of the child div as well as its "top" and "left" values without any luck.
Here is the code:
#box {
width: 1000px;
height: 400px;
background-color: grey;
margin: 0;
padding: 0;
}
#shape {
display: none;
position: relative;
}
</style>
</head>
<body>
<div id="box">
<div id="shape"></div>
</div>
<script>
function makeShapeAppear() {
var top = Math.floor(Math.random() * 301);
var left = Math.floor(Math.random() * 901);
var size = Math.floor(Math.random() * 101) + 50;
document.getElementById("shape").style.display = "block"
document.getElementById("shape").style.top = top + "px";
document.getElementById("shape").style.left = left + "px";
document.getElementById("shape").style.width = size + "px";
document.getElementById("shape").style.height = size + "px";
document.getElementById("shape").style.backgroundColor = "red";}
</script>
</body>
Thank you in advance.
The reason this occurs is because you do not take into account the random size of the child.
var size = Math.floor(Math.random() * 101) + 50;
That line can produce a size up to 150.
var top = Math.floor(Math.random() * 301);
That line can set the top at a maximum of 300.
The parent div is only 400px tall, so if the size gets computed at 150 and the top at 300 the child will overflow the parent on the bottom by 50px. A similar situation occurs when setting left that will cause the child to overflow on the right of the parent. You need to constrain top and left. Example below.
function makeShapeAppear() {
var size = Math.floor(Math.random() * 101) + 50;
var top = Math.min(Math.floor(Math.random() * 101), 180 - size);
var left = Math.min(Math.floor(Math.random() * 301), 400 - size);
document.getElementById("shape").style.display = "block"
document.getElementById("shape").style.top = top + "px";
document.getElementById("shape").style.left = left + "px";
document.getElementById("shape").style.width = size + "px";
document.getElementById("shape").style.height = size + "px";
document.getElementById("shape").style.backgroundColor = "red";
setTimeout(function() { makeShapeAppear(); }, 1000);
}
makeShapeAppear();
#box {
width: 400px;
height: 180px;
background-color: grey;
margin: 0;
padding: 0;
}
#shape {
display: none;
position: relative;
}
<div id="box">
<div id="shape"></div>
</div>