Как контролировать инерционную прокрутку? Предотвратить множественные вызовы API из-за инерции - PullRequest
0 голосов
/ 25 октября 2019

Я должен сделать запрос к API, когда window.scrollTop < 20. В случае успеха я добавляю новый дочерний компонент к вершине .list компонента.

Я использую throttle, чтобы предотвратить множественные вызовы API, которые происходят из-за инерции прокрутки. Это хорошо работает, если длительность выборки близка к длительность дросселя и оба значения больше 800 (я не уверен, почему это работает хорошо только тогда, когда продолжительность больше 800, не обязательно, но работаетхорошо для большой продолжительности). В противном случае это не работает - скажем, длительность близка к 300.

Поведение -

  1. из-за прокрутки по инерции, окно продолжает прокручиваться после успешной выборки иwindow.scrollTop еще раз меньше 20, что делает еще один вызов API. или
  2. window.scrollTop меньше 20 и полоса прокрутки достигает вершины.

Параметры, от которых зависит это поведение: :

  1. импульс прокрутки
  2. высота предварительно добавленного списка дочернего элемента.
  3. длительность газа
  4. длительность выборки

Кконец инерции прокрутки я пробовал overflow: hidden. Это делает пользовательский интерфейс невосприимчивым к длительности выборки, что недопустимо.

Я сохраняю позицию scrollTop, которая была во время запроса на выборку, для которой я выполняю window.scrollTo(0, scrollHeight - state.docScrollHeight). Я записываю позицию scrollTop во время запроса на выборку в state.docScrollHeight.

Я хочу предотвратить несколько вызовов API и поддерживать позицию прокрутки.

Fiddle: https://jsfiddle.net/05mwb3hq/1/

Эта проблема лучше всего воспроизводится в инструменте разработчика.

var state = {
  loadingMore: false,
  docScrollHeight: 0,
  lastCall: 0,
  lastCallToFetch: 0,
}

var count = 0;

function createEl() {
  let el = document.createElement('div');
  el.classList.add('list-comp');
  let elNum = document.createElement('span');
  elNum.textContent = count;
  elNum.classList.add('elNum');
  el.appendChild(elNum);
  const listEl = document.querySelector('#list');
  listEl.prepend(el);
  count++;
}

function mockApi(lagDuration) {
  return new Promise(resolve => {
    setTimeout(resolve, lagDuration)
  });
}

function throttle(fetchCb, throttleDuration) {
  let previousCall = state.lastCall;
  state.lastCall = Date.now();

  if (previousCall === undefined || (state.lastCall - previousCall) > throttleDuration) {
    state.lastCallToFetch = Date.now();
    state.loadingMore = true;
    document.querySelector('.loader').classList.remove('d-none');
    fetchCb(300)
      .then(() => {
        createEl();
        state.loadingMore = false;
      })
      .then(() => {
        const {
          scrollHeight
        } = document.documentElement;
        if (!state.loadingMore) {
          window.scrollTo(0, scrollHeight - state.docScrollHeight);
          document.querySelector('.loader').classList.add('d-none');
        }
      });
  }
}

var onScrollList = function on_scroll_list() {
  if (window.scrollY < 20) {
    if (!state.loadingMore) {
      state.docScrollHeight = document.documentElement.scrollHeight;
      state.loagingMore = true;
      throttle(mockApi, 800);
    }
  }
}

window.addEventListener('scroll', onScrollList);

window.onload = function() {
  window.scrollTo(0, document.body.clientHeight);
}
* {
  box-sizing: border-box;
}

.d-none {
  display: none;
}

#main-scrollable-comp {
  overflow-y: scroll;
  background-color: grey;
}

#default-comp {
  background-color: red;
  height: 800px;
}

.list-comp {
  height: 200px;
  width: 100%;
  background-color: purple;
  border: 2px solid yellow;
  display: flex;
  align-items: flex-end;
  justify-content: center;
}

.elNum {
  color: green;
  font-size: 54px;
}

.loader-container {
  height: 100px;
  background-color: white;
  display: flex;
  align-items: center;
  justify-content: center;
}

.loader {
  border: 4px solid #f3f3f3;
  border-top: 4px solid #3498db;
  border-radius: 50%;
  width: 36px;
  height: 36px;
  animation: spin 1s linear infinite;
}

@keyframes spin {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }
}
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>Smooth Scroll</title>
  <link href="styles.css" rel="stylesheet">
</head>

<body>
  <div id="main-scrollable-comp">
    <div class='loader-container'>
      <div class='loader d-none'></div>
    </div>
    <div id='list'></div>
    <div id="default-comp"></div>
  </div>
</body>

</html>
...