web-dev-qa-db-ja.com

スクロール時のCSS変換の変更:ぎくしゃくした動きとスムーズな動き

私は既存の視差ライブラリに不満があるので、自分で作成しようとしています。私の現在のクラスは3つのメインクラスで構成されています。

  • ScrollDetectorは、画面に対する要素のスクロール位置を追跡します。現在の位置を表すフロートを返す関数があります:
    • _0_は、ビューポートの下端にある要素の上端を表します
    • _1_は、ビューポートの上端にある要素の下端を表します
    • 他のすべての位置は、線形に内挿/外挿されます。
  • ScrollAnimationScrollDetectorインスタンスを使用して、ScrollDetector要素に基づいて、別の要素の任意のCSS値を補間します。
  • ParallaxativeAnimation extends ScrollAnimation特別なケースの背景画像で、ウィンドウのスクロール速度の正確な係数でスクロールする必要があります。

私の現在の状況はこれです:

  • ScrollAnimations _transform: translateY(x)を使用するとスムーズに動作します。
  • ParallaxativeAnimationsはtranslateY(x)を使用して動作しますが、ぎくしゃくとアニメーション化します。
  • translate3d(0, x, 0)を使用したParallaxativeAnimationsはぎこちないですが、それほど悪くはありません。
  • Rellax ライブラリのアニメーションはtranslate3d(0, x, 0)を使用しており、完全にスムーズに動作します。

比較を見ることができます このペンで 。 (ジャーキネスはFirefoxで最もよく現れます。)私のライブラリは Bitbucket です。

私のライブラリのどこに問題があるのか​​わからないし、それを理解する方法もわかりません。以下は、スムーズに機能するScrollAnimationクラスをスクロールしている間に重い作業が行われる場所の簡略化したペーストです。

_getCSSValue(set, scrollPosition) {
    return set.valueFormat.replace(set.substitutionString, ((set.endValue - set.startValue) * scrollPosition + set.startValue).toString() + set.unit)
}

updateCSS() {
    var cssValues = [];

    var scrollPosition = this.scrollDetector.clampedRelativeScrollPosition();

    var length = this.valueSets.length;
    for(var i = 0; i < length; i++) {
        cssValues.Push(getCSSValue(valueSets[i], scrollPosition) );
    }

    this.setCSS(cssValues);
    this.ticking = false;
}

requestUpdate() {
    if(!this.ticking) {
        requestAnimationFrame(() => { this.updateCSS(); });
    }

    this.ticking = true;
}
_

そして、これがぎこちないParallaxativeAnimationクラスの同等のものです:

_updateCSS() {
    var scrollPosition = this.scrollDetector.clampedRelativeScrollPosition();
    var cssValues = [];

    var length = this.valueSets.length;
    for(var i = 0; i < length; i++) {
        var scrollTranslate = -((this.scrollTargetSize - this.valueSets[i].parallaxSize) * scrollPosition);

        cssValues.Push(
            this.valueSets[i].valueFormat.replace(this.valueSets[i].substitutionString, scrollTranslate.toString() + 'px')
        );
    }

    this.setCSS(cssValues);
    this.ticking = false;
}

requestUpdate() {
    if(!this.ticking) {
        requestAnimationFrame(() => { this.updateCSS(); });
    }

    this.ticking = true;
}
_

数学はもう複雑に見えないので、それがアニメーションのパフォーマンスにどのように影響しているかはわかりません。違いは視差画像のスタイリングにあるのではないかと思いましたが、上のペンでは、Rellaxバージョンはまったく同じCSSを持っていますが、完全にスムーズにアニメーションします。 Rellaxは各フレームでより複雑な計算を行っているようです:

_var updatePosition = function(percentage, speed) {
  var value = (speed * (100 * (1 - percentage)));
  return self.options.round ? Math.round(value) : Math.round(value * 100) / 100;
};


//
var update = function() {
  if (setPosition() && pause === false) {
    animate();
  }

  // loop again
  loop(update);
};

// Transform3d on parallax element
var animate = function() {
  for (var i = 0; i < self.elems.length; i++){
    var percentage = ((posY - blocks[i].top + screenY) / (blocks[i].height + screenY));

    // Subtracting initialize value, so element stays in same spot as HTML
    var position = updatePosition(percentage, blocks[i].speed) - blocks[i].base;

    var zindex = blocks[i].zindex;

    // Move that element
    // (Set the new translation and append initial inline transforms.)
    var translate = 'translate3d(0,' + position + 'px,' + zindex + 'px) ' + blocks[i].transform;
    self.elems[i].style[transformProp] = translate;
  }
  self.options.callback(position);
};
_

Chrome Developer Toolsから本当にわかる唯一のことは、フレームレートが60 fpsを大幅に下回っていないことです。そのため、フレームごとに多くの作業をしすぎているのではないかもしれませんが、位置を計算するときに数学的に正しくないことをしていますか?

だから私は知りません。私はここで頭上にいるのは明らかです。 StackOverflowでライブラリ全体をスローして「FIX IT」と言って申し訳ありませんが、誰かが私が間違っていることを教えられる場合は、または教えてください開発ツールを使用して自分が間違っていることを理解する方法を教えてください。


編集

わかりました。スクロールのジッターの最も重要な要素は、翻訳される要素の高さです。ライブラリの計算が間違っていたため、scrollPixelsPerParallaxPixelプロパティが高いときに、背景画像が必要以上に高くなりました。私は今それを修正しようとしている最中です。

31
75th Trombone

要素に will-change を実装することで、視覚的なパフォーマンスを向上させることができます。 最近のブラウザー でサポートされています(Edgeを除き、IEを除く)。

Will-change CSSプロパティは、要素がどのように変更されることが予想されるかをブラウザに示唆します。ブラウザは、要素が実際に変更される前に最適化を設定する場合があります。これらの種類の最適化は、実際に必要になる前に、コストがかかる可能性のある作業を行うことにより、ページの応答性を向上させることができます。

次のように強制することもできます:

function gogoJuice() {
  // The optimizable properties that are going to change
  self.elems[i].style.willChange = 'transform';
}

function sleepNow() {
  self.elems[i].style.willChange = 'auto';
}

または、より基本的には、変更している要素のCSSで:

.parallax {
  will-change: transform;
}

このプロパティは、作成者が事前に変更される可能性のあるプロパティについてユーザーエージェントに知らせるための方法として意図されています。次に、ブラウザは、プロパティの変更が実際に行われる前に、プロパティの変更に必要な事前最適化を適用することを選択できます。そのため、ブラウザに実際に最適化を実行する時間を与えることが重要です。何かが変更されることを少なくとも少し前に予測し、その後変更を設定する方法を見つけます。

3
Zze

計算の他に、Promiseを使用して非同期で実行することもできます。

await Promise.all([
  loop(update);
]);

パフォーマンスにプラスの影響があるかどうかを確認します。

コメントしますが、評判はまだ十分ではありません。

0
manavortex