Uwaga: poniższe informacje są przede wszystkim wynikiem moich własnych eksperymentów w React Native 0.50. W ScrollView
dokumentacji brakuje obecnie wielu informacji przedstawionych poniżej; na przykład onScrollEndDrag
jest całkowicie nieudokumentowany. Ponieważ wszystko tutaj opiera się na nieudokumentowanym zachowaniu, niestety nie mogę obiecać, że te informacje pozostaną poprawne za rok, a nawet za miesiąc.
Ponadto wszystko poniżej zakłada czysto pionowy widok scroll, którego offset Y nas interesuje; tłumaczenie na przesunięcia x , jeśli zajdzie taka potrzeba, jest miejmy nadzieję łatwym ćwiczeniem dla czytelnika.
Różne programy obsługi zdarzeń w ScrollView
ujęciu event
i pozwalają uzyskać bieżącą pozycję przewijania za pośrednictwem event.nativeEvent.contentOffset.y
. Niektóre z tych programów obsługi mają nieco inne zachowanie między systemami Android i iOS, jak opisano szczegółowo poniżej.
W systemie Android
Odpala każdą klatkę, gdy użytkownik przewija, na każdej klatce, gdy widok przewijania przesuwa się po jej zwolnieniu, na ostatniej klatce, gdy widok przewijania zatrzymuje się, a także zawsze, gdy przesunięcie widoku przewijania zmienia się w wyniku jego ramki zmieniające się (np. z powodu rotacji z poziomego na pionowy).
Na iOS
Pożary, gdy użytkownik przeciąga lub gdy przesuwa się widok przewijania, z pewną częstotliwością określoną przez scrollEventThrottle
i co najwyżej raz na klatkę, kiedy scrollEventThrottle={16}
. Jeśli użytkownik zwolni widok przewijania, gdy ma on wystarczający pęd do szybowania, przewodnik onScroll
również wystrzeli, gdy dojdzie do spoczynku po szybowaniu. Jednakże, jeśli użytkownik przeciągnie, a następnie zwolni widok przewijania, gdy jest on nieruchomy, nieonScroll
ma gwarancji, że uruchomi się w końcowej pozycji, chyba że zostało ustawione tak, że uruchamia każdą klatkę przewijania.scrollEventThrottle
onScroll
Ustawienie wiąże się z kosztem wydajności, scrollEventThrottle={16}
który można zmniejszyć, ustawiając go na większą liczbę. Oznacza to jednak, że onScroll
nie będzie odpalać każdej klatki.
Uruchamia się, gdy widok przewijania zatrzymuje się po szybowaniu. W ogóle nie uruchamia się, jeśli użytkownik zwolni widok przewijania, gdy jest nieruchomy, tak że nie ślizga się.
onScrollEndDrag
Uruchamia się, gdy użytkownik przestaje przeciągać widok przewijania - niezależnie od tego, czy widok przewijania jest nieruchomy, czy zaczyna się ślizgać.
Biorąc pod uwagę te różnice w zachowaniu, najlepszy sposób śledzenia przesunięcia zależy od konkretnych okoliczności. W najbardziej skomplikowanym przypadku (musisz obsługiwać Androida i iOS, w tym obsługiwać zmiany w ScrollView
ramce ze względu na obrót, a nie chcesz akceptować spadku wydajności na Androidzie z ustawienia scrollEventThrottle
na 16) i musisz obsługiwać zmienia się również zawartość w widoku przewijania, wtedy jest to właściwy cholerny bałagan.
Najprostszy przypadek jest taki, jeśli potrzebujesz tylko obsługi Androida; po prostu użyj onScroll
:
<ScrollView
onScroll={event => {
this.yOffset = event.nativeEvent.contentOffset.y
}}
>
Aby dodatkowo obsługiwać iOS, jeśli z przyjemnością uruchamiasz program onScroll
obsługi każdą klatkę i akceptujesz wpływ na wydajność, a jeśli nie musisz obsługiwać zmian ramek, jest to tylko trochę bardziej skomplikowane:
<ScrollView
onScroll={event => {
this.yOffset = event.nativeEvent.contentOffset.y
}}
scrollEventThrottle={16}
>
Aby zmniejszyć narzut wydajności na iOS, jednocześnie gwarantując, że rejestrujemy dowolną pozycję, na której ustala się widok przewijania, możemy zwiększyć scrollEventThrottle
i dodatkowo zapewnić onScrollEndDrag
obsługę:
<ScrollView
onScroll={event => {
this.yOffset = event.nativeEvent.contentOffset.y
}}
onScrollEndDrag={event => {
this.yOffset = event.nativeEvent.contentOffset.y
}}
scrollEventThrottle={160}
>
Ale jeśli chcemy obsłużyć zmiany ramek (np. Ponieważ pozwalamy na obracanie urządzenia, zmieniając dostępną wysokość ramki widoku scroll) i / lub zmiany treści, to musimy dodatkowo zaimplementować oba onContentSizeChange
i onLayout
śledzić wysokość obu ramka widoku przewijania i jej zawartość, a tym samym nieustannie obliczaj maksymalne możliwe przesunięcie i wnioskuj, kiedy przesunięcie zostało automatycznie zmniejszone z powodu zmiany rozmiaru ramki lub zawartości:
<ScrollView
onLayout={event => {
this.frameHeight = event.nativeEvent.layout.height;
const maxOffset = this.contentHeight - this.frameHeight;
if (maxOffset < this.yOffset) {
this.yOffset = maxOffset;
}
}}
onContentSizeChange={(contentWidth, contentHeight) => {
this.contentHeight = contentHeight;
const maxOffset = this.contentHeight - this.frameHeight;
if (maxOffset < this.yOffset) {
this.yOffset = maxOffset;
}
}}
onScroll={event => {
this.yOffset = event.nativeEvent.contentOffset.y;
}}
onScrollEndDrag={event => {
this.yOffset = event.nativeEvent.contentOffset.y;
}}
scrollEventThrottle={160}
>
Tak, to dość przerażające. Nie jestem też w 100% pewien, że zawsze będzie działać dobrze w przypadkach, gdy jednocześnie zmieniasz rozmiar ramki i zawartość widoku przewijania. Ale to najlepsze, co mogę wymyślić, i dopóki ta funkcja nie zostanie dodana w ramach samej struktury , myślę, że jest to najlepsze, co każdy może zrobić.