在滚动上更改CSS变换:生涩移动与平滑移动

时间:2017-11-15 19:45:56

标签: javascript css performance scroll parallax

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

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

我目前的情况是:

  • ScrollAnimation使用transform: translateY(x)顺利工作。
  • ParallaxativeAnimation使用translateY(x)工作,但动画片很快。
  • 使用ParallaxativeAnimation
  • translate3d(0, x, 0)是生涩的,但不是很糟糕。
  • 使用translate3d(0, x, 0)的{​​{3}}图书馆动画效果非常顺畅。

您可以看到比较Rellax。 (这种混蛋在Firefox中表现得最好。)我的库是on this pen

我不知道我图书馆的问题在哪里,我不知道如何解决。这是一个简略的粘贴,在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开发者工具中得知的是帧率不会低于60 fps,所以也许并不是因为我每帧都做太多工作,而是我正在做的事情在计算位置时,数学上是不正确的?

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

修改

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

3 个答案:

答案 0 :(得分:2)

通过在元素上实现will-change,可以提高视觉性能。 recent browsers支持该功能(不包括edge和IE)。

  

将更改的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;
}
  

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

答案 1 :(得分:0)

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

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

只是为了看它是否对性能产生积极影响。

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

答案 2 :(得分:-1)

任何触及DOM的东西都会很慢。 CSS动画很好,但如果你更新CSS,你就会触及DOM。请考虑使用canvas元素!