Подтвердить что ты не робот

Изменение CSS-преобразования при прокрутке: отрывистое движение против плавного движения

Я недоволен существующими библиотеками параллакса, поэтому я пытаюсь написать свои собственные. Мой текущий состоит из трех основных классов:

  • ScrollDetector отслеживает позицию прокрутки элемента относительно экрана; он имеет функции для возврата float, представляющего его текущую позицию:
    • 0 представляет верхний край элемента, находящийся в нижнем краю окна просмотра.
    • 1 представляет нижний край элемента, находящегося на верхнем краю окна просмотра.
    • Все остальные позиции интерполируются/экстраполируются линейно.
  • ScrollAnimation использует экземпляр ScrollDetector для интерполяции произвольных значений CSS для другого элемента на основе элемента ScrollDetector.
  • ParallaxativeAnimation extends ScrollAnimation для специального случая фонового изображения, которое должно прокручиваться с точным коэффициентом скорости прокрутки окна.

Моя текущая ситуация такова:

  • ScrollAnimation с помощью transform: translateY(x) работает плавно.
  • ParallaxativeAnimation с помощью translateY(x) работает, но оживляется.
  • ParallaxativeAnimation с использованием translate3d(0, x, 0) являются отрывистыми, но не так сильно.
  • Анимированные библиотеки Rellax, которые используют translate3d(0, x, 0), работают совершенно гладко.

Вы можете увидеть сравнение в этом пером. (В Firefox 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 кадров в секунду, так что, возможно, это не то, что я делаю слишком много работы в каждом кадре, но что я делаю что-то математически неверно, когда я вычисляю позицию?

Так что я не знаю. Я явно в голове. Мне жаль бросать целую библиотеку в StackOverflow и говорить "FIX IT", но если кто-то может сказать, что я делаю неправильно, или расскажите, как использовать инструменты для разработчиков, чтобы понять, что я делаю неправильно, я Я очень ценю это.


ИЗМЕНИТЬ

Хорошо, я понял, что самым важным фактором в дрожании прокрутки является высота переводимого элемента. У меня был просчет в моей библиотеке, который заставлял фоновое изображение быть намного выше, чем нужно, когда мое свойство scrollPixelsPerParallaxPixel было высоким. Я сейчас пытаюсь исправить это.

4b9b3361

Ответ 1

Помимо вычислений, вы можете попробовать запустить его асинхронно, используя Promise:

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

чтобы увидеть, оказывает ли это положительное влияние на производительность.

Я бы прокомментировал, но пока у меня недостаточно репутации.

Ответ 2

Все, что касается DOM, будет медленным. CSS-анимации прекрасны, но если вы обновите CSS, вы коснетесь DOM, и он будет медленным. Вместо этого рассмотрите использование элемента canvas!