Ionic Shrinking Header


#1

Can someone put together a working sample of the ‘Shrinking Header’ directive using Ionic 1.0rc. With the platform specific styling (Android and iOS) and the view caching and navbar changes it needs someone with some decent skills to hack something together.

I have tried my best but I can never nail down the correct .querySelector() for the header and the tabs. Below is one attempt I’ve had. Any help appreciated.

@WidawskiJ - you seem to have a good handle on the shrinking directive, I read the ‘Shrinking-Floating-Material-Button’ post with your codepen. Any thoughts?

A huge bonus would be a way for Android to pin the TABS to the top of the viewport on scrolling.

    .directive('headerShrink', function ($document) {
var fadeAmt;

var shrink = function (header, content, amt, max) {
    amt = Math.min(max, amt);
    fadeAmt = 1 - amt / max;
    ionic.requestAnimationFrame(function () {
        header.style[ionic.CSS.TRANSFORM] = 'translate3d(0, -' + amt + 'px, 0)';
        for (var i = 0, j = header.children.length; i < j; i++) {
            header.children[i].style.opacity = fadeAmt;
        }
    });
};

return {
    restrict: 'A',
    link: function ($scope, $element, $attr) {
        var starty = $scope.$eval($attr.headerShrink) || 0;
        var shrinkAmt;

        var amt;

        var y = 0;
        var prevY = 0;
        var scrollDelay = 0.4;

        var fadeAmt;

        var header = $document[0].body.querySelector('[nav-bar="active"] .bar');
        //var header = $document[0].body.querySelector('.bar-header');
        //var header = $document[0].body.querySelector('[nav-bar="active"] .bar');

        var tabs = $document[0].body.querySelector('div.tabs');
        var headerHeight = header.offsetHeight;

        function onScroll(e) {
            var scrollTop = e.detail.scrollTop;

            if (scrollTop >= 0) {
                y = Math.min(headerHeight / scrollDelay, Math.max(0, y + scrollTop - prevY));
            } else {
                y = 0;
            }
            //console.log(scrollTop);

            ionic.requestAnimationFrame(function () {
                fadeAmt = 1 - (y / headerHeight);
                if (ionic.Platform.isAndroid() == true) {
                    //tabs.style[ionic.CSS.TRANSFORM] = 'translate3d(0, ' + -y + 'px, 0)';
                } else {
                    //header.style[ionic.CSS.TRANSFORM] = 'translate3d(0, ' + -y + 'px, 0)';
                    tabs.style[ionic.CSS.TRANSFORM] = 'translate3d(0, ' + y + 'px, 0)';
                }
                for (var i = 0, j = header.children.length; i < j; i++) {
                    header.children[i].style.opacity = fadeAmt;
                    tabs.children[i].style.opacity = fadeAmt;
                }
            });

            prevY = scrollTop;
        }

        $element.bind('scroll', onScroll);
    }
}
 })

#2

I hacked this together to get it to work with sideMenus. But then I found out there is another branch in the repo that might do it a little cleaner. However, neither one works with ‘top-tabs’ and overall it was a little jerky on an iphone 6 with collecton-repeat an a lot of photos. So I pushed off implementation until I have a chance to look at it more closely. Works great in desktop chrome though.

But looking at your code, I think you might want to use $document[0].body.querySelectorAll() - that was the difference with the new branch in the official repo.


#3

Very good, your fork on GitHub is in the right direction. I had to add a few lines to get it perfect for the Ionic platform styling (Android vs. iOS). I have one small issue on the iOS styling. The bottom tabs shrink out but the element is still present in the view. I haven’t inspected it but maybe a z-index fix or a different querySelector() might fix it.

Thanks for the help!


#4

thanks. show me your code. My version still fails with top-tabs – they are inside the <ion-view> and don’t shrink with the .bar-header. This might be the same issue you have with Android tabs


#5
.directive('headerShrink', function($document) {
var fadeAmt;

var shrink = function (header, content, amt, max) {
    amt = Math.min(max, amt);
    fadeAmt = 1 - amt / max;
    ionic.requestAnimationFrame(function () {
        header.style[ionic.CSS.TRANSFORM] = 'translate3d(0, -' + amt + 'px, 0)';
        for (var i = 0, j = header.children.length; i < j; i++) {
            header.children[i].style.opacity = fadeAmt;
        }
    });
};

return {
    restrict: 'A',
    link: function ($scope, $element, $attr) {
        var starty = $scope.$eval($attr.headerShrink) || 0;
        var shrinkAmt;

        var amt;

        var y = 0;
        var prevY = 0;
        var scrollDelay = 0.4;

        var fadeAmt;

        var tabs = $document[0].body.querySelector('div.tabs');
        var headers = $document[0].body.querySelectorAll('ion-header-bar, .bar-header');
        var headerHeight = headers[0].offsetHeight;               

        var floatingButton = $document[0].body.querySelector('.md-fab-bottom-right');

        function onScroll(e) {
            var scrollTop = e.detail.scrollTop;
            if (scrollTop >= 0) {
                y = Math.min(headerHeight / scrollDelay, Math.max(0, y + scrollTop - prevY));
            } else {
                y = 0;
            }

            // console.log(scrollTop);

            ionic.requestAnimationFrame(function () {
                fadeAmt = 1 - (y / headerHeight);
                if (ionic.Platform.isAndroid() == true) {
                    tabs.style[ionic.CSS.TRANSFORM] = 'translate3d(0, ' + -y + 'px, 0)';
                } else {
                    tabs.style[ionic.CSS.TRANSFORM] = 'translate3d(0, ' + y + 'px, 0)';
                }
                for (var h = 0; h < headers.length; h++) {
                    headers[h].style[ionic.CSS.TRANSFORM] = 'translate3d(0, ' + -y + 'px, 0)';
                    //floating action button 
                    floatingButton.style[ionic.CSS.TRANSFORM] = 'translate3d(0, ' + y + 'px, 0)';

                    for (var i = 0, j = headers[h].children.length; i < j; i++) {
                        headers[h].children[i].style.opacity = fadeAmt;
                    }
                }
                $element[0].style.top = Math.max(headerHeight - y, 0) + 'px'
            });

            prevY = scrollTop;
        }

        $element.bind('scroll', onScroll);
    }
}
});

#6

Let me know if you have any thoughts about the iOS tabs still overlaying the bottom of the viewport when the tabs scroll away. Maybe a selector issue.


#7

@mixofia - to further improve the platform styling for Ionic 1.0 (android top tabs)

I’ve put together a reverse function on the shrink directive.

  $rootScope.$on('$stateChangeSuccess', function (event, toState, toParams, fromState, fromParams) {
    //check if the user is coming from the SEARCH state (only place currently using shrinking header)
    if (fromState.name === 'tab.search') {
        var tabs = document.querySelector('div.tabs');
        var headers = document.querySelectorAll('ion-header-bar, .bar-header');
        //check the platform (Android vs. iOS)
        if (ionic.Platform.isAndroid() == true) {
            tabs.style[ionic.CSS.TRANSFORM] = 'translate3d(0, 0px, 0)';
            for (var h = 0; h < headers.length; h++) {
                headers[h].style[ionic.CSS.TRANSFORM] = 'translate3d(0, 0px, 0)';
                for (var i = 0, j = headers[h].children.length; i < j; i++) {
                    headers[h].children[i].style.opacity = 1;
                }
            }
        } else {//iOS condition      
            for (var h = 0; h < headers.length; h++) {
                headers[h].style[ionic.CSS.TRANSFORM] = 'translate3d(0, 0px, 0)';
                for (var i = 0, j = headers[h].children.length; i < j; i++) {
                    headers[h].children[i].style.opacity = 1;
                }
            }
        };
    } else {
        return;
    };
});

Just make sure you check the state you are coming from (I only use the shrinking header on one .state so I check that first. If you have any advice on improving this let me know.

I am going to look into the bottom tabs on iOS shrinking and transforming correctly. It moves down and out but the element is overlaying the position unfortunately so I’ve removed the shrinking header from touching the iOS tabs (bottom) for now with a platform() check.


#8

Here you go @bradmartin Ionic Scroll Sista