So I have an element that is using CSS3 transitions to move across the page. I'm trying to see how the actual output FPS of that animation on the page is (for instance, if the page is outputting at 5FPS, a div moving from 0px to 10px at a transition value of 1s should report back 2px, 4px, 6px, etc).
Instead, I just get whatever value I already set the div's position to.
// css has defined a transition of 10s on the moving div
document.getElementById("movingDiv").style.left = "0px";
console.log(document.getElementById("movingDiv").style.left); //outputs 0px
document.getElementById("movingDiv").style.left = "100px";
window.setTimeout(function(){
console.log(document.getElementById("movingDiv").style.left); //outputs 100px instead of, for instance, 43px or wherever the div would visually appear to be
}, 3000);
That's not the exact code, but just some that's generic enough to illustrate my point.
Restating the question, how would I find where an element visually appears to be during its transition between one position and another? I'm not using jQuery animations as many others have answered for, and don't just want to calculate where the element should be. I want to see where the element actually appears to be on the page. I would also like if this works off-screen as well (like to the left of or above the visible window).
To help see why I'm actually trying to do this, is that I'm trying to get the FPS output of the page. I have seen many cases where the page outputs terrible FPS but Javascript still outputs over 100 FPS because the Javascript can run faster than the page can render itself which I'm trying to avoid.
You can use window.requestAnimationFrame:
var moving = false,
el = document.getElementById("mover");
el.className = el.className + " move-right";
el.addEventListener('transitionend', function () {
moving = true;
});
function getPosition() {
var rect = el.getBoundingClientRect()
console.log(rect.top, rect.left);
if (!moving) {
window.requestAnimationFrame(getPosition);
}
}
window.requestAnimationFrame(getPosition);
http://jsfiddle.net/ob7kgmbk/1/
Related
I built a basic picture carousel a while back, and I'm finally getting around to transferring it from MooTools over to jQuery so I can drop MooTools. I've got the script completely functional, but for whatever reason when the carousel slides in one direction, you can see a "pop" where it resets itself.
I've tried playing around with the order it handles everything, but no matter what it seems to always desync for just a fraction of a section.
Here's a copy of my code: https://jsfiddle.net/Chaosxmk/pf6dzchm/
The offending section of code is this:
styles['position'] = 'absolute';
styles[self.params.axis] = -32768;
$(self.list[0]).css(styles).hide();
$(self.list[0]).appendTo(self.carousel);
$(self.list[conf.mi]).css(self.params.axis, (100-conf.pr)+'%');
styles = {};
styles['position'] = 'relative';
styles[self.params.axis] = 'auto';
$(self.list[conf.mi]).css(styles);
Issue is that $.fadeOut() sets display:none on the element, which causes some strange rendering issues in your setTimeout() callback. Works better if you use $.fadeTo() instead:
if (self.params.direction) {
// Go forward
self.carousel.css(self.params.axis, '-'+conf.pr+'%');
$(self.list[0]).fadeTo(400, 0);
$(self.list[conf.mi]).css(self.params.axis, '100%').fadeTo(400, 1);
} else {
// Go backward
self.carousel.css(self.params.axis, conf.pr+'%');
$(self.list[conf.mi-1]).fadeTo(400, 0);
self.list.last().css(self.params.axis, '-'+conf.pr+'%').fadeTo(400, 1);
}
For simplicity I used a 400ms duration, but you can set this to whatever you need.
JSFiddle
I'm trying to implement an HTML infinite scroller in which at any given time there are only a handful of div elements on list (to keep the memory footprint small).
I append a new div element to the list and at the same time I'm removing the first one, so the total count of divs remains the same.
Unfortunately the viewport doesn't stay still but instead it jumps backwards a little bit (the height of the removed div actually).
Is there a way to keep the viewport still while removing divs from the list?
I made a small self contained HTML page (well, it still needs JQuery 3.4.1) which exposes the problem: it starts by adding 5 divs and then it keeps adding a new one and removing the first one every 1 second
function getRandomColor() {
var letters = '0123456789ABCDEF';
var color = '#';
for (var i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
}
function removeit() {
// remove first one
var tiles = $(".tile");
$(tiles[0]).remove();
}
function addit() {
// append new one
var jqueryTextElem = $('<div class="tile" style="height:100px;background-color:' + getRandomColor() + '"></div>');
$("#inner-wrap").append(jqueryTextElem);
}
function loop() {
removeit();
addit();
window.setTimeout(loop, 1000);
}
addit();
addit();
addit();
addit();
addit();
loop();
<div id="inner-wrap"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
You can temporarily add position: fixed to the parent element:
first add position: fixed to the parent;
then remove the item;
then remove position: fixed from the parent
I have a feeling you're trying to have your cake and eat it, in that if you get the viewport to be "still", I think you're meaning you don't want a user to see the scrollbar move and then also not have any new affordance to scroll further down the page, because you would want the scrollbar thumb/grabber to still sit at the bottom of the scrollbar track?
I mean, you could just use $(window).scrollTop($(window).scrollTop() + 100); in your example to make it so the scroll position of the viewport won't visually move when removing elements, but at that point, you wouldn't be keeping the users view of the current elements the same or even allowing a user to have new content further down the page to scroll towards. You'd just be "pushing up" content through the view of the user?
If you are trying to lighten the load of what is currently parsed into document because you are doing some heavy lifting on the document object at runtime, maybe you still want to remove earlier elements, but retain their geometry with some empty sentinel element that always has the height of all previously removed elements added to it? This would allow you to both have a somewhat smaller footprint (though not layout-wise), while still having a usable scrollbar that can communicate to a user and both allow a user to scroll down, towards the content that has been added in.
All in all, I think what you currently have is how most infinite scrollers do and should work, meaning the scroll position and scrollbar should change when content is added in the direction the user is scrolling towards, this communicates to them that they can in fact keep scrolling that way. You really shouldn't want the viewports scroll position to be "still".
To see more clearly why I don't think you have an actual issue, replace your loop() definition with something like this...
function loop() {
$(window).scroll(function() {
// check for reaching bottom of scroller
if ($(window).scrollTop() == ($(document).height() - $(window).height())) {
addit();
removeit();
}
})
}
I would like to move my image down the screen from the top left to the bottom left. I call two functions when the body loads:
window.onload = function() {
MoveRight();
MoveDown();
};
I then retreive the width and height of the clients browser window (to ensure the animation stops when it reaches the sides of the window):
document.body.style.height = height;
document.body.style.width = width;
The function "MoveDown()" is this:
function MoveDown(){
for(var i = 0; i < ; i++)
{
document.getElementById("Amanda").style.top=+i;
}
}
For some reason when I load the webpage, the image just sits in the top left. I had hoped the for loop would increment the "top" value by 1px every time, until such time that it was touching the bottom of the window when it would stop.
If it helps, the image position is set to relative with left and top both set to 0px.
If anyone could help it would be great.
*I collect the width as I want the image to move diagonally but figured that if I got moving down figured out I could easily change the code to make it go sideways at the same time.
The reason it's not moving is most likely (depending on browser) because you're not setting the units. Try
document.getElementById("Amanda").style.top=i+"px";
However, you'll find that it jumps straight down rather than animating. The reason is your loop executes all in one go without giving the browser a chance to redraw. There are a number of ways of getting around this, but one simple one would be like this
function MoveDown() {
var i=0;
function step() {
document.getElementById("Amanda").style.top=i+"px";
i++;
if (i<=100) setTimeout(step,10);
}
step();
}
Do you have position: absolute or position: relative (or position: fixed) as styling for your image?
Asking this because top applies only to positioned elements (and by default elements have position: static which is they are not explicitly positioned).
See https://developer.mozilla.org/en-US/docs/Web/CSS/top and https://developer.mozilla.org/en-US/docs/Web/CSS/position
On rereading your question, this loop of yours looks like an endless loop. Consider adding a stop rule for it, or as suggested in the comments - if you do not need some kind of sliding animation, just put css rule for bottom: 0
You'll want to use setTimeout or setInterval (I can never remember) with some interval and a function that increments the top value every time it runs. Then cancel the timeout/interval when the image reaches it's destination!
Hello StackOverflow Community,
what I am trying to achieve is a header that can be moved with the mouse.
You klick into the header and drag the mouse and the elements inside the header will move with different speeds.
I achieved the parallaxing part but the performance is not really good. It is partially a bit laggy while dragging the backgrounds.
My question now is: what can be changed in the code to get a performance boost?
That's the part of the code that takes care of parallaxing. On every mousemove a each loop is executed which I think is the reason for the performance beeing so laggy:
var dragging = false;
var clickMouseX;
//Our object for the layers
//each layer has a different scrolling speed
var movingObjects = {
'#header-l1' : {'speed': 1},
'#header-l2' : {'speed': 1.4},
'#header-l3' : {'speed': 1.85},
'#header-l4' : {'speed': 2.2},
};
$('#header-wrapper').mousedown(function(e){
dragging = true;
//Get initial mouse position when clicked
clickMouseX = e.pageX;
$(this).mousemove(function(mme){
//execute only if mousedown
if(dragging){
//iterate through all layers which have to be parallaxed
$.each(movingObjects, function(el, opt){
var element = $(el);
//get difference of initial mouse position and current mouse position
var diff = clickMouseX - mme.pageX;
//scroll-position left speed 1
if(diff < 0) diff = -1;
//scroll position right speed 1
if(diff >= 0) diff = 1;
//get current position of layer
currLeft = parseInt(element.css('left'));
//get current layer width
elWidth = element.width();
//if right border is reached don't scroll further
if(currLeft < -(elWidth - 810)){
element.css('left', -(elWidth - 810));
}
//so do with left border
if(currLeft > 0){
element.css('left', 0);
}
//parallax it! Subtract the scroll position speed multiplied by the speed of the desired
//layer from the current left property
element.css('left', parseInt(element.css('left')) - diff*opt.speed);
});
}
});
/* Cursor */
$(this).css('cursor', 'pointer');
return false;
});
I also put a fiddle up:
http://jsfiddle.net/yWGDz/
Thanks in advance,
Thomas
P.S. maybe someone even finds out why layer two and three have the same scroll speed while having different speeds defined.
I worked at this a bit, and came up with this: http://jsfiddle.net/amqER/2/
This works a lot faster than the original (especially in firefox, where it performs a whole lot better, chrome it's still pretty slow). I also changed up some of the logic in your code, to make it make more sense.
A list of things that I did:
Minify your pngs
2 of your png files were over 2 megs, so I threw them into a png compressor (tinypng) and that reduced the size a lot. This helps with loading time and overall snappiness.
Re-use values as much as possible
In your original code, you wrote to and then subsequently read from the css left property a couple times in your code. Doing this is going to make it a lot slower. Instead, I kept an left property, and would only touch $.css when I absolutely needed to. Likewise for reading each element's width each update.
Also, like I said, I modified your logic to (I think) make more sense, given what you were trying to accomplish. It calculates a new diff each update, and tries to move according to that. Also, it doesn't try to keep moving once one of the images falls off (which yours does if you move all the way to the right, and it looks really weird). You can also look at this: http://jsfiddle.net/amqER/5/, which maybe is more like the control scheme you wanted.
Just some quick performance tips.
Try not to use $(this).mousemove instead save $(this) into a variable and use that.
var th = $(this);
th.mousemove...
Try to avoid using $.each. This is probably the part that's slowing your code down.
You can replace it with a for loop, but I would suggest, in this case, sending in each element one by one.
var parallax = function(img){
};
parallax(img1);
parallax(img2);
instantly-increase-your-jquery-performance
Whilst Xymostech's answer does greatly improve upon the original poster's original code; the performance is hardly improved for me in Chrome.
Whilst inspecting the page FPS, the solution posted here runs at 15FPS for me on a Retina MacBook Pro.
I made a very simple change to the code, altering it to use translate3d properties instead of left. Now, it runs at 55-60 FPS for me. I'd call that a massive performance boost.
If 'show paint rectangles' are turned on in Chrome, you'll see the previously posted solution is continually painting changes to the dom whilst the parallax is in motion. With the translate3d solution, there's simply zero painting done the whole time the parallax is in motion.
http://jsfiddle.net/LG47e/
I am teaching myself web programming. I'm also working on C++. I am having a problem with Javascript.
I need to know how to create an "if statement" based on the location of an image.
I am making a simple game that has a cannon that is moving back and forth. I want the user to press a button that will cause the cannon to stop and fire launching another image toward the target.
I have already created the images and a gif of the image that will travel from the cannon in an arc toward the target.
If the user fires when the cannon is in the correct position the image will hit the target.
Can someone tell me how to create an if statement based on position? Or can someone tell me how to make the cannon stop and make the gif appear?
To move the cannon, read up on the onkeyup() event - it will wait for when a key is released, and do something.
What will it do? Probably change the left position of the cannon somehow. I'd recommend setting the CSS style position:absolute on your cannon, then changing the .left property with Javascript.
For example, here's some Javascript to get you started (untested):
var cannon = document.getElementById("cannonPic");
var leftlim = 200;
document.body.onkeyup = function() {
// Remove 'px' from left position style
leftPosition = cannon.style.left.substring(0, cannon.style.left - 2);
// Stop the cannon?
if (leftPosition >= leftLim) {
return;
}
// Move cannon to new position
cannon.style.left = cannon.style.left.substr(0, cannon + 10);
}
And its companion HTML would look like...
...
<img id='cannonPic' src='cannon.jpg' />
...
<script type='text/javascript' src='cannon.js' />
The HTML could be styled like this:
#cannonPic {
left:0;
position:absolute;
}
To answer your "appear/reappear" sub-question, you can use the .display property, accessed via Javascript:
var cannon = document.getElementById("cannonPic");
function appear() {
cannon.style.display = '';
}
function hide() {
cannon.style.display = 'none';
}
A small word of warning, things traveling in arcs will require some math to translate them in two dimensions, depending on how accurate you want it. A fun exercise though if you like math :)
To get the very first image on your page's x and y position on the screen, for instance, try:
var xpos = document.getElementsByTagName('img')[0].x;
var ypos = document.getElementsByTagName('img')[0].y;
Just to add a little background, the way this is typically done:
You have a main loop that will "run" the game.
Each iteration of the loop, you a) update the positions of in-game objects (cannon, projectiles, and targets in your case) and b) render the resulting objects to the screen.
When you detect a "fire" keypress, you simply set the "speed" of your moving cannon to 0, causing it to "stop".
You can retrieve the object's position using Steve's or sajawikio's approach but your game logic determines (and should know) the position of all objects at all times. It is your game logic that says "draw the projectile at position (x,y)". Your game logic should NOT say "I have a projectile, not sure exactly where it is, HMM, so let's query it's position using Javascript". At least not in this case where you have simple, predictable movement.