Banging my head trying to sort out the correct logic for adding simple parallax behavior.
I would like to have a number of elements on a page which start out with their top offset a certain distance (e.g. 300px). Then as you scroll down the page, once the top of the element is revealed it will slowly shift upwards (tied to scroll) until the top of element reaches middle of viewport at which time it's top offset is 0 and it remains in place.
I tried using third party script (Scroll Magic, Stellar, etc), but when I couldn't get it right now I'm trying custom code:
https://jsfiddle.net/louiswalch/5bxz8fku/1/
var $Window = $(window);
var offset_amount = 400;
var window_height = $Window.height();
var window_half = (window_height/2);
var sections = $('SECTION.reveal');
sections.each(function() {
var element = $(this);
// Make sure we always start with the right offset
element.css({top: offset_amount});
$Window.bind('scroll', function() {
var viewport_top = $Window.scrollTop();
var viewport_middle = viewport_top + (window_height/2)
var viewport_bottom = viewport_top + window_height;
var element_top = element.offset().top;
if (element_top > viewport_top && element_top <= viewport_bottom) {
var distance_to_middle = (element_top - viewport_middle);
var amount_to_middle = (distance_to_middle / window_half);
console.log(amount_to_middle);
if (amount_to_middle >= 0) {
element.css({top: (offset_amount * amount_to_middle)+ 'px'});
} else {
// ? Lock to end position ?
}
}
});
});
jsBin demo 1. (margin space effect on both enter and exit)
jsBin demo 2. (preserve 0 margin once touched)
Instead of targeting the section elements, (create and) target their first child elements,
otherwise you'll create a concurrency mess trying to get the top position but simultaneously modifying it.
Also, you cannot rely on fixed 300px margin (i.e: if window height is less than 500px, you're already missing 100px). That space can vary when the screen height is really small, so you also need to find the idealMarg value.
var $win = $(window),
$rev = $('.reveal'),
winH2 = 0,
winSt = 0;
function reveal() {
winSt = $win.scrollTop();
winH2 = $win.height()/2;
$rev.each(function(i, el){
var y = el.getBoundingClientRect().top,
toMiddleMax = Math.max(0, y-winH2),
idealMarg = Math.min(300, toMiddleMax),
margMin = Math.min(idealMarg, idealMarg * (toMiddleMax/winH2));
$(">div", this).css({transform: "translateY("+ margMin +"px)"});
});
}
$win.on({"load resize scroll" : reveal});
*{box-sizing:border-box; -webkit-box-sizing:border-box;}
html, body{height:100%; margin:0;}
section > div{
padding: 40px;
min-height: 100vh;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<section>
<div style="background-color:red">1</div>
</section>
<section class="reveal">
<div style="background-color: yellow">2</div>
</section>
<section class="reveal">
<div style="background-color: orange">3</div>
</section>
<section class="reveal">
<div style="background-color: pink">4</div>
</section>
I've used in HTML just a <div> logically, that has to be the one and only first child of a section parent.
You're welcome to tweak the above code to make it more performant.
Hey so here is my go at an awnser.
http://jsbin.com/wibiferili/edit?html,js,output
The jist of it is as follows.
JS
var $Window = $(window),
parallaxFactor = 2;
$('.parallaxblock').each(function(a,b){
var element = $(b);
element.css("top",element.data("pOffset") + "px");
$Window.bind('scroll', function() {
var pos =
// Base Offset
element.data("pOffset")
// parallaxFactor
- ($Window.scrollTop() / parallaxFactor);
pos = pos < 0 ? 0 : pos;
element.animate({"top": pos + "px"},10);
return;
});
});
Styles
body{
height: 4000px;
}
.parallaxblock{
position:fixed;
background:#999;
opacity:.5;
}
Example Usage
<div class="parallaxblock" data-p-offset=100>Im A Block</div>
<div class="parallaxblock" data-p-offset=200>Im Also Block</div>
<div class="parallaxblock" data-p-offset=1500>Im Another Block</div>
So by checking the offest its never lower then 0 we can lock it at the top of the screen once it reaches it.
I get the offset amount of the data tag on the div.
If you wanted to change the rate of scroll in different posistions you could change the parallax factor at a certain percentage of screen height.
Hope this helps.
Related
I've tried to find similar posts about this but failed to do so. What I'm trying to do is set up a parallax background that has a moderate zoom upon scrolling. I have the parallax down, that was simple enough, but the zoom on scroll is causing me difficulties.
if ($(".zoomImage").length == 0)
{
console.warn("You're attempting to set hero images without an existing class. '.heroImage'");
return;
}
$(document).scroll(function(){
var scrollpos = $(this).scrollTop();
var screenHeight = $(window).height();
var screenWidth = $(window).width();
$(".zoomImage").each(function(){
var offset = $(this).offset().top;
// Only modify when top is at top of browser on screen.
if (offset < scrollpos && scrollpos < offset + screenHeight)
{
var heroEffectPerc = 100 + 25 * (scrollpos - offset) / (screenHeight * 1.0);
$(this).css("background-size", heroEffectPerc + "% auto");
}
});
});
This is where I'm doing the zoom for the image, the parallax is done in pure CSS as represented below. The issue I'm having is figuring out the mathematics to make sure that the image doesn't escape the edge of its parent when the screen gets excessively wide or tall and still achieve the same effect. 1:
CSS:
pageCanvas
{
position: relative;
background-repeat: no-repeat;
background-position: center;
background-size: auto 100%;
background-color: white;
display: block;
left: 0;
top: 0;
width: 100%;
height: 100vh;
}
pageCanvas.parallax
{
background-attachment: fixed;
}
.
<pageCanvas class="parallax zoomImage" style="background-image: url('./Images/DisplayBackground.png');">
<banner>
<header>
Company name
</header>
<description>
I don't want to<br><span style="margin-left: 200px;">advertise here.</span>
</description>
</banner>
</pageCanvas>
I've tried to get it working, but either have an issue with one of the following:
White background shows on too wide.
White background shows on too tall.
Image stretching.
Do I need to bring in the images origin ratio with this or something? If I figure out a solution prior to an answer given, I'll post it.
Although the original was in a namespace, I'm going to place this answer as if it were not because I hadn't specified. Either way, I found the solution.
The first step was finding the ratio of the original image;
$(".zoomImage").each(function(index){
var bg = $(this).css('background-image');
bg = bg.replace('url(','').replace(')','').replace(/\"/gi, "");
// get image size.
var tmpImg = new Image();
tmpImg.src=bg;
$(tmpImg).one('load', function(){
orgWidth = tmpImg.width;
orgHeight = tmpImg.height;
bgImageRatios[index] = orgHeight / (orgWidth * 1.0);
});
});
To make life easier, I placed them in an array that was global to the name space. This is so I don't have to A) keep finding the ratio of the image, and B) can access it similarly to initializing later on. It should be noted that this method would require being called again in the instance there is any more or less '.zoomImage' classes brought into instance, as the array will be incorrect at that point.
What I did next was place the original code that loops the class into a function.
function zoomImage(scrollpos, screenHeight, screenWidth)
{
//console.log(screenHeight);
$(".zoomImage").each(function(index){
var offset = $(this).offset().top;
if (offset < scrollpos && scrollpos < offset + screenHeight)
{
var heroEffectPerc = 100 + 25 * (scrollpos - offset) / (screenHeight * 1.0);
if ((bgImageRatios[index] * screenWidth / screenHeight) > 1)
$(this).css("background-size", heroEffectPerc + "% auto");
else
$(this).css("background-size", "auto " + heroEffectPerc + "%");
}
});
}
I put it into a function because it would have been placed into two separate locations otherwise. (that's just messy coding). I updated the image size as follows.
$(window).on("resize", function(){
var scrollpos = $(document).scrollTop();
var screenHeight = $(this).height();
var screenWidth = $(this).width();
pageCanvas.zoomImage(scrollpos, screenHeight, screenWidth);
});
$(document).on("scroll", function(){
var scrollpos = $(this).scrollTop();
var screenHeight = $(window).height();
var screenWidth = $(window).width();
pageCanvas.zoomImage(scrollpos, screenHeight, screenWidth);
});
The following sources helped me solve my answer:
Can I get div's background-image url?
How to get image size from URL
Credit is due to them.
I am trying to create a sticky sidebar as follows, the sidebar's height is greater than that of the viewport.
This is the page structure:
<div class="wrapper">
<div class="content__left"> </div>
<div class="sticky"> </div>
<div>
There is a fixed mainmenu at the top of height 54px that scrolls along with the page. I have written this code which works perfectly but the problem is that when the class "fixed-top" and "fixed-bottom" are applied it changes the width of the sidebar and moves it out of the normal document flow. Even when I change the width back to normal it still moves out of the normal flow due to which the result is not smooth. I have tried making changes to margin, translating it but none of it seems to work, I would really appreciate some help here.
Here is the code :
.stop-top{
position:static;
top:0px;
bottom:auto;
}
.fixed-top{
position:fixed;
top:54px;
bottom:auto;
}
.fixed-bottom{
position:fixed;
top:auto;
bottom:0px;
}
.stop-bottom{
position:relative;
bottom:auto;
}
.hang{
position:relative;
bottom:auto;
}
.
// Sidebar div
var stickySidebar = $('.sticky');
var stickyHeight = stickySidebar.height();
var stickyOffsetLeft = $('.sticky').offset().left;
var originalOffset = stickySidebar.offset().top + $('.main-menu').height();
// content__left div
var contentLeft = $('.content__left');
var contentLeftTop = contentLeft.offset().top;
// Floating menu
var mainmenu = $('.main-menu');
var mainmenuHeight = mainmenu.height();
var lastScrollTop = 0;
var onScroll = function () {
var sidebarTop = stickySidebar.offset().top;
var scrollTop = $(window).scrollTop();
var stopPosition;
//triggered on scrolling down
if (scrollTop > lastScrollTop){
// Bottom Position of the viewport
var ViewportBottom = $(window).scrollTop() + $(window).height();
// if class "fixed-top" is found while scrolling down, remove it and add "hang"
if(stickySidebar.hasClass("fixed-top")){
stickySidebar.removeAttr( 'style' );
var newTop = stickySidebar.offset().top;
stickySidebar.removeClass("fixed-top");
stickySidebar.addClass("hang");
// Calculate 'top' for hang class
stickySidebar.css('top', newTop - contentLeftTop);
}
// if bottom of the viewport crosses the bottom of the sidebar,
if(ViewportBottom > $('.sticky').offset().top + $('.sticky').height()){
// Remove any added classes and instead add class "fixed-bottom"
stickySidebar.css("top","");
stickySidebar.removeClass("hang");
stickySidebar.removeClass("stop-top");
stickySidebar.removeClass("stop-bottom");
stickySidebar.addClass("fixed-bottom");
stickySidebar.css({"width":0.3 * stickySidebar.parent().width()});
// stickySidebar.css({"width":0.3 * stickySidebar.parent().width(), "left":stickyOffsetLeft });
}
var contentLeftBottom = $('.content__left').offset().top + $('.content__left').height();
// When bottom of the viewport crosses the bottom of the 'content__left', remove class "fixed-bottom" and add "stop-bottom"
if(ViewportBottom > contentLeftBottom){
stickySidebar.css('width',"");
// stickySidebar.css('left',"");
stickySidebar.removeClass("fixed-bottom");
stickySidebar.addClass("stop-bottom");
stickySidebar.css('top', contentLeft.height() - stickyHeight);
}
}
else {// triggered on scrolling up
// if class "fixed-bottom" is found while scrolling up, remove it and add "hang"
if(stickySidebar.hasClass("fixed-bottom")){
stickySidebar.removeAttr( 'style' );
var newTop = stickySidebar.offset().top;
stickySidebar.removeClass("fixed-bottom");
stickySidebar.addClass("hang");
// Calculate 'top' for hang class
stickySidebar.css('top', newTop - contentLeftTop);
}
// When top of the viewport is less than the top of sidebar, add class "fixed-top"
if(scrollTop + mainmenuHeight < $(".sticky").offset().top){
stickySidebar.css("top","");
stickySidebar.removeClass("hang");
stickySidebar.removeClass("stop-bottom");
stickySidebar.addClass("fixed-top");
stickySidebar.css({"width":0.3 * stickySidebar.parent().width()});
// stickySidebar.css({"width":0.3 * stickySidebar.parent().width(), "left":stickyOffsetLeft});
}
// When top of the viewport is less than top of the 'content__left', add class "stop-top"
if(scrollTop + mainmenuHeight < contentLeftTop){
stickySidebar.removeClass("fixed-top");
stickySidebar.addClass("stop-top");
stickySidebar.removeAttr( 'style' );
}
}
lastScrollTop = scrollTop;
} // onScroll
$(window).on('scroll', onScroll);
I need to retrieve the visible height of a div within a scrollable area. I consider myself pretty decent with jQuery, but this is completely throwing me off.
Let's say I've got a red div within a black wrapper:
In the graphic above, the jQuery function would return 248, the visible portion of the div.
Once the user scrolls past the top of the div, as in the above graphic, it would report 296.
Now, once the user has scrolled past the div, it would again report 248.
Obviously my numbers aren't going to be as consistent and clear as they are in this demo, or I'd just hard code for those numbers.
I have a bit of a theory:
Get the height of the window
Get the height of the div
Get the initial offset of the div from the top of the window
Get the offset as the user scrolls.
If the offset is positive, it means the top of the div is still visible.
if it's negative, the top of the div has been eclipsed by the window. At this point, the div could either be taking up the whole height of the window, or the bottom of the div could be showing
If the bottom of the div is showing, figure out the gap between it and the bottom of the window.
It seems pretty simple, but I just can't wrap my head around it. I'll take another crack tomorrow morning; I just figured some of you geniuses might be able to help.
Thanks!
UPDATE: I figured this out on my own, but looks like one of the answers below is more elegant, so I'll be using that instead. For the curious, here's what I came up with:
$(document).ready(function() {
var windowHeight = $(window).height();
var overviewHeight = $("#overview").height();
var overviewStaticTop = $("#overview").offset().top;
var overviewScrollTop = overviewStaticTop - $(window).scrollTop();
var overviewStaticBottom = overviewStaticTop + $("#overview").height();
var overviewScrollBottom = windowHeight - (overviewStaticBottom - $(window).scrollTop());
var visibleArea;
if ((overviewHeight + overviewScrollTop) < windowHeight) {
// alert("bottom is showing!");
visibleArea = windowHeight - overviewScrollBottom;
// alert(visibleArea);
} else {
if (overviewScrollTop < 0) {
// alert("is full height");
visibleArea = windowHeight;
// alert(visibleArea);
} else {
// alert("top is showing");
visibleArea = windowHeight - overviewScrollTop;
// alert(visibleArea);
}
}
});
Calculate the amount of px an element (height) is in viewport
Fiddle demo
This tiny function will return the amount of px an element is visible in the (vertical) Viewport:
function inViewport($el) {
var elH = $el.outerHeight(),
H = $(window).height(),
r = $el[0].getBoundingClientRect(), t=r.top, b=r.bottom;
return Math.max(0, t>0? Math.min(elH, H-t) : Math.min(b, H));
}
Use like:
$(window).on("scroll resize", function(){
console.log( inViewport($('#elementID')) ); // n px in viewport
});
that's it.
jQuery .inViewport() Plugin
jsFiddle demo
from the above you can extract the logic and create a plugin like this one:
/**
* inViewport jQuery plugin by Roko C.B.
* http://stackoverflow.com/a/26831113/383904
* Returns a callback function with an argument holding
* the current amount of px an element is visible in viewport
* (The min returned value is 0 (element outside of viewport)
*/
;(function($, win) {
$.fn.inViewport = function(cb) {
return this.each(function(i,el) {
function visPx(){
var elH = $(el).outerHeight(),
H = $(win).height(),
r = el.getBoundingClientRect(), t=r.top, b=r.bottom;
return cb.call(el, Math.max(0, t>0? Math.min(elH, H-t) : Math.min(b, H)));
}
visPx();
$(win).on("resize scroll", visPx);
});
};
}(jQuery, window));
Use like:
$("selector").inViewport(function(px) {
console.log( px ); // `px` represents the amount of visible height
if(px > 0) {
// do this if element enters the viewport // px > 0
}else{
// do that if element exits the viewport // px = 0
}
}); // Here you can chain other jQuery methods to your selector
your selectors will dynamically listen to window scroll and resize but also return the initial value on DOM ready trough the first callback function argument px.
Here is a quick and dirty concept. It basically compares the offset().top of the element to the top of the window, and the offset().top + height() to the bottom of the window:
function getVisible() {
var $el = $('#foo'),
scrollTop = $(this).scrollTop(),
scrollBot = scrollTop + $(this).height(),
elTop = $el.offset().top,
elBottom = elTop + $el.outerHeight(),
visibleTop = elTop < scrollTop ? scrollTop : elTop,
visibleBottom = elBottom > scrollBot ? scrollBot : elBottom;
$('#notification').text(`Visible height of div: ${visibleBottom - visibleTop}px`);
}
$(window).on('scroll resize', getVisible).trigger('scroll');
html,
body {
margin: 100px 0;
}
#foo {
height: 1000px;
background-color: #C00;
width: 200px;
margin: 0 auto;
}
#notification {
position: fixed;
top: 0;
left: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<div id="foo"></div>
<div id="notification"></div>
The logic can be made more succinct if necessary, I've just declared separate variables for this example to make the calculation as clear as I can.
Here is a version of Rory's approach above, except written to function as a jQuery plugin. It may have more general applicability in that format. Great answer, Rory - thanks!
$.fn.visibleHeight = function() {
var elBottom, elTop, scrollBot, scrollTop, visibleBottom, visibleTop;
scrollTop = $(window).scrollTop();
scrollBot = scrollTop + $(window).height();
elTop = this.offset().top;
elBottom = elTop + this.outerHeight();
visibleTop = elTop < scrollTop ? scrollTop : elTop;
visibleBottom = elBottom > scrollBot ? scrollBot : elBottom;
return visibleBottom - visibleTop
}
Can be called with the following:
$("#myDiv").visibleHeight();
jsFiddle
Here is the improved code for jquery function visibleHeight: $("#myDiv").visibleHeight();
$.fn.visibleHeight = function() {
var elBottom, elTop, scrollBot, scrollTop, visibleBottom, visibleTop, height;
scrollTop = $(window).scrollTop();
scrollBot = scrollTop + $(window).height();
elTop = this.offset().top;
elBottom = elTop + this.outerHeight();
visibleTop = elTop < scrollTop ? scrollTop : elTop;
visibleBottom = elBottom > scrollBot ? scrollBot : elBottom;
height = visibleBottom - visibleTop;
return height > 0 ? height : 0;
}
I have created a parallax scroll, which seem to be working fine in firefox however in the chrome browser there's a slight jump on the body text when scrolling. click here scroll to the about section. I am not sure if t this is a css or JS issue.. below is a snippet i have incorporated into my parallax function
Does anyone know how i an fix this issue?
$(document).ready(function(){
// Cache the Window object
$window = $(window);
// Cache the Y offset and the speed of each sprite
$('[data-type]').each(function() {
$(this).data('offsetY', parseInt($(this).attr('data-offsetY')));
$(this).data('Xposition', $(this).attr('data-Xposition'));
$(this).data('speed', $(this).attr('data-speed'));
});
// For each element that has a data-type attribute
$('[data-type="background"]').each(function(){
// Store some variables based on where we are
var $self = $(this),
offsetCoords = $self.offset(),
topOffset = offsetCoords.top;
// When the window is scrolled...
$(window).scroll(function() {
// If this section is in view
if ( ($window.scrollTop() + $window.height()) > (topOffset) &&
( (topOffset + $self.height()) > $window.scrollTop() ) ) {
// Scroll the background at var speed
// the yPos is a negative value because we're scrolling it UP!
var yPos = -($window.scrollTop() / $self.data('speed'));
// If this element has a Y offset then add it on
if ($self.data('offsetY')) {
yPos += $self.data('offsetY');
}
// Put together our final background position
var coords = '50% '+ yPos + 'px';
// Move the background
$self.css({ backgroundPosition: coords });
$('[data-type="scroll-text"]', $self).each(function() {
var $text= $(this);
var pos = ($window.scrollTop()/10) * $text.data('speed');
var curP = $text.css('margin-top');
var is_chrome = navigator.userAgent.toLowerCase().indexOf('chrome') > -1;
if(is_chrome) {
$text.animate({
paddingTop: pos,
}, 200, 'linear', function() {
// Animation complete.
});
} else {
$text.css('padding-top', pos);
}
});
}; // in view
}); // window scroll
}); // each data-type
}); // document ready
Some suggestions:
1.) Use position: fixed to avoid any jitter, as you'll be taking the element out of the document flow. You can then position it using z-index.
2.) Cache as much as you can to ease processing time.
3.) Math.round may not be necessary, but try adding this CSS to your moving areas: -webkit-transform: translate3d(0,0,0); This will force hardware acceleration in Chrome, which may ease some of the jittering. (It looked smoother on my screen when I added this with Inspector, but it didn't get rid of the jumpiness with the scroll wheel.) Note: Don't do this on your entire document (e.g. body tag), as it might cause some issues with your current layout. (Your navigation bar didn't stick to the top of the window, for instance.)
4.) If you have any animations running as part of your parallax logic (tweening the margin into place or something along those lines), remove it - that would probably cause the jump you see.
Hope this helps. Best of luck.
I see the same jittering in FireFox and Chrome (Mac). Looking at your containers, one thing that's glaring at me is the pixel position that's being calculated/used.
Chrome: <div id="about-title" style="margin-top: 1562.3999999999999px;">
FireFox: <div id="about-title" style="margin-top: 1562.4px;">
Browsers aren't going to allow content to sit at 1/2 pixel, let alone 0.3999999 of a pixel. I think it's moving it, and trying to calculate whether to round up or round down. It jitters because it's calculating with every click of your mouse wheel.
Thus, I'd try adding Math.round() to your positions so that the containers are never being left in limbo.
Take a look at the code here: http://webdesigntutsplus.s3.amazonaws.com/tuts/338_parallax/src/index.html
Firebug some of the elements, and you'll see that their only fraction of a pixel is '0.5'. Most of them (the bulk) go to round number values.
You are going to have to change the way that the scrolling works (i.e. change how the spacing is computed), but this can be fixed by adding the position:fixed CSS element to the page elements that are scrolling. The problem is coming from the time that it takes for the JavaScript to process and then render.
For example, on your page you would set each of the <div> tags containing text to have a fixed position and then use the JavaScript/JQuery function to update the top: CSS element. This should make the page scroll smoothly.
Have you tried adding the preventdefault inside the scroll function?
$(window).scroll(function(e) {
e.preventDefault();
// rest of your code
}
In a previous question I created a fairly good parallax scrolling implementation. Jquery Parallax Scrolling effect - Multi directional You might find it useful.
Here's the JSFiddle http://jsfiddle.net/9R4hZ/40/ use the up/down arrows or scroll wheel.
Using padding and margin for the positioning are probably why you're experiencing rendering issues. While my code uses scroll or keyboard input for the effect you can loop the relavent portion and check the $moving variable until you reach the desired element on screen.
function parallaxScroll(scroll) {
// current moving object
var ml = $moving.position().left;
var mt = $moving.position().top;
var mw = $moving.width();
var mh = $moving.height();
// calc velocity
var fromTop = false;
var fromBottom = false;
var fromLeft = false;
var fromRight = false;
var vLeft = 0;
var vTop = 0;
if($moving.hasClass('from-top')) {
vTop = scroll;
fromTop = true;
} else if($moving.hasClass('from-bottom')) {
vTop = -scroll;
fromBottom = true;
} else if($moving.hasClass('from-left')) {
vLeft = scroll;
fromLeft = true;
} else if($moving.hasClass('from-right')) {
vLeft = -scroll;
fromRight = true;
}
// calc new position
var newLeft = ml + vLeft;
var newTop = mt + vTop;
// check bounds
var finished = false;
if(fromTop && (newTop > t || newTop + mh < t)) {
finished = true;
newTop = (scroll > 0 ? t : t - mh);
} else if(fromBottom && (newTop < t || newTop > h)) {
finished = true;
newTop = (scroll > 0 ? t : t + h);
} else if(fromLeft && (newLeft > l || newLeft + mw < l)) {
finished = true;
newLeft = (scroll > 0 ? l : l - mw);
} else if(fromRight && (newLeft < l || newLeft > w)) {
finished = true;
newLeft = (scroll > 0 ? l : l + w);
}
// set new position
$moving.css('left', newLeft);
$moving.css('top', newTop);
// if finished change moving object
if(finished) {
// get the next moving
if(scroll > 0) {
$moving = $moving.next('.parallax');
if($moving.length == 0)
$moving = $view.find('.parallax:last');
} else {
$moving = $moving.prev('.parallax');
if($moving.length == 0)
$moving = $view.find('.parallax:first');
}
}
// for debug
$('#direction').text(scroll + " " + l + "/" + t + " " + ml + "/" + mt + " " + finished + " " + $moving.text());
}
May not be related to your specifics, but I had a jumpy parallax scrolling problem, I was able to solve it adding the following CSS for the fixed portions of the page:
#supports (background-attachment: fixed)
{
.fixed-background
{
background-attachment: fixed;
}
}
Not sure of all the specifics, but found at Alternate Fixed & Scroll Backgrounds
I'm trying to create a div 'floater' which acts similar to a fixed div (stuck in a specific position regardless of scrolling), but when it hits the boundaries of the div it is within, it stops being fixed.
When the scrollbar is at the top, it should have the div placed at 0 within the containing div (positioned say 100 pixels from the top) and when the scrollbar reaches the bottom, it should prevent the floater from going outside the container. The height of the floater would be static, but the container height would be dynamic.
I've seen this type of this all over the place, but can't figure out how to find a good example for it.
I'd like to avoid jQuery if possible, as I imagine it should only require some simple JavaScript to determine the current position relative to the div it is within.
Thank you.
Okay folks, here is a complete solution. I've only tested this in Firefox and IE, but it should work across the board (I think). This is straight JavaScript and jQuery is not used. The first JS function makes sure the height returned works in various browsers.
Edit - I improved on this, see below.
<html>
<head>
<style type="text/css">
* {margin:0;padding:0;}
#header {height:300px;width:100%;background:#888;}
#main {height:800px;width:70%;background:#eee;float:left;}
#side {width:30%;height:auto;background:#ddd;float:left;position:relative;}
#floater {height:400px;width:90%;background:#dcd;top:0px;position:absolute;}
#footer {height:300px;width:100%;background:#888;clear:both;}
</style>
<script>
function getPageY() {
var height = 0;
if(typeof(window.pageYOffset) == 'number') {
height = window.pageYOffset;
}
else if(document.body && document.body.scrollTop) {
height = document.body.scrollTop;
}
else if(document.documentElement && document.documentElement.scrollTop) {
height = document.documentElement.scrollTop;
}
return height;
}
onload=function() {
window.onscroll = scroll;
function scroll() {
ybox.value = "this: "+getPageY();
var f = document.getElementById("floater");
var y = getPageY()-300; // minus header height
var divh = document.getElementById("main").offsetHeight;
if (divh > 400) { // greater than floater height
divh -= 400; // minus floater height
if (y < 0) y = 0;
else if (y > divh) y = divh;
f.style.top = y+"px";
}
}
}
</script>
</head>
<body>
<div id="header"></div>
<div id="main"></div>
<div id="side"><div id="floater">Float Content<br />
<input name="ybox" id="ybox"></div></div>
<div id="footer"></div>
</body>
</html>
This works, but with images it is extremely jumpy. I modified it to use a fixed position when it should be stuck in a position. Replace the three matching lines with this for a smoother result:
if (y < 0) {y = 0;f.style.position = "absolute";}
else if (y > divh) {y = divh;f.style.position = "absolute";f.style.top = divh+"px";}
else {f.style.position = "fixed";f.style.top = 0;}
I've implemented this and its pretty good. http://net.tutsplus.com/tutorials/html-css-techniques/creating-a-floating-html-menu-using-jquery-and-css/. But this is done using jquery only. Ill let you know if icome across any links with just plain javascript.