I'm working on my Javascript project.
In this project I have to create some animations.
In this specific case, I have to make a ball bounce up and down.
The code below works great just from the top to the bottom, but not viceversa.
var div = document.getElementById('container-ball');
function createBall(event)
{
var x = event.clientX;
var y = event.clientY;
var newBall = document.createElement('div');
newBall.style.position = "absolute"
newBall.style.width = '15px';
newBall.style.height = '15px';
var bx = newBall.style.left = x + 'px';
var by = newBall.style.top = y + 'px';
newBall.style.borderRadius = '10px';
newBall.style.backgroundColor = 'white';
var incrementPos = 0;
var id = setInterval(bounce, 5);
function bounce()
{
incrementPos++;
by = newBall.style.top = incrementPos + y + "px";
if(by == 650 + "px")
{
clearInterval(id)
var id2 = setInterval(function bounceUp()
{
incrementPosYMax -= 650
by = newBall.style.bottom = by + "px" - incrementPosYMax
}, 5)
}`/*Function that make the ball bounce down and up(but when it came at 650 px it stopped )*/ì
} /*End of the set interval */`
div.appendChild(newBall);
}
div.addEventListener("click", createBall);
This down below is the HTML CODE
<html>
<head>
<link rel= "stylesheet" href="style/style.css">
</head>
<body>
<div id ="container-ball">
</div>
<script src="js/main.js"></script>
</body>
Working example (comments see below):
const areaHeight = 150; // it is 650px in the original question
var div = document.getElementById('container-ball');
function createBall(event) {
var x = event.clientX;
var y = event.clientY;
var newBall = document.createElement('div');
newBall.className = 'ball';
var bx = newBall.style.left = x + 'px';
var by = newBall.style.top = y + 'px';
var incrementPos = 0;
var id = setInterval(bounce, 5);
let direction = 1; // 1 = down, -1 = up
function bounce() {
incrementPos += direction;
by = newBall.style.top = incrementPos + y + "px";
if (by == areaHeight + "px" || by == y + 'px') {
direction = -direction;
}
}
div.appendChild(newBall);
}
#container-ball {
width: 300px;
height: 157px;
background: gray;
}
#container-ball .ball {
position: absolute;
width: 15px;
height: 15px;
border-radius: 10px;
background-color: white;
border: 1px solid #333;
box-sizing: border-box;
}
<div id="container-ball" onclick="createBall(event)"></div>
Click on the grey box
Now, the explanation.
I've moved ball's styles to CSS - this is easier to control in the future. So when I have a class for ball, I can write in my code: newBall.className = 'ball';
I removed incrementPosYMax because I do not really understand if you need it
I understand your 'bounce' as bounce, so my ball just fall to the floor and then return to the original position. I do not really understand if you mean that (please, comment if it is wrong).
Your program is quite small, so I do not see the need for another setInterval, so all the animation in my example is inside only one setInterval
I've added new variable direction to control the direction for the ball (1 = down, -1 = up)
I do not like the parts with by == areaHeight + "px", but I keep them for you, because you use it in your code.
This code have some bugs, that you (or me if you ask) can fix. I just need to understand that my approach is correct
How the direction works:
Take a look at this line by = newBall.style.top = incrementPos + y + "px"; here you set new "y" coordinate for the ball as sum of 'original' "y" coordinate (in y) and offset (the distance that ball moved over the time) in incrementPos. So, if you increase incrementPos, then the ball's position will be lower (because "zero" in browser is at the top left corner, bigger "y" means lower the element).
Before my change, in your code you changed the offset with this line: incrementPos++; (means you increase incrementPos by 1 on every bounce step).
To move to another direction, you need to subtract 1 on every bounce step.
To reflect the "direction" of that move, I've added direction variable (so 1 means move down, and -1 means move up)
Now the offset is changed by: incrementPos += direction; (so I add this direction, not always 1)
Sometimes we need to change the "direction" with this code: direction = -direction;
The "levels" where we need to change direction is checked by this code: if (by == areaHeight + "px" || by == y + 'px') - here we check bottom (areaHeight) and top (y - it is where user clicks the mouse)
Related
<html>
<div id="my_box_realtime" style="background-color: red; position: absolute; min-width: 100px; min-height: 100px"></div>
<script type="text/javascript">
var x = bottom
var y = right
var d = document.getElementById('my_box_realtime');
var position = 0;
setInterval(function() {}, 500)
position += 1;
d.style.top = position + 'px';
d.style.left = position + 'px';
function my_box_realtime() {
if (position)
}
</script>
</html>
The box needs to move smoothly and slowly to a set coordinate of bottom 0 and right 0.
Any help would be great. Very new to this and it's an assignment I have.
For your problem, we need to:
maintain top & left values separately
Check on Every Interval if the Boxes Bottom & Right have reached the Target Distance in relation to the window
Update the Top & Left Values if necessary
Finally, when the Box is at the Target Location, Clear the Interval.
var d = document.getElementById('my_box_realtime');
var x = 0; // Bottom Target in px
var y = 0; // Right Target in px
var positionTop = 0;
var positionLeft = 0;
let interval = setInterval(function() {
const {
bottom,
right
} = d.getBoundingClientRect();
const clientW = window.innerWidth;
const clientH = window.innerHeight;
if (clientH - bottom !== x) {
positionTop += 1;
d.style.top = positionTop + 'px';
}
if (clientW - right !== y) {
positionLeft += 1;
d.style.left = positionLeft + 'px';
}
if (right === clientW && bottom === clientH) {
clearInterval(interval);
}
}, 50);
<div id="my_box_realtime" style="background-color: red; position: absolute; min-width: 100px; min-height: 100px"></div>
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 3 years ago.
Improve this question
Hi there
so i'm trying to create a bouncing ball using javascript and i need some help finishing it without using any complicated code
this is the code that i have done:
https://codepen.io/messili-islem/pen/XWrjOja
i appreciate your help guys
Thanks
var ball = document.getElementById('ball')
var ballObj = { x: 0, y: 0, dx: 1, dy: 1, size: 100 }
var x = 0
var y = 0
function movement () {
var id = setInterval(moveball, 1)
function moveball () {
function downleft () {
x++
y++
ball.style.top = x + 'px'
ball.style.left = y + 'px'
}
function upright () {
x--
y--
ball.style.top = x + 'px'
ball.style.left = y + 'px'
}
function downright () {
x++
y--
ball.style.top = x + 'px'
ball.style.left = y + 'px'
}
function upleft () {
x--
y++
ball.style.top = x + 'px'
ball.style.left = y + 'px'
}
downleft()
if (x == 400) {
clearInterval(id)
var id2 = setInterval(upleft, 1)
}
}
}
That's a starter code
I use requestAnimationFrame instead of setInterval for time based calculations
I use transform style instead of top and left for better performance
I encapsulated all the code into an anonymous function to prevent global namespace pollution
I used EventListener instead of onclick on button
"use strict";
// This is a self-invoking anonymous function
// It's used to encapsulate the inner variables
// and functions in order for the global namespace
// to not be polluted and to avoid naming conflicts
// See: https://stackoverflow.com/questions/592396/what-is-the-purpose-of-a-self-executing-function-in-javascript
// See: https://idiallo.com/javascript/self-invoking-anonymous-function
(function() {
var ball = document.getElementById("ball");
var container = document.getElementById("container");
var btn = document.getElementById("btn");
// some factor to scale the speed of the ball
var factor = 1 / 5;
var timeOld, timeElapsed;
// the current x position of the ball
var x = 0;
var y = 0;
var step = 1;
// the direction in the x dimension (1 or -1)
var dx = step;
var dy = step;
var width = ball.offsetWidth;
var height = ball.offsetHeight;
var cH = container.offsetHeight;
var cW = container.offsetWidth;
function movement() {
// use requestAnimationFrame in favor of setInterval/setTimeout
// See: https://css-tricks.com/using-requestanimationframe/
requestAnimationFrame(moveball);
// check the balls position and set the direction if out of bounds
function checkBall() {
if (x + width >= cW) dx = -step;
if (x <= 0) dx = step;
if (y + height >= cH) dy = -step;
if (y <= 0) dy = step;
}
// move the ball by (dx,dy)
function moveball(timestamp) {
// measure the time elapsed since
// last call to moveball function
if (!timeOld) timeOld = timestamp;
timeElapsed = timestamp - timeOld;
timeOld = timestamp;
// calculate ball's position based on
// movement's direction and time elapsed
x += dx * timeElapsed * factor;
y += dy * timeElapsed * factor;
// use CSS transform instead of top and left
// for performance reasons
// See: https://www.keycdn.com/blog/animation-performance#there-are-three-main-types-of-css-properties
ball.style.transform = "translate(" + x + "px, " + y + "px)";
checkBall();
// call requestAnimationFrame again
// like you would do with
// setTimeout
requestAnimationFrame(moveball);
}
}
btn.addEventListener('click', movement)
})();
#container {
width: 450px;
height: 300px;
position: relative;
border: 1px solid black;
margin: auto;
}
#ball {
border-radius: 100px;
width: 100px;
height: 100px;
position: absolute;
background-image: radial-gradient(farthest-side at 30px 30px, red, black 125%);
}
<p><button id="btn" >movement()</button></p>
<div id="container">
<div id="ball"></div>
</div>
Everything was going smoothly until now. I want to have this hor() function reverse after 20 seconds. The original hor() function grabs an image offscreen and moves it horizontally from the left to the center of the page. I'd like to create a function that does the opposite after 20 seconds. The "after 20 seconds" part is giving me the most grief. If you could show me what a new function would look like or an 'else' addition to the current function that would be great. Thanks.
<script language="JavaScript" type="text/JavaScript">
var x = -500;
var y = 100;
function hor(val) {
if (x <= 500){
x = x + val;
document.getElementById("pos").style.left = x + "px";
setTimeout("hor(5)", 10);
}
}
</script>
<style type="text/css">
<!--
#pos {
position: absolute;
left: -500px;
top: 100px;
z-index: 0;
}
</style>
<body onLoad="setTimeout('hor(5)',5000)">
<div id="pos">
<img src="image.jpg">
</div>
Perhaps you could try changing the script to this:
// Define variables
var x = -500,
y = 100,
direction = 1;
// Function which moves "pos" element
function hor(val) {
// Move by specified value multiplied by direction
x += val * direction;
if(x > 500){
x = 500;
// Reverse direction
direction = -1;
} else if(x < -500){
x = -500;
// Reverse direction, once again
direction = 1;
}
// Move element
document.getElementById("pos").style.left = x + "px";
// Continue loop
setTimeout("hor("+val+")", 10);
}
This code will continue moving the pos element by the specified value until x greater than 500, at which point it will switch direction, then continue until x reaches -500, and so on.
EDIT:
This code will fix that issue with 20 seconds (I had thought that the 500 pixel thing computed to 20 seconds, lol).
// Define variables
var x = -500,
y = 100,
direction = 1;
firstCall = true;
timeElapsed = 0;
// Function which moves "pos" element
function hor(val) {
// Don't let the first call count!
if(!firstCall)timeElapsed += 10;
else firstCall = false;
if(timeElapsed >= 2000){
// Reverse direction
direction *= -1;
timeElapsed = 0;
}
// Move by specified value multiplied by direction
x += val * direction;
// Move element
document.getElementById("pos").style.left = x + "px";
// Continue loop
setTimeout("hor("+val+")", 10);
}
Try this. Here the img is moved horizontally and after sometime it is reverted back to its original position.
<script>
var x = -500;
var y = 100;
var op;
function hor(val) {
if (x <= 500 && x>=-500) {
x = x + val;
document.getElementById("pos").style.left = x + "px";
setTimeout(function(){hor(val);},10);
}
}
$(document).ready(function(){
setTimeout(function(){hor(5);},1000);
setInterval(function(){
setTimeout(function(){hor(-5);},1000);
},2000);
});
</script>
I have changed the timings for testing purpose only, you can change it back.
I am a JS noob. I am getting into browser game programming and wanted to make a quick example of a ball dropping and bouncing just to learn. For some reason, when I created a jsfiddle my code actually didn't work, the onclick event for my div id="ball" didn't seem to be attaching, but when I run it in my browser it does. But that is not my question.
In this code, the user clicks the ball, which is just a div with a black bg. The div then follows the users cursor, and when the user clicks a second time, the div begins to fall towards the bottom of the window. When it hits the bottom, it should bounce back up, with an apex half the distance between the y coordinate of where it was originally dropped and the bottom of window. So if it was dropped at y position 600 and the bottom of the page is 800, the apex for the first bounce should be 700. The 2nd bounce, the apex would be 750. 3rd bounce, 775. You get the idea. Can someone help me a bit here? I am guessing I need to increment a counter each time the ball hits the bottom?
<html>
<head>
<style>
#ball {
width: 50px;
height: 50px;
background-color: black;
position: absolute;
}
</style>
<script>
window.onload = function() {
var ballClicked = false;
var ballFalling = false;
var ballX = 100;
var ballY = 100;
var timesBounced = 0;
var bounceApex = 0;
var startingDropHeight = 0;
var intervalVar;
var ball = document.getElementById("ball");
ball.style.left = ballX;
ball.style.top = ballY;
ball.onclick = function() {
if (ballClicked == false) {
ballClicked = true;
} else {
ballClicked = false;
ballFalling = true;
startingDropHeight = ballY;
intervalVar = setInterval(function(){dropBall()} , 5);
}
};
document.onmousemove = function(e) {
if (ballClicked == true) {
ballX = e.pageX;
ballY = e.pageY;
ball.style.left = ballX;
ball.style.top = ballY;
}
};
function dropBall() {
if (ballFalling == true) {
ballY = ballY + 1;
ball.style.top = ballY;
if (ballY == window.innerHeight - 50) {
timesBounced = timesBounced + 1;
bounceApex = (startingDropHeight + (window.innerHeight - 50)) / 2;
ballFalling = false;
if (bounceApex > window.innerHeight - 50) {
clearInterval(intervalVar);
}
};
} else {
ballY = ballY - 1;
ball.style.top = ballY;
if (ballY == bounceApex) {
ballFalling = true;
};
}
};
};
</script>
</head>
<body>
<div id="ball"></div>
</body>
</html>
When adding left and top styles, you need to specify the unit as well. So, instead of:
ball.style.left = 100;
it should be:
ball.style.left = "100px";
I've fixed that in your code and made a working jsfiddle, will improve the bouncing in a bit. See it here: http://jsfiddle.net/12grut99/
About the repetitive bouncing, this line is the issue:
bounceApex = (startingDropHeight + (window.innerHeight - 50)) / 2;
You're always calculating the apex based on the original drop height, yet after every bounce, the drop height should be the previous bounceApex (the highest point the ball reached).
I'm working on a portal/dashboard type interface which has panels/widgets that can be freely dragged around the dashboard space as long as they don't overlay any other panels. New panels can be added to the dashboard via a menu containing all available panels, and when a menu item is clicked, the panel is placed into the dashboard. The panels currently occupying the dashboard space are all represented in an object like this:
{
'panel_1': { top: 0, left: 0, width: 300, height: 350 },
'panel_2': { top: 0, left: 370, width: 200, height: 275 },
'panel_3': { top: 370, left: 0, width: 275, height: 400 },
...
}
My question is, what is a valid algorithm that would, when the user clicks one in the menu, correctly place a new panel (of a given width and height) in unoccupied space that is closest to a left and top (x and y) value of 0, 0, without overlapping any of the existing panels?
I think, simple bruteforce algorithm will fit you. As I remember, fit rectangle solve another problem
Iterate over your dashboard axis to find out, whether you can place your rectangle, until X < rectangle.widh + dashboard.width, same for Y.
Foreach X,Y on dashboard iterate over every panel to find whether they overlap. You can apply some optimization, to decrease amount of iteration. If panel overlap rectangle, you can increase X or Y(which is in nested loop) not by 1, but by width or height of panel.
In most cases, you will not make dashboard.width*dashboard.height*panel.count iteration. With some optimization, it will find best fit rather quick
I know this is an old question but if anyone wants a proof of concept then it looks like this:
function findSpace(width, height) {
var $ul = $('.snap-layout>ul');
var widthOfContainer = $ul.width();
var heightOfContainer = $ul.height();
var $lis = $ul.children('.setup-widget'); // The li is on the page and we dont want it to collide with itself
for (var y = 0; y < heightOfContainer - height + 1; y++) {
var heightOfShortestInRow = 1;
for (var x = 0; x < widthOfContainer - width + 1; x++) {
//console.log(x + '/' + y);
var pos = { 'left': x, 'top': y };
var $collider = $(isOverlapping($lis, pos, width, height));
if ($collider.length == 0) {
// Found a space
return pos;
}
var colliderPos = $collider.position();
// We have collided with something, there is no point testing the points within this widget so lets skip them
var newX = colliderPos.left + $collider.width() - 1; // -1 to account for the ++ in the for loop
x = newX > x ? newX : x; // Make sure that we are not some how going backwards and looping forever
var colliderBottom = colliderPos.top + $collider.height();
if (heightOfShortestInRow == 1 || colliderBottom - y < heightOfShortestInRow) {
heightOfShortestInRow = colliderBottom - y; // This isn't actually the height its just the distance from y to the bottom of the widget, y is normally at the top of the widget tho
}
}
y += heightOfShortestInRow - 1;
}
//TODO: Add the widget to the bottom
}
function isOverlapping($obsticles, tAxis, width, height) {
var t_x, t_y;
if (typeof (width) == 'undefined') {
// Existing element passed in
var $target = $(tAxis);
tAxis = $target.position();
t_x = [tAxis.left, tAxis.left + $target.outerWidth()];
t_y = [tAxis.top, tAxis.top + $target.outerHeight()];
} else {
// Coordinates and dimensions passed in
t_x = [tAxis.left, tAxis.left + width];
t_y = [tAxis.top, tAxis.top + height];
}
var overlap = false;
$obsticles.each(function () {
var $this = $(this);
var thisPos = $this.position();
var i_x = [thisPos.left, thisPos.left + $this.outerWidth()]
var i_y = [thisPos.top, thisPos.top + $this.outerHeight()];
if (t_x[0] < i_x[1] && t_x[1] > i_x[0] &&
t_y[0] < i_y[1] && t_y[1] > i_y[0]) {
overlap = this;
return false;
}
});
return overlap;
}