修复运动中的Jank锁定到Android上的Scroll

时间:2013-09-23 16:25:34

标签: javascript android performance scroll requestanimationframe

我正在创建一个类似Chrome for Android地址栏的标题。结果是标题是一个伪的粘性标题,当你向下滚动时滚动出视图,然后你开始向后滚动标题滚动回视图。

现在它在桌面上工作正常(大约60fps)但在Chrome for Android(在Nexus 7 2013上)它充满了jank。

Demo: jsFiddle

标题和内容区域都使用变换translateY移动,这比pos:top

更高效

我也使用requestAnimationFrame去抖动滚动,只在最方便浏览器时更改属性。

标题为position: fixed; top: 0;,然后使用transform: translateY(...);滚动进出视图。此外,我使用transform: translateY(...);

而不是使用margin-top从标题下方获取内容

我的js的基本结构如下:

var latestScrollTop = 0;
var lastReactedScrollTop = 0;

var ticking = false;

function DoScroll()
{
    var builtUpScrollTop = latestScrollTop - lastReactedScrollTop;

    // Fold the top bar while we are scrolling (lock it to scrolling)
    $('header.main-header').css('transform', 'translateY(' ... 'px)');
    HeaderHeightChange();

    lastReactedScrollTop = latestScrollTop;
    ticking = false;
}

function HeaderHeightChange()
{
    // We need to update the margin-top for the content so we don't overlap it
    $('main.content-area').css('transform', 'translateY(' ... 'px)');

}

function requestTick() {
    if(!ticking) {
        requestAnimationFrame(function(){
            DoScroll();
        });
    }
    ticking = true;
}

$(window).on('scroll', function(e) {
    latestScrollTop = $(window).scrollTop();
    requestTick();
});

效果不完整,因为它需要在完成滚动(并编码)后解决折叠,但我不想在只有滚动移动锁定到标题导致jank时使问题复杂化。我在上下滚动时看到绘制矩形,即使我正在改变变换,我认为gpu正在处理,不应该绘画。

编辑:在使用ADB进行调试时,似乎每帧中都有一堆清晰的灰色轮廓时间。 Chrome Timeline screenshot. Lots of grey clear outlined frame bars.

1 个答案:

答案 0 :(得分:1)

事实证明,即使我使用的是transform: translateY(),您仍然需要添加translateZ(0)才能看到图层的好处并加速gpu。

但是我也更新了我的代码以使用对象文字代码样式,并通过阅读然后编写来消除时间轴中的forced synchronous layout警告。这与requestAnimationFrame结合在一起。

Demo: jsFiddle

var myUtils = {
    clamp: function(min, max, value) {
        return Math.min(Math.max(value, min), max);
    },

    getTranslateYFromTransform: function(rawTransform) {
        return parseFloat(rawTransform.match(/^matrix\((([+-]?[0-9]*\.?[0-9]*),\s*?){5}([+-]?[0-9]*\.?[0-9]*)\)$/)[3])
    }

};


var scrollHeader = {
    latestScrollTop: 0,
    lastReactedScrollTop: 0,

    headerHeight: 0,
    headerTransformTranslateY: 0,

    ticking: false,

    requestTick: function() {
        if(!scrollHeader.ticking) {
            requestAnimationFrame(function(){
                scrollHeader.doHeaderFold();
            });
        }
        scrollHeader.ticking = true;
    },

    doHeaderFold: function() {
        var header = $('header.main-header');

        var builtUpScrollTop = scrollHeader.latestScrollTop - scrollHeader.lastReactedScrollTop;

        scrollHeader.headerHeight = header.outerHeight();

        scrollHeader.headerTransformTranslateY = myUtils.clamp(-parseInt(scrollHeader.headerHeight), 0, (myUtils.getTranslateYFromTransform(header.css('transform')) - builtUpScrollTop));

        // Fold the top bar while we are scrolling (lock it to scrolling)
        header.css('transform', 'translateY(' + scrollHeader.headerTransformTranslateY + 'px) translateZ(0)');

        scrollHeader.headerHeightChange();

        scrollHeader.lastReactedScrollTop = scrollHeader.latestScrollTop;
        scrollHeader.ticking = false;
    },

    headerHeightChange: function() {
        // We need to update the margin-top for the content so we don't overlap it
        $('main.content-area').css('transform', 'translateY(' + (scrollHeader.headerHeight + scrollHeader.headerTransformTranslateY) + 'px) translateZ(0)');
    }
};

$(window).on('scroll', function(e) {
    //console.log(e);
    scrollHeader.latestScrollTop = $(window).scrollTop();

    scrollHeader.requestTick();

});

这使得ADB(Nexus 7 2013)上的时间轴调试看起来像(非常流畅): Chrome Timeline nice buttery smooth results

在第一次滚动时,为了摆脱小跳跃,在动画制作之前将transform: translateZ(0)添加到元素中。