0

Chrome for Android のアドレス バーのように機能するヘッダーを作成しています。その効果は、ヘッダーが疑似スティッキー ヘッダーであり、下にスクロールするとビューの外にスクロールし、次に上にスクロールし始めると、ヘッダーがスクロールしてビューに戻るということです。

現在、デスクトップ (約 60fps) では正常に動作しますが、Chrome for Android (Nexus 7 2013) ではジャンクがいっぱいです。

デモ: jsFiddle

ヘッダーとコンテンツ領域の両方が、pos:top よりもパフォーマンスの高い変換 translateY で移動されます。

また、 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

私はそれを使用していましたが、レイヤーの利点を確認し、GPU を高速化するtransform: translateY()ために追加する必要があることがわかりました。translateZ(0)

しかし、オブジェクト リテラル コード スタイルを使用するようにコードを更新し、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) でのタイムラインのデバッグが次のようになります (非常にスムーズ): Chrome タイムライン バターのように滑らかな結果

また、最初のスクロール時に小さなジャンプを取り除くには、transform: translateZ(0)アニメーション化する前に要素に追加します。

于 2013-09-24T21:29:20.540 に答える