RXJS — выдает только большие значения, чем последнее значение

#angular #functional-programming #rxjs #observable

#angular #функциональное программирование #rxjs #наблюдаемый

Вопрос:

Я хочу реализовать прослушиватель прокрутки для своего сайта с помощью rxjs. В настоящее время прослушиватель выдает каждое число scrollY. Возможно ли реализовать прослушиватель прокрутки, который выдает только позицию прокрутки, если число больше, чем раньше, БЕЗ СОХРАНЕНИЯ СВОЙСТВА STATEFULL. Я хотел бы решить это только с помощью операторов. Текущая реализация выглядит следующим образом:

   public lastScrolledHeight: number = 0;
  ...
  public ngOnInit() {
    this.listenForScroll()
      .subscribe(scrollHeight => {
        this.showAddButton = this.lastScrolledHeight >= scrollHeight; // true or false
        this.lastScrolledHeight = scrollHeight;
      });
  }

  private listenForScroll(): Observable<number> {
    return fromEvent(window, 'scroll').pipe(
      debounceTime(25),
      map((e: any) => e.path[1].scrollY)
    );
  }
 

подсказки

Одним из подходов уже может быть добавление startsWith(0) оператора. Это приведет к тому, что начальная позиция будет равна 0. Но если scan() filter() или reduce() поможет, я не могу сказать.

usecase

Я прокручиваю вниз до Y = 300. должно быть выдано 300. Я прокручиваю до Y = 50. Ничего не должно выдаваться. Я снова прокручиваю вниз до 150, должно быть выдано 150.

Ответ №1:

Вы можете использовать scan оператор вместе с distinctUntilChanged :

 return fromEvent(window, 'scroll').pipe(
  debounceTime(25),
  map((e: any) => e.path[1].scrollY),
  scan((prev, curr) => Math.max(prev, curr), 0),
  distinctUntilChanged()
)
 

Что происходит, так это то, что наблюдаемое изменяется, чтобы содержать максимальное значение текущей и предыдущей итерации (и имеет значение 0 в качестве начального значения).

После этого проверяется, distinctUntilChanged() что наблюдаемые повторяющиеся события не генерируются.

Это гарантирует, что вы будете получать только значения, которые больше предыдущего.

Комментарии:

1. Хотя мне действительно понравилась простота этого подхода, кажется, что он будет выдавать только увеличивающуюся позицию прокрутки и не будет выдавать, если пользователь будет прокручивать вниз, затем вверх, затем снова вниз, см. Этот пример . Тем не менее, хорошее использование scan здесь

Ответ №2:

Я думаю, вы могли бы использовать для этого попарно:

 source$.pipe(
  startWith(-1),
  pairwise(),
  switchMap(([a,b])=>
    b > a
    ? of(b)
    : EMPTY
  )
)
 

RXJS - выдает только большие значения, чем последнее значение

Проверьте этот код на игровой площадке

Надеюсь, это поможет

Комментарии:

1. Я проверю это как можно скорее! Забавный факт: часть моей бакалаврской диссертации была посвящена RxJS, и все еще непонятно, насколько я все еще не знаю. ^^

2. @creep-story, да, в FRP так много всего! Обязательно проверьте ту игровую площадку, на которую я ссылался — это может помочь вам лучше просматривать Rx и другие библиотеки FRP! Кроме того, теперь я думаю, что вариант, предложенный Даниэлем, выглядит лучше.

3. @creep-story, я добавил еще один ответ с помощью пользовательского оператора. Я думаю, что этот подход должен быть предпочтительнее этого. / на мгновение я подумал, что простой distinctUntilChanged с предикатом решит вашу проблему, но оказалось, что он сравнивает ранее переданное значение с новым, в то время как вам нужно сравнить любое предыдущее значение с новым /

4. ваша оценка отлично работает с пользовательским оператором, однако я бы оценил, если вы также ознакомитесь с моим решением. 🙂

Ответ №3:

Хотя я так благодарен за помощь @Kos и @Daniel за то, что они потратили время, чтобы помочь мне найти чистое решение, я нашел подход, который является чистым и простым.

 fromEvent(document, 'scroll').pipe(
      debounceTime(50),
      // get scrollY
      map((e: any) => e.path[1].scrollY),
      startWith(0),
      distinctUntilChanged(),
      // map the last scroll values into an array
      pairwise(),
      // returns true if delta of prev amp; curr is greaterOrEqual 0 => scroll up
      map(([prev, curr]: Array<number>) => prev - curr >= 0)
    );
 

Комментарии:

1. Ну, это совершенно круто, если вы собираетесь сделать if-else позже. Пара вещей: вы, вероятно, захотите переместиться distinctUntilChanged вниз — afaik, документ не будет генерировать событие прокрутки без изменений. Поместите startWith после карты и просто startWith(0) . И вам определенно не нужно Boolean(..) , просто prev-curr >= 0 это уже логическое значение

2. Да, ты прав, лол. ^^ Но мне больше не нужно ничего делать, если / else. 🙂

3. ах, ты имеешь в виду это … да, моя подписка выглядит так: … .subscribe(show => this.showAddButton = show);

4. Круто 🙂 Да, но наши решения были направлены на фильтрацию, а не на отображение. Получайте удовольствие от Rx-ing!

5. distinctUntilChanged() может быть полезно в ситуации, когда есть также вертикальная прокрутка, в этом случае события будут запускаться с тем же scrollY значением. Я бы просто изменил карту внизу filter(([prev, next]) => next > prev) , чтобы наблюдаемая не выдавала все значения, только те, которые выше. Если я не понял что-то не так 🙂

Ответ №4:

Будучи неудовлетворенным моим предыдущим подходом, я решил создать пользовательский оператор, который охватывает rxjs.filter и будет использовать предикат для сравнения текущего значения с предыдущим:

 // it will take a predicate to compare values
// by default it will behave as distinctUntilChanged()
const filterChanges = (predicate = ((a,b) => a!==b)) => {
  // store previous value
  let prevValue = void 0;
  return pipe(
    filter((value, index)=>{
      // pass through the first value on stream
      if (index === 0) {
        prevValue = value;
        return value;
      }

      // compare current with prev
      const result = predicate(value, prevValue);
      prevValue = value;
      return resu<
    })
  );
};
 

И тогда это так же просто, как передача компаратора:

 source$.pipe(
  filterChanges((a, b) => a > b)
)
 

Вывод:

фильтр изменяет пользовательский оператор

Вот пример игровой площадки.

Надеюсь, это поможет