1
votes

RXJS - N'émet que des valeurs plus grandes que la dernière valeur

Je souhaite implémenter un écouteur de défilement pour mon site avec rxjs. L'auditeur émet actuellement chaque numéro scrollY. Est-il possible d'implémenter un écouteur de défilement qui n'émet que la position de défilement si le nombre est plus élevé qu'avant SANS STOCKER UNE PROPRIÉTÉ STATEFULL . Je voudrais résoudre ce problème uniquement avec les opérateurs. L'implémentation actuelle ressemble à ceci:

  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)
    );
  }

astuces

Une approche pourrait déjà être d'ajouter l'opérateur startsWith (0) . Cela émettrait la position initiale à 0. Mais si scan () , filter () ou reduction () aiderait, je ne peux pas le dire.

usecase

Je fais défiler jusqu'à Y = 300. 300 devraient être émis. Je fais défiler jusqu'à Y = 50. Rien ne doit être émis. Je fais défiler à nouveau jusqu'à 150, 150 devraient être émis.


0 commentaires

4 Réponses :


3
votes

Je suppose que vous pouvez utiliser par paires pour cela:

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

 RXJS - N'émettre que des valeurs supérieures à la dernière valeur

Vérifier ce code dans un terrain de jeu

J'espère que cela vous aidera

p >


4 commentaires

Je vais tester ça dès que possible! Fait amusant: une partie de ma thèse de baccalauréat portait sur RxJS et toujours là, ce n'est pas crédible à quel point je ne sais toujours pas. ^^


@ creep-story, oui, il y a tellement de choses à propos de FRP! Assurez-vous de vérifier le terrain de jeu que j'ai lié - cela pourrait vous aider à obtenir une meilleure vue sur Rx et d'autres bibliothèques FRP! Aussi, je pense maintenant que l'option suggérée par Daniel est meilleure.


@ creep-story, j'ai ajouté une autre réponse avec un opérateur personnalisé. Je pense que cette approche devrait être préférable à celle-ci. / pendant un moment, j'ai pensé qu'un simple distinctUntilChanged avec un prédicat résoudrait votre problème, mais il s'est avéré qu'il compare la valeur précédemment passée avec la nouvelle, alors que vous devez comparer toute valeur précédente avec la nouvelle /


votre approche fonctionne très bien avec l'opérateur personnalisé, mais j'apprécierais si vous consultez également ma solution. :)



3
votes

Vous pouvez utiliser l'opérateur scan ainsi que distinctUntilChanged :

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


1 commentaires

Bien que j'aie vraiment apprécié la simplicité de cette approche, il semble qu'elle n'émettra qu'une position de défilement croissante, et n'émettra pas si l'utilisateur faisait défiler vers le bas puis vers le haut puis vers le bas à nouveau, voir cet exemple . Pourtant, bonne utilisation de scan ici



1
votes

N'étant pas satisfait de mon approche précédente, j'ai décidé de créer un opérateur personnalisé qui englobe rxjs.filter et utilisera un prédicat pour comparer la valeur actuelle à la précédente:

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


0 commentaires

1
votes

Bien que je sois si reconnaissant de l'aide de @Kos et @Daniel pour avoir mis le temps de m'aider à trouver une solution propre, j'ai trouvé une approche qui est propre et simple.

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 & curr is greaterOrEqual 0 => scroll up
      map(([prev, curr]: Array<number>) => prev - curr >= 0)
    );


8 commentaires

Eh bien, c'est totalement cool si vous allez faire if-else plus tard. Deux choses: vous voudrez probablement déplacer distinctUntilChanged vers le bas - afaik, le document n'émettra pas d'événement de défilement sans modifications. Placez startWith après la carte et ayez juste startWith (0) . Et vous n'avez certainement pas besoin d'un Boolean (..) , juste prev-curr> = 0 est déjà un booléen


Ouais tu as raison, lol. ^^ Mais je n'ai plus besoin de si / sinon. :)


qu'en est-il de "si vrai alors faites défiler vers le haut"? )


ahh tu veux dire ça ... oui mon abonnement ressemble à ceci: ... .subscribe (show => this.showAddButton = show);


Cool :) Ouais, eh bien, nos solutions visaient à filtrer, pas à cartographier. Amusez-vous bien Rx-ing!


distinctUntilChanged () peut être utile dans les situations où il y a aussi un défilement vertical, dans ce cas les événements seront déclenchés avec la même valeur scrollY . Je changerais simplement la carte du bas en filter (([prev, next]) => next> prev) pour que l'observable n'émette pas toutes les valeurs, juste celles qui sont plus élevées. Sauf si je me trompe :)


Honnêtement, j'ai dû marquer la réponse @Kos comme la bonne réponse, car ma question initiale était également de filtrer les valeurs pour ne pas les mapper. Nécessaire pour corriger cela. Merci!


Nous aurions probablement dû demander plus de détails :) Ce que @MaksymCierzniak pourrait signifier avec la première partie est que distinctUntilChanged peut être déplacé vers le bas, par exemple map-startWith-pairwise-map-distinctUntilChanged . Peut-être que j'avais également déplacé le anti-rebond pour qu'il soit le dernier - vous devez jouer ici pour tester la performance.