滚动时改变CSS变换:生涩运动与平滑运动

75t*_*one 31 javascript css performance scroll parallax

我对现有的视差库感到不满,所以我正在努力编写自己的视差库.我目前的课程包括三个主要课程:

  • ScrollDetector跟踪元素相对于屏幕的滚动位置; 它具有返回表示其当前位置的float的函数:
    • 0 表示位于视口底部边缘的元素的上边缘
    • 1 表示位于视口顶部边缘的元素的下边缘
    • 所有其他位置都是线性插值/外插的.
  • ScrollAnimation使用ScrollDetector实例根据元素在另一个元素上插入任意CSS值ScrollDetector.
  • ParallaxativeAnimationScrollAnimation针对背景图像的特殊情况进行扩展,该背景图像应以窗口滚动速度的精确因子滚动.

我目前的情况是这样的:

  • ScrollAnimationS使用transform: translateY(x)工作进展顺利.
  • ParallaxativeAnimation使用translateY(x)工作,但动画jerkily.
  • ParallaxativeAnimation使用的translate3d(0, x, 0)是生涩,但不是那么糟糕.
  • Rellax库的动画,其使用translate3d(0, x, 0),工作完全顺利.

你可以在这支笔上看到比较.(jerkiness在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;
}
Run Code Online (Sandbox Code Playgroud)

这里的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;
}
Run Code Online (Sandbox Code Playgroud)

数学似乎并不复杂,所以我无法弄清楚这是如何影响动画性能的.我认为差异可能是我对视差图像的造型,但在上面的笔中,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);
};
Run Code Online (Sandbox Code Playgroud)

我唯一可以从Chrome开发者工具中得知的是帧率不会低于60 fps,所以也许并不是因为我每帧都做太多工作,而是我在做数学上不正确的事情.我计算位置?

所以我不知道.我显然已经在我的头上了.我很抱歉在StackOverflow上放了一个完整的库并说"FIX IT",但是如果有人能说出我做错了什么,或者告诉我如何使用Developer Tools来弄清楚我做错了什么,我我非常感激.


编辑

好吧,我已经发现滚动抖动的最重要因素是被翻译元素的高度.我的图书馆里有一个错误的计算,导致背景图像比我的scrollPixelsPerParallaxPixel财产高时需要的高得多.我现在正试图纠正这个问题.

Zze*_*Zze 6

您可以通过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';
}
Run Code Online (Sandbox Code Playgroud)

或者更简单地说,只是在您要更改的元素的 css 中:

.parallax {
  will-change: transform;
}
Run Code Online (Sandbox Code Playgroud)

此属性旨在作为作者的一种方法,让用户代理了解可能提前更改的属性。然后,浏览器可以选择在属性更改实际发生之前应用属性更改所需的任何提前优化。因此,给浏览器一些时间来实际进行优化非常重要。找到某种方法来至少稍微提前预测某些事情将会发生变化,然后设置 will-change 。


小智 0

除了计算之外,您还可以尝试使用 Promise 异步运行它:

await Promise.all([
  loop(update);
]);
Run Code Online (Sandbox Code Playgroud)

只是为了看看它是否对性能有积极的影响。

我想发表评论,但我还没有足够的声誉。