5

Update

I made a repo on GitHub:

https://github.com/yckart/Veil.js

Big thanks to Sargo Darya and Edward J Payton.


I've to create a header which slides up if you scroll down and vice versa. The problem is, that it jumps (if you are in the diff-range between 0-128).

I can not figure out where the problem sits. Any idea how to get this to work correctly?

Here's what I've done so far: http://jsfiddle.net/yckart/rKW3f/

// something simple to get the current scroll direction
// false === down | true === up
var scrollDir = (function (oldOffset, lastOffset, oldDir) {
    return function (offset) {
        var dir = offset < oldOffset;
        if (dir !== oldDir) lastOffset = offset;
        oldOffset = offset;
        oldDir = dir;
        return {dir: dir, last: lastOffset};
    };
}());

var header = document.querySelector('header');
var height = header.clientHeight;
addEventListener('scroll', function () {
    var scrollY = document.documentElement.scrollTop || document.body.scrollTop;
    var dir = scrollDir(scrollY);
    var diff = dir.last-scrollY;

    var max = Math.max(-height, Math.min(height, diff));
    header.style.top = (dir.dir ? max-height : max) + 'px';
});

Another problem is, that if the scroll-direction was changed the first time, nothing happens. However, this could be fixed with an interval, or else.

Community
  • 1
  • 1
yckart
  • 32,460
  • 9
  • 122
  • 129
  • can you use jQuery? it's in your tag but you don't use it in `jsfiddle`? – BMH Sep 04 '13 at 10:48
  • @BMH Sure, no problem! I had jQuery as dependency, but there was no need for it yet. – yckart Sep 04 '13 at 10:56
  • Just my thought, it should be easier to use jQuery function to show and hide the `header`, you just need to identify the direction. But with jQuery the mystery of this question is not revealed, is it? ;) – BMH Sep 04 '13 at 11:11
  • No, not really :P See my first approach: http://fiddle.jshell.net/cMnVq/ <= Looks good, but my client doesn't think so :( – yckart Sep 04 '13 at 11:14

3 Answers3

4

I believe this is exactly what you want:

var header = $('header'),
headerHeight = header.height(),
treshold = 0,
lastScroll = 0;

$(document).on('scroll', function (evt) {
    var newScroll = $(document).scrollTop(),
        diff = newScroll-lastScroll;

    // normalize treshold range
    treshold = (treshold+diff>headerHeight) ? headerHeight : treshold+diff;
    treshold = (treshold < 0) ? 0 : treshold;

    header.css('top', (-treshold)+'px');

    lastScroll = newScroll;
});

Demo on: http://jsfiddle.net/yDpeb/

Sargo Darya
  • 569
  • 6
  • 19
  • Any idea how we can make this a bit more iOS-like, and let a `footer` move along the same time a `header` does. http://jsfiddle.net/yckart/VsmSw/6 Currently the `footer` is a bit quicker than the `header`. Maybe calculating with percentages could do the trick, don't know... – yckart Sep 24 '13 at 12:54
  • To be able to do that you would need to normalize both values. Take the percentage of the highest one and calculate the position of all others with that. Following assumptions: Header height is 200, footer height is 50. Scenario: You scrolled 100 upwards so it gives 100%/200px*100px=>50%. Now you just update the footer and set the css to 50% of it's size making the footer scroll exactly 25px. – Sargo Darya Sep 24 '13 at 14:28
  • 1
    The event handler should be throttled to avoid overloading the event handler thread (the scroll event will trigger a lot). Something like `_.throttle(f, 500)` if you are using underscore / lo-dash would work. – Nacho Coloma Sep 24 '14 at 10:05
1

Try this out:- http://jsfiddle.net/adiioo7/rKW3f/7/

JS:-

var scrollDir = (function (oldOffset, lastOffset, oldDir) {
    return function (offset) {

        var dir = offset < oldOffset;
        if (dir !== oldDir) {
            lastOffset = offset;
        } else {
            offset = offset - height;
        }
        oldOffset = offset;
        oldDir = dir;
        return {
            dir: dir,
            last: lastOffset
        };
    };
}());

var header = document.querySelector('header');
var height = header.clientHeight;

$(window).scroll(function () {
    var scrollY = $(window).scrollTop();
    var dir = scrollDir(scrollY);
    var diff = dir.last - scrollY;

    var max = Math.max(-height, Math.min(height, diff));

    max = (dir.dir ? max - height : max); 
    max = scrollY<height?0:max;


    $('header').data('size', 'small');
    $('header').stop().animate({
        top: max
    }, 600);


});
Aditya Singh
  • 9,512
  • 5
  • 32
  • 55
  • Ahm, no... This makes no sense! It hides the header immediately and doesn't show it again. – yckart Sep 24 '13 at 08:07
  • I think you misunderstood what I try to achieve. The `header` has to scroll up **depending** on the current scroll-offset and vice versa move down. You can see this effect on androids stack-browser (the adressbar) and since iOS7 is released they've a *similar* effect in mobile-safari. Even in google-plus on iOS is this effect implemented. What you did is something like this: http://stackoverflow.com/questions/18604022/slide-header-up-if-you-scroll-down-and-vice-versa#comment27395783_18604022 – yckart Sep 24 '13 at 10:17
  • Although it was not what the OP wanted, it is still close to what was asked in the title, and it is almost what I was looking for. I would like to modify it a little bit to suit my needs, but unfortunately, as I am not a front-end dev, I have trouble understanding all the logic behind your code. Could you please comment it a little bit to make it clearer ? :) – Jacquot Dec 21 '17 at 23:31
1

Sargo Darya's answer above is exacty what i was looking for but I found a bug with Webkits inertia scrolling so I made a fix:

// Scrolling Header
var header = $('header'),
headerHeight = header.height(),
offset = 0,
lastPos = 0;

$(document).on('scroll', function(e) {
var newPos = $(document).scrollTop(),
    pos = newPos-lastPos;

if (offset+pos>headerHeight) { 
    offset = headerHeight;
} else if (newPos < 0){ // webkit inertia scroll fix
    offset = 0;
} else {
    offset = offset+pos;
};
if (offset < 0) {
    offset = 0;
} else {
    offset = offset;
};

header.css('top', (-offset)+'px');

lastPos = newPos;
});

Adding one line fixed it. If - if scroll position is lower than 0, set header offset at 0.

Demo based on Sargo Darya's - http://jsfiddle.net/edwardomni/D58vx/4/

EJP
  • 181
  • 4
  • 13
  • 1
    Great suggestion, I merged your bugfix and created a repo: https://github.com/yckart/Veil.js – yckart Nov 28 '13 at 13:33