0

我正在创建一个类似于 Chrome for Android 地址栏的标题。效果是标题是一个伪粘性标题,当您向下滚动时会滚动到视图之外,然后您开始向上滚动标题滚动回到视图中。

现在它在桌面上运行良好(大约 60fps),但在 Chrome for Android 上(在 Nexus 7 2013 上)它充满了卡顿。

演示:jsFiddle

标题和内容区域都使用 transform translateY 移动,它比 pos:top 性能更高

我还使用 requestAnimationFrame 来消除滚动并仅在浏览器最方便时更改属性。

标题是position: fixed; top: 0;,然后使用 滚动进出视图transform: translateY(...);。另外,我没有使用 margin-top 从标题下方获取内容,而是使用transform: translateY(...);

我的 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();
});

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

编辑:似乎在使用 ADB 进行调试时,每帧中都有一堆清晰的灰色轮廓时间。 Chrome 时间轴屏幕截图。 许多灰色清晰的轮廓框架条。

4

1 回答 1

1

事实证明,即使我正在使用transform: translateY()它,您仍然需要添加translateZ(0)以查看层的好处并使其 gpu 加速。

但我也确实更新了我的代码以使用对象文字代码样式,并forced synchronous layout通过阅读然后写入来消除时间轴中的警告。这与requestAnimationFrame.

演示: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)上的时间线调试看起来像(非常顺利): 铬时间线漂亮的黄油般光滑的结果

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

于 2013-09-24T21:29:20.540 回答