I'm trying to make a smooth javascript animation that makes a div slide from position left 900 to 550 on the screen..
With two variables; place and speed, I use place to locate where the div is on the screen, and speed to decide the speed.
To make this nice and flowy I've tried to make the speed go slower and slower, so the slide starts fast and goes slower and slower.
I don't know if I'm trying to do the right thing here, but basically, I want the speed to start at 100 %, lets say 50 px, and go a percentages slower for each time.
The speeds percentages should be equal to the number of my place.
So that place starts at 900, and ends at 550.
The speed should start at 50 and go a percentages slower for each time and end at 0 as the place ends at 550...
How do I set this up???
I've tried this:
function doit(place, speed, proc) {
var denne = document.getElementById("screen1");
if (place > 550) {
var speedproc = 100 - (place / 950 * 100); // (the reason that I'm using 950 here is because it should have a percentage to start from that isn't 0)
var newspeed = speed - (speed / 100 * proc);
speed = newspeed;
proc += speedproc;
place -= speed;
denne.style.marginLeft = place + "px";
setTimeout("doit(" + place + ", " + speed + ", " + proc + ")", 20);
}
}
And the body:
<body onload='doit(900, 50, 0);'>
But it acts out crazy... What am I doing wrong?
All in all I would do something like this to create the slow down effect:
newspeed = speed*proc; //Now, proc is a number between 0 and 1.
//the closer proc is to 1, the smaller the decrease will be.
// when proc is 1 - there will be no decrease in speed
For example:
<body onload='doit(900, 50, 0.9);'>
Related
I would like to animate the moving behavior of my thumbnails divs. I want each individual div to move differently. The grid itself is also infinite scrollable in both directions. At the moment I have the following problem:
The divs are moving all to the left side of the page
At some point the animation stops
Sometimes the grid flickers during the first initial scroll
You can see the result in this pen:
https://codepen.io/Dennisade/pen/OJymaKZ
This is how I set up the css:
CSS:
.grid-image {
text-align: center;
width: 50%;
margin-bottom: 18%;
pointer-events: none;
will-change: transform;
transition: 20s linear
}
JS
//MOVING ANIMATION
setInterval(function() {
$('.grid-image').each(function(index){
var yAxis= index * Math.floor(Math.random()-2);
var xAxis= index * Math.floor(Math.random()-1);
$('.grid-image').css('transform', 'matrix(1, 0, 0, 1, ' + yAxis + ',' + xAxis + ')');
});
}, 50);
//ENDLESS SCROLL
var origDocHeight = document.body.offsetHeight;
var clone = $(".wrapper").contents().clone();
clone.appendTo(".wrapper");
clone.prependTo(".wrapper");
$(document).scroll(function(){
var scrollWindowPos = $(document).scrollTop();
if(scrollWindowPos >= origDocHeight ) {
$(document).scrollTop(0);
}
if(scrollWindowPos <= 0 ) {
$(document).scrollTop(origDocHeight);
}
});
there seem to be two separate parts here. First, the divs are not moving correctly and then they stop moving altogether. Second, scrolling isn't working properly.
Looking at div movement first:
I am not familiar with jquery but one of your problems seems to be that this line
$('.grid-image').css('transform', 'matrix(1, 0, 0, 1, ' + yAxis + ',' + xAxis + ')');
does not update the transform settings for the current element but changes the CSS for all elements of class grid-image. To ensure each div gets its new matrix you can change the above line to
this.style.transform='matrix(1, 0, 0, 1, ' + yAxis + ',' + xAxis + ')';//incidentally did you want the x and y this way round?
Another problem is that all the divs apart from the first will still end up with the same xAxis and yAxis in their transform matrix. This is because Math.floor(Math.random()) is equivalent to Math.floor(Math.random()*1) which gives a random integer from 0 (inclusive) to 1 (exclusive), so it is always 0. Depending on how big you want these values to be you can use a higher integer. For example, Math.floor(Math.random()*10) gives a random integer from 0 to 9.
I do not know whether you intended this or not, but the first div will never have its transform changed as its index is 0 so these lines always set xAxis and yAxis = 0
var yAxis= index * Math.floor(Math.random()-2);
var xAxis= index * Math.floor(Math.random()-1);
Adding 1 to the index, for example, would ensure the first div gets new values.
Now looking more closely at the calculations for the axes, CSS transform matrix interprets the last two of its parameters as translateX and translateY. These are relative to the current position of the element. In the original the values were always negative so all the elements moved gradually to the left and (less noticeably as the increment was smaller) up.
Using your Codepen my laptop became very hot, with the fan going crazy - there is a lot of calculation going on with a matrix transform on so many elements 20 times a second. I have attempted to mitigate this by noticing that you are not scaling or skewing elements so a translate transform instead of a matrix transform would do.
Putting these things together if we replace the setInterval call with
setInterval(function() {
$('.grid-image').each(function(index){
var yAxis= (index+1) * Math.floor(Math.random()*1000) * (Math.random() < 0.5 ? -1 : 1);
var xAxis= (index+1) * Math.floor(Math.random()*1000) * (Math.random() < 0.5 ? -1 : 1);
this.style.transform='translate('+xAxis+'px,'+yAxis+'px)';
});
}, 50);
we get random-looking motion of all the divs. The amount of motion of course depends on both the time interval and the calculations for the x and y increments and I have *1000 here to get some easily visible motion. It needs to be more if you want elements to move across the screen more. I've kept the index in the calculations but I'm not sure if that's what you intended - elements further down move more than those at the top, and then as you scroll you find slower ones again (so the overall effect is to have a hint of a pattern rather than total randomness).
Using this code I have not seen the animation stop - and have run it for many minutes. The laptop got hot but not so much. I suppose there could be a question around whether less powerful processors can complete all the matrix transforms in the time interval. If that proves to be a problem then moving to setTimeout would be helpful as would any CSS changes that encouraged use of GPUs.
Hope this helps with your first two points.
Now to look at the scrolling. This code
var clone = $(".wrapper").contents().clone();
clone.appendTo(".wrapper");
clone.prependTo(".wrapper");
puts just one copy of the content div into the .wrapper div. I think this is because it gets appended, then prepended which means it isn't anymore appended i.e. it can't be in two places at once. Replacing it with this code gives 3 copies of a content div which I guess you know will be enough for the sizes of display your code will be used on
var clone = $(".wrapper").contents().clone();
var clone2 = $(".wrapper").contents().clone();
clone.appendTo(".wrapper");
clone2.prependTo(".wrapper");
Scrolling back now works but forward has a problem. In this code
$(document).scroll(function(){
var scrollWindowPos = $(document).scrollTop();
if(scrollWindowPos >= origDocHeight ) {
$(document).scrollTop(0);
}
if(scrollWindowPos <= 0 ) {
$(document).scrollTop(origDocHeight);
}
});
where scrollWindowPos >= origDocHeight it scrolls to 0 which means next time scrollWindowPos <=0 is true and it jumps. Replacing with this code enables scrolling forward
$(document).scroll(function(){
var scrollWindowPos = $(document).scrollTop();
if(scrollWindowPos >= origDocHeight ) {
$(document).scrollTop(1); /* used to be 0 - using 1 is a bit hacky but it does the job */
}
else if(scrollWindowPos <= 0 ) {
$(document).scrollTop(origDocHeight);
}
});
It is not perfect. Scrolling by grabbing the scroll bar and moving it downwards creates 'flashes' but scrolling by selecting the scroll bar's downward arrow gives smooth scrolling, no flashes (Windows 10, Edge and Chrome). I can't think at the moment how to investigate this further.
I had to remove the display:none on the body which is in the CSS on Codepen in order to get a scroll bar, so it may be that I have misunderstood what you wanted from scrolling.
Here is the complete JS code with all the above changes in it
//MOVING ANIMATION
setInterval(function() {
$('.grid-image').each(function(index){
var yAxis= (index+1) * Math.floor(Math.random()*1000) * (Math.random() < 0.5 ? -1 : 1);
var xAxis= (index+1) * Math.floor(Math.random()*1000) * (Math.random() < 0.5 ? -1 : 1);
this.style.transform='translate('+xAxis+'px,'+yAxis+'px)';
});
}, 50);
//ENDLESS SCROLL
var origDocHeight = document.body.offsetHeight;
var clone1 = $(".wrapper").contents().clone();
var clone = $(".wrapper").contents().clone();
clone.appendTo(".wrapper");
clone1.prependTo(".wrapper");
$(document).scroll(function(){
var scrollWindowPos = $(document).scrollTop();
if(scrollWindowPos >= origDocHeight ) {
$(document).scrollTop(1);
}
else if(scrollWindowPos <=0 ) {
$(document).scrollTop(origDocHeight);
}
});
I'm making a 2D game in JavaScript. For it, I need to be able to "perfectly" check collision between my players(the game has two players, open the picture please) and the walls! I mean, I have a function that actually works, but when I make them jump against the walls they pass through the walls and keep moving until they reach another area or even leave the canvas!
Also, if they are falling down and I make them collide with a wall, they just stop there wich is also pretty bad!
I really need help with that!! It's a university project and I have to finnish it really soon!
My game looks like this
The collision detection function I have is here:
function blockRectangle (objA, objB) {
var distX = (objA.x + objA.width / 2) - (objB.x + objB.width / 2);
var distY = (objA.y + objA.height / 2) - (objB.y + objB.height / 2);
var sumWidth = (objA.width + objB.width) / 2;
var sumHeight = (objA.height + objB.height) / 2;
if (Math.abs(distX) < sumWidth && Math.abs(distY) < sumHeight) {
var overlapX = sumWidth - Math.abs(distX);
var overlapY = sumHeight - Math.abs(distY);
if (overlapX > overlapY) {
objA.y = distY > 0 ? objA.y + overlapY : objA.y - overlapY;
}
else {
objA.x = distX > 0 ? objA.x + overlapX : objA.x - overlapX;
}
}
}
I did the walls with a maze and I'm using a for cycle to check the collisions with all of the walls I have saved in an array!
As you can see here:
for (var i in walls) {
var wall = walls[i];
if ((player.x < (wall.x + wall.width)) && ((player.x + player.width) > wall.x) && (player.y < (wall.y + wall.height)) && ((player.height + player.y) > wall.y)) {
player.falling = false;
}
blockRectangle(player, wall);
}
Please help me!!! Thank you all!
In your case I doubt a pixel perfect collision is required.
You can maintain a boolean matrix to store the position of solid objects. Solid objects like walls or players. Then in every frame you can check if your player is trying to move to a position where there is a solid object, if it is then stop it. You don't have to create grid of width x height in pixels, but rather choose a largest block (single element in the grid) in which each solid object reasonably occupies most of the block.
For example you can choose block size to be player_width / 2 x player_height /2.
See following image with grid
Another simple way could be to just check the background pixel color. Since your game is simple, background and object colors are different. So you just have to check if the player is trying to move somewhere where pixel color is not of background, thus there is a solid object and player should stop. You don't have to test for a lot of pixels, just 1 pixel in the direction the player is trying to move. (1 for horizontal and 1 for vertical). This however can not be used if you don't have a clear background color. Background color here is kind of the boolean grid for us in the previous suggestion.
My project currently features quizzes, which have a countdown timer using Javascript. This time then resets to 7 seconds after it has counted down fully or a the user selects a multiple-choice answer.
That function is working, but it just shows how much time is left, in text. I want to make a "bar" (think: column) that counts down (so, if a quiz gives you 7 seconds, the bar reduces 1/7th each time). jQuery and Javascript seem to be the easiest way to do this, so I added:
var height = 50;
var timePassed = 0;
var totalSeconds = 7;
var reduceHeight = function(elem, totalSeconds){
timePassed++;
height = 100 - (100/totalSeconds * timePassed);
elem.css({height: height + "%"});
if(height > 0){
window.setTimeout(function(){
reduceHeight(elem, totalSeconds)
}, 1000)
}
}
$(document).ready(function(){
var inner = $('#inner');
reduceHeight(inner, 20);
});
The bar now appears and does count down, but it is not completing reduceHeight at the correct rate.
Since I am defining the totalSeconds variable, it seems like it should since height is being set at 100/totalSeconds * timePassed. To reiterate, as the timer will run for 7 seconds, I would want it to become 1/7th smaller in each passing second.
Any idea where I went wrong?
I think your height calculation logic is incorrect
it should be
height = 100 - ( ( timePassed/totalSeconds ) * 100 );
since for example totalSeconds is 20 and timePassed is 10
then you height should be 50,
height = 100 - ( ( 10/20 ) * 100 ) = 50;
Which means that you need to
get the ratio of timePassed with totalSeconds
and then get the percentage out of that ratio
so that you can reduce that percentage value from 100
So I just changed my game update function to update alle movements depending on the difference between the actual FPS and the fixed game FPS.
For example I set my game to a fixed 60 FPS.
If the actual FPS is 20 I calculate the difference like that:
((20 * 100) / 60) / 100 which would result in 0.333
So the difference of 1 game loop would be 1 - 0.333 = 0.677
Thus all game movements wouldn't be updated based on a normal game loop:
x += speedX * 1 but width an increased value: x += speedX * 1.677
So now my game runs at the same speed on every system, just with less pictures, but thats not the problem.
I'm not entirely sure if that causes the collisoin detection to fail, but I guess as the game speed gets increased to match the current fps, the collision detection isn't able to track all events anymore because positions get skipped. It will only check for collision around the player.
How do I manage this problem ?
Edit:
Here is an example of the collision detection:
var xi = this._game.level.getField(this.x);
var yi = this._game.level.getField(this.y);
var left = this._game.level.levelData["col"][(xi-1) + "," + yi] ? (xi-1) * 32 : null;
var right = this._game.level.levelData["col"][(xi+1) + "," + yi] ? (xi+1) * 32 : null;
var top = this._game.level.levelData["col"][xi + "," + (yi-1)] ? (yi-1) * 32 : null;
var bottom = this._game.level.levelData["col"][xi + "," + (yi+1)] ? (yi+1) * 32 : null;
// Will hit ground ?
if (this.y + this.height >= bottom && bottom != null) {
this.ground = true;
if (!this.jumping) this.y = bottom - this.height;
}
// Left
if (this.x <= left + 32 && left != null) {
this.x = left + 32;
}
// Right
if (this.x + this.width >= right && right != null) {
this.x = right - this.width;
}
this is the player.
and left/right are the collision tile positions on the grid.
Each tile is 32x32 and the player is 25x25.
Here is the full game: [removed]
Edit: The problem is actually somewhere else since bottom is not null.
When I put the player at bottom - (this.height + 1) instead of bottom - this.height (1 pixel higher), he doesn't allways fall through.
Im on my phone right now so I can't check out your sample. So if I am reading your collision detection stuff right, then you update the players position and then check for a collision. This, if the frame rate is low enough or the object is moving fast enough, means that an object can "phase" right through a barrier. Being entirely on one side of it before the frame update and being entirely on the opposite side of it after the frame update.
There are a couple ways to address this problem, the most direct is to not only test where the object is now, but where it's been as well while it moved. Implement a collision detection algorithm that detects all the zones that a player or object has to pass through to get from a to b and base your collision on that.
Also checkout gamedev.stackexchange.com. It's usually a good place to ask game specific questions.
I have the following code
function randomizer(start, end)
{
return Math.floor((end - start) * Math.random()) + 1;
}
var top_pos = randomizer(1, $(document).height());
$('.element_selector').css('top', top_pos + 'px');
but the result is not what I realy expect from. The most of the times the element is places near to top (about 80% of the times).
Is there any better solution to place my random element in realy random position into vertical axes ?
Math.random() will return a value like 0.318 etc.
you could do Math.random() * 10 (or *20) to increase the value.