Reputation: 865
I'm trying to get the div to snap to the center of the viewport, right now it just snaps to the top. I was trying to put an offset of 50% but can only get it in px's.
EDIT
I added a new fiddle where I tried to include $(window).scrollTop() / 2)
$("#item").offset().top - 100
var body = $("html, body");
var items = $(".item");
var animating = false;
$(window).scroll(function() {
clearTimeout($.data(this, 'scrollTimer'));
if (!animating) {
$.data(this, 'scrollTimer', setTimeout(function() {
items.each(function(key, value) {
if ($(value).offset().top > $(window).scrollTop()) {
animating = true;
$(body).stop().animate( { scrollTop: $(value).offset().top }, 1000,'swing');
setTimeout(function() { animating = false; }, 2000);
return false;
}
});
}, 50));
}
});
Upvotes: 3
Views: 6111
Reputation: 5880
Here's the trick to keep your viewport centralized on a particular div
.
You need to take into account the following three criteria to be able to centralize the viewport on a given item
:
height
of the last item that appeared on the viewport.offset().top
of the item.window
object).The required scrollTop
value for the window
can be calculated as in the following:
var scrollValue = itemOffset // offset of the item from the top of the page
- .5 * windowHeight // half the height of the window
+ .5 * itemHeight; // half the height of the item
You are basically, moving the top of your viewport to the item under view's top offset initially. This, as you've already experienced, snaps the item to the top of the window.
The real magic part comes when you subtract half of the window's height to go halfway along it vertically, and then shifting your view back down by adding half the item's height
. This makes the item appear vertically centralized with regards to the viewport.
Note:
To be able to query the last item that appeared on the viewport, you have to iterate over all of the elements that have a top offset value (i.e. offset().top
) less than or equal to that of the window
's scrollTop
value:
$.each($('.item'), function(i, value) {
if ($(viewport).scrollTop() >= $(this).offset().top) {
lastItemInView = $(this);
}
});
With the above, the lastItemInView
variable will always end up with the last element visible in the window.
Upvotes: 3
Reputation: 2362
I found this:
$('html, body').animate({scrollTop: $('#your-id').offset().top -100 }, 'slow');
Source: Run ScrollTop with offset of element by ID
Upvotes: 5
Reputation: 9691
Not sure if you figured this out yet or not but I took some code from this answer (How to tell if a DOM element is visible in the current viewport?) that shows how to tell if an element is visible in the view port.
Using that I modified your code to loop through each item and find the first visible one in the viewport and then center that one also factoring in the margin-top
you have. Let me know if this helps!
Fiddle: http://jsfiddle.net/kZY9R/86/
var body = $("html, body");
var items = $(".item");
var animating = false;
$(window).scroll(function() {
clearTimeout($.data(this, 'scrollTimer'));
if (!animating) {
$.data(this, 'scrollTimer', setTimeout(function() {
items.each(function(key, value) {
if (elementInViewport(value)) {
animating = true;
var margin = parseInt($(value).css('margin-top'));
$('html,body').animate({
scrollTop: $(value).offset().top - ($(window).height() + margin - $(value).outerHeight(true)) / 2
}, 200);
setTimeout(function() {
animating = false;
}, 2000);
return false;
}
});
}, 50));
}
});
function elementInViewport(el) {
var top = el.offsetTop;
var left = el.offsetLeft;
var width = el.offsetWidth;
var height = el.offsetHeight;
while (el.offsetParent) {
el = el.offsetParent;
top += el.offsetTop;
left += el.offsetLeft;
}
return (
top < (window.pageYOffset + window.innerHeight) &&
left < (window.pageXOffset + window.innerWidth) &&
(top + height) > window.pageYOffset &&
(left + width) > window.pageXOffset
);
}
Upvotes: 2