Zdarzenia wskaźnikowe w React Native
Ta strona została przetłumaczona przez PageTurner AI (beta). Nie jest oficjalnie zatwierdzona przez projekt. Znalazłeś błąd? Zgłoś problem →
Dziś udostępniamy eksperymentalną, wieloplatformową API wskaźników dla React Native. Omówimy motywację, działanie oraz korzyści dla użytkowników. Znajdziecie też instrukcje włączania tej funkcji - z niecierpliwością czekamy na wasze opinie!
Minął ponad rok od przedstawienia naszej wizji wielu platform, która pokazywała zalety wykraczania poza mobilne i podnosiła poprzeczkę dla wszystkich systemów. W tym czasie zwiększyliśmy inwestycje w React Native dla VR, desktopu i webu. Różnice w sprzęcie i interakcjach na tych platformach skłoniły nas do przemyślenia, jak React Native powinien kompleksowo obsługiwać wprowadzanie danych.
Wykraczając poza dotyk
Desktop i VR tradycyjnie opierały się na myszy i klawiaturze, podczas gdy mobilne - głównie na dotyku. To podejście ewoluowało wraz z laptopami z ekranami dotykowymi i rosnącymi potrzebami obsługi klawiatury czy pióra na urządzeniach mobilnych. Obecny system zdarzeń dotykowych w React Native nie radzi sobie z tymi wyzwaniami.
W rezultacie użytkownicy platform spoza głównego drzewa forkowali React Native i/lub tworzyli własne komponenty natywne, by obsługiwać kluczowe funkcje jak wykrywanie najechania czy lewy przycisk myszy. To rozproszenie prowadzi do nadmiarowości właściwości z obsługą zdarzeń o podobnym celu, ale dla różnych platform. Zwiększa to złożoność frameworka i utrudnia współdzielenie kodu między platformami. Z tych powodów zespół postanowił dostarczyć wieloplatformową API wskaźników.
React Native dąży do dostarczania solidnych i ekspresyjnych API do budowania aplikacji na wiele platform przy zachowaniu charakterystycznych doświadczeń platformowych. Zaprojektowanie takiego API jest wyzwaniem, ale na szczęście istnieją wcześniejsze rozwiązania w obszarze wskaźników, z których React Native może skorzystać.
Czerpiąc z Webu
Web to platforma z podobnymi wyzwaniami skalowania na wiele systemów przy jednoczesnym uwzględnianiu przyszłościowego projektu. Konsorcjum World Wide Web (W3C) odpowiada za ustalanie standardów i propozycji budowy sieci współdziałającej między różnymi platformami i przeglądarkami.
Najbardziej istotne dla naszych potrzeb, W3C zdefiniowało zachowanie abstrakcyjnej formy wprowadzania danych zwanej wskaźnikiem. Specyfikacja Pointer Events rozszerza zdarzenia myszy i ma dostarczać jednolity zestaw zdarzeń i interfejsów dla wprowadzania danych z różnych urządzeń, pozwalając jednocześnie na obsługę specyficzną dla danego urządzenia gdy to konieczne.
Implementacja specyfikacji Pointer Events przynosi użytkownikom React Native wiele korzyści. Poza rozwiązaniem wspomnianych problemów, podnosi możliwości platform, które historycznie nie musiały obsługiwać wielu typów wprowadzania danych. Pomyśl o podłączaniu myszy Bluetooth do telefonu z Androidem czy obsłudze najechania przez Apple Pencil na iPadzie M2.
Zgodność ze specyfikacją daje też możliwość wymiany wiedzy między Webem a React Native. Edukacja o oczekiwaniach Weba dotyczących Pointer Events może jednocześnie służyć developerom React Native. Rozumiemy jednak, że wymagania React Native różnią się od webowych, a nasze podejście do specyfikacji to "najlepszy wysiłek" z dobrze udokumentowanymi odstępstwami dla jasności oczekiwań. Trwają prace nad dostosowaniem niektórych standardów webowych, by zmniejszyć fragmentację API w obszarach dostępności i wydajności.
Przenoszenie testów platformowych Weba
Choć specyfikacja Pointer Events dostarcza interfejsy i opisy zachowań API, uznaliśmy, że nie są wystarczająco szczegółowe, by pewnie wprowadzać zmiany i powoływać się na specyfikację jako weryfikację. Przeglądarki używają jednak innego mechanizmu zapewniającego zgodność i interoperacyjność - testów platformowych Weba!
Testy platformowe Weba pisane są pod imperatywne API DOM przeglądarek - nieobsługiwane przez React Native, który używa własnych prymitywów widoków. Oznacza to, że nie możemy współdzielić tych testów z przeglądarkami i zamiast tego stworzyliśmy analogiczne API testowe dla React Native, ułatwiające przenoszenie testów platformowych Weba.
Wdrożyliśmy nowy framework testów manualnych, którego używamy do weryfikacji naszych implementacji przez RNTester. Testy te tymczasowo nazywamy RNTester Platform Tests i są wciąż dość podstawowe. Nasza implementacja dostarcza API do budowania przypadków testowych jako komponentów, które są renderowane, a wyniki raportowane wyłącznie przez interfejs użytkownika.

Testy te będą nadal pomocne w miarę rozwijania naszej implementacji zdarzeń wskaźnikowych. Będą również skalować się do testowania implementacji na platformach innych niż Android i iOS. Wraz ze wzrostem liczby testów w naszym zestawie będziemy dążyć do zautomatyzowania ich uruchamiania, aby lepiej wykrywać regresje w implementacjach.
Jak to działa
Duża część naszej implementacji zdarzeń wskaźnikowych bazuje na istniejącej infrastrukturze do obsługi zdarzeń dotykowych. Na Androidzie i iOS wykorzystujemy odpowiednio zdarzenia MotionEvent i UITouch. Ogólny przepływ dystrybucji zdarzeń przedstawiono poniżej.

Na przykładzie Androida, ogólne podejście do wykorzystania zdarzeń platformowych to:
-
Iteracja przez wszystkie wskaźniki
MotionEventi przeszukiwanie w głąb w celu określenia docelowego widoku Reacta dla każdego wskaźnika oraz jego ścieżki przodków. -
Mapowanie kategorii
MotionEventna odpowiednie zdarzenia wskaźnikowe. Istnieje relacja 1-do-wielu międzyMotionEventaPointerEvent. Na diagramie ilustrującym ich zależność, przerywane linie oznaczają zdarzenia wywoływane, gdy urządzenie wskazujące nie obsługuje najeżdżania (hover).

-
Budowanie interfejsu
PointerEventz wykorzystaniem szczegółów platformy zMotionEventi stanu buforowanego z poprzednich interakcji (np. właściwośćbutton). -
Przesyłanie zdarzeń wskaźnikowych z Androida do kolejki zdarzeń React Native i wykorzystanie JSI do wywołania metody
dispatchEventwreact-native-renderer, która iteruje przez drzewo Reacta w fazie bąbelkowania i przechwytywania zdarzenia.
Postęp implementacji
Jeśli chodzi o obecny postęp implementacji specyfikacji zdarzeń wskaźnikowych, skupiliśmy się na solidnej implementacji podstawowej najczęstszych zdarzeń obsługujących naciskanie, najeżdżanie i przesuwanie.
Zdarzenia
| Implemented | Work in Progress | Yet to be Implemented |
|---|---|---|
| onPointerOver | onPointerCancel | onClick |
| onPointerEnter | onContextMenu | |
| onPointerDown | onGotPointerCapture | |
| onPointerMove | onLostPointerCapture | |
| onPointerUp | onPointerRawUpdate | |
| onPointerOut | ||
| onPointerLeave |
onPointerCancel został podpięty pod natywne zdarzenie "cancel" platformy, ale niekoniecznie odpowiada to sytuacjom, w których platforma webowa oczekuje jego wywołania.
Właściwości zdarzeń
Dla każdego wspomnianego zdarzenia zaimplementowaliśmy większość właściwości oczekiwanych w obiekcie PointerEvent — choć w React Native są one dostępne przez właściwość event.nativeEvent. Pełną listę zaimplementowanych właściwości można znaleźć w definicji interfejsu Flowtype. Godnym uwagi wyjątkiem jest właściwość relatedTarget, której pełna implementacja jest nietrywialna ze względu na trudności w udostępnianiu natywnych referencji widoków w tym kontekście.
Prace przyszłościowe i eksploracje
Oprócz wymienionych powyżej zdarzeń istnieją również inne interfejsy API związane ze zdarzeniami wskaźnika (Pointer Events). W ramach tego projektu planujemy wdrożyć następujące funkcjonalności:
-
Pointer Capture API
- Obejmuje imperatywne API dostępne poprzez referencje do elementów, w tym
setPointerCapture(),releasePointerCapture()orazhasPointerCapture().
- Obejmuje imperatywne API dostępne poprzez referencje do elementów, w tym
-
Właściwość stylu
touch-action- W środowisku webowym ta właściwość CSS służy do deklaratywnego negocjowania gestów między przeglądarką a kodem obsługi zdarzeń strony. W React Native może być wykorzystana do koordynacji obsługi zdarzeń między procedurami zdarzeń wskaźnika komponentu View a nadrzędnym ScrollView.
-
click,contextmenu,auxclickclickto abstrakcyjna definicja interakcji, która może być wywołana poprzez mechanizmy dostępności (accessibility) lub inne charakterystyczne dla platformy interakcje.
Kolejną zaletą natywnej implementacji zdarzeń wskaźnika jest możliwość usprawnienia obsługi gestów, obecnie ograniczonej tylko do zdarzeń dotykowych i realizowanej w JavaScript poprzez interfejsy Responder, Pressability i PanResponder.
Ponadto badamy możliwość wdrożenia interfejsu EventTarget dla hostowych komponentów React Native (tj. add/removeEventListener), co umożliwi tworzenie dodatkowych abstrakcji w kodzie użytkownika do obsługi interakcji wskaźnika.
Testowanie funkcjonalności
Nasza implementacja zdarzeń wskaźnika jest nadal w fazie eksperymentalnej, ale zależy nam na opinii społeczności. Aby przetestować to API, należy włączyć kilka flag funkcjonalności:
Włączanie flag funkcjonalności
Nadpisywanie natywnych flag funkcji poniżej (takich jak RCTConstants i ReactFeatureFlags) sięga technicznie do wewnętrznych mechanizmów React Native. Takie działanie może wkrótce zakłócić Twoją konfigurację, ponieważ pracujemy nad ich wycofaniem w celu szerszego wdrożenia obsługi zdarzeń wskaźnikowych.
Zdarzenia wskaźnika są dostępne tylko w Nowej Architekturze (Fabric) i wymagają React Native 0.71+, który w momencie pisania tego tekstu jest wersją release candidate.
W głównym pliku JavaScript (index.js w domyślnym szablonie React Native) włącz flagę shouldEmitW3CPointerEvents dla zdarzeń wskaźnika oraz shouldPressibilityUseW3CPointerEventsForHover do integracji z Pressability.
import ReactNativeFeatureFlags from 'react-native/Libraries/ReactNative/ReactNativeFeatureFlags';
// enable the JS-side of the w3c PointerEvent implementation
ReactNativeFeatureFlags.shouldEmitW3CPointerEvents = () => true;
// enable hover events in Pressibility to be backed by the PointerEvent implementation
ReactNativeFeatureFlags.shouldPressibilityUseW3CPointerEventsForHover =
() => true;
Konfiguracja dla iOS
Aby zapewnić wysyłanie zdarzeń z natywnego renderera iOS, włącz odpowiednią flagę w kodzie inicjalizacyjnym aplikacji (zwykle w pliku AppDelegate.mm).
#import <React/RCTConstants.h>
// ...
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
RCTSetDispatchW3CPointerEvents(YES);
// ...
}
Aby implementacja rozróżniała wskaźniki myszy i dotyku na iOS, dodaj UIApplicationSupportsIndirectInputEvents do pliku info.plist projektu w Xcode.
Konfiguracja dla Androida
Podobnie jak w iOS, włącz flagę funkcjonalności w kodzie inicjalizacyjnym aplikacji Androida (zwykle w metodzie onCreate głównej aktywności React lub surface).
import com.facebook.react.config.ReactFeatureFlags;
//... somewhere in initialization
@Override
public void onCreate() {
ReactFeatureFlags.dispatchPointerEvents = true;
}
JavaScript
function onPointerOver(event) {
console.log(
'Over blue box offset: ',
event.nativeEvent.offsetX,
event.nativeEvent.offsetY,
);
}
// ... in some component
<View
onPointerOver={onPointerOver}
style={{height: 100, width: 100, backgroundColor: 'blue'}}
/>;
Zapraszamy do przekazywania opinii
Obecnie zdarzenia wskaźnika zasilają naszą platformę VR i sklep Oculus, ale zależy nam na wczesnych opiniach społeczności dotyczących zarówno podejścia, jak i implementacji. Jeśli masz pytania lub przemyślenia, dołącz do dedykowanej dyskusji o zdarzeniach wskaźnika.

