0

I have a header which I would like to control the visibility of while scrolling. When scrolling down the page, the header should operate as any element would. When scrolling up the page, regardless of page position, I would like to show the header as the page ascends. This behavior can be best understood by observing a simple example.

I am listening for scroll events and updating a fixed position div with a negative top value:

$(function()
{
    var $header = $("#header");
    var $window = $(window);

    var headerHeight = $header.outerHeight();
    var headerY = $header.outerHeight();
    var scrollY = $window.scrollTop();

    $window.on("scroll", function()
    {
        headerY += scrollY - $window.scrollTop();
        if (headerY > headerHeight)
        {
            headerY = headerHeight;
        }
        else if (headerY < 0)
        {
            headerY = 0;
        }
        $header.css(
            {
                top: headerY - headerHeight,
            });
        scrollY = $window.scrollTop();
    });
});

However, the behavior in Chrome for Mobile is not at all what I would expect. The header often does not animate and will instantly appear or disappear when the bounds are met. When it does animate, the performance is haltingly irregular and often only draws one to three frames. This behavior is not exhibited when using desktop Chrome's mobile emulation; it only occurs on an actual device.

This only seems to happen when dealing with a fixed position element at the top of the page. If I attempt to alter a fixed element in a different manner, such as moving it in from a side or the bottom, everything works much closer to expected. In fact, if I alter both a fixed position top element and fixed position side element, the top element will behave more uniformly, like so.

Why is Chrome for Mobile so erratic with fixed position elements at the top of the page? Is there any way I can account for it in a succinct way?

Benjamin Soddy
  • 557
  • 1
  • 6
  • 22

2 Answers2

1

Try using some hardware accelerated CSS3 transforms. Instead of using top to position your element, try using transform:translateY.

Also try adding transform:translateZ(0) on the header element.

scooterlord
  • 15,124
  • 11
  • 49
  • 68
  • I attempted transform:translateZ(0) initially to force GPU acceleration, but it did not work. Transitioning from updating the top style attribute to transform: translateY worked perfectly. Basically, this: `transform: "translateY(" + (headerY - headerHeight) + "px)",` – Benjamin Soddy Sep 08 '16 at 18:40
  • Glad I could help – scooterlord Sep 08 '16 at 19:36
0

There are a lot in play here. Mobile devices are not that powerful as desktops and animations sometimes may appear sluggish. Especially when there are CSS shadows involved in the page.

I am not sure what causes this but I would give a shot to window.requestAnimationFrame function whenever is supported (you need to check for support for older browsers such as IE9).

The idea is to redraw the screen whenever the mobile GPU is ready to redraw (normally every 16.6 ms) and not to flood it with request that can't handle in a timely manner.

So I would try something like this (not tested) :

$(function()
{
    var $header = $("#header");
    var $window = $(window);

    var headerHeight = $header.outerHeight();
    var headerY = $header.outerHeight();
    var scrollY = $window.scrollTop();
    var requestId;

    $window.on("scroll", function()
    {
        //run only when no request is pending.
        if (requestId === undefined) {
             requestId = window.requestAnimationFrame(animateHeader);
        }
    });

    function animateHeader() {
        requestId = undefined;

        headerY += scrollY - $window.scrollTop();
        if (headerY > headerHeight)
        {
            headerY = headerHeight;
        }
        else if (headerY < 0)
        {
            headerY = 0;
        }
        $header.css(
            {
                top: headerY - headerHeight,
            });
        scrollY = $window.scrollTop();
    }

});

Since I am not sure if that solves anything, some feedback once you tried it would be nice!

  • Edited condition requestAnimationFrame should be called.
dimitrisk
  • 774
  • 9
  • 15
  • Interesting approach met with a very peculiar behavior on the device. It attempts to animate and successfully updates the style of the header element, however the device still outright refuses to redraw the element. With more experimentation, I can confirm that it only has a problem with element if it is a fixed position with a top anchor. Bottom, left, and right all will redraw normally. – Benjamin Soddy Sep 06 '16 at 19:58