Μεταφορά κοινόχρηστου στοιχείου με Reactive Native

Σε αυτή την ανάρτηση θα μιλήσω για το πώς θα επιτύχουμε τη μετάβαση Shared Element with React Native τόσο για το iOS όσο και για το Android.

Έχω δημοσιεύσει τον κώδικα στο GitHub και μπορείτε να ρίξετε μια ματιά εάν θέλετε να πηδήσετε δεξιά σε αυτό.

Πρόθεση

Ας ρίξουμε μια ματιά σε αυτό που θα χτίσει. Παρακάτω υπάρχει ένα παράδειγμα πλέγματος φωτογραφιών όπου θα προσθέσουμε μια κοινή μετάβαση στοιχείων. Με αυτό τον τρόπο, μπορούμε να μεταβαίνουμε ομαλά μεταξύ ενός πλέγματος και της σελίδας λεπτομερειών για μια φωτογραφία που επιλέγουμε.

Πρόκειται για μια πολύ πιο ομαλή και συνεχή εμπειρία.

Πλησιάζω

Πριν δημιουργήσουμε αυτό, επιτρέψτε μου να σας πω πώς λειτουργεί το σύστημα κάτω από την κουκούλα. Από τη στιγμή που το React Native δεν υποστηρίζει πραγματικά κοινά στοιχεία, όταν λέμε ότι κάνουμε μια κοινή μετάβαση στοιχείων μεταξύ δύο οθονών, δεν μοιραζόμαστε τεχνικά κανένα στοιχείο. Αντ 'αυτού, κάθε οθόνη έχει το δικό της στοιχείο.

Αυτό που κάνω είναι να περάσω τις πληροφορίες σχετικά με το κοινό στοιχείο - όπως η θέση και το μέγεθός του - μεταξύ αυτών των δύο στοιχείων.

Όταν εκκινείται η οθόνη λεπτομερειών, το φόντο της είναι διαφανές και κρύβει όλα τα στοιχεία της. Στη συνέχεια μεταβάλλει τις ιδιότητες του κοινόχρηστου στοιχείου σε εκείνο που πέρασε και, στη συνέχεια, το καθιστά ορατό. Στη συνέχεια, ζωντανεύει στην φυσική του θέση. Καθώς η μετάβαση εξελίσσεται, το φόντο του παραθύρου και τα υπόλοιπα μη κοινόχρηστα στοιχεία σιγά-σιγά ξεθωριάζουν μέχρι να είναι τελείως αδιαφανή.

Έτσι, ενώ το στοιχείο δεν είναι τεχνικά κοινόχρηστο, αυτό το έξυπνο κόλπο καπνού και καθρέφτη το κάνει να φαίνεται ότι είναι.

Τώρα λοιπόν που καταλαβαίνουμε πώς λειτουργεί αυτή η διαδικασία, αφήνουμε να πάμε βήμα προς βήμα για να καταλάβουμε πώς μοιράστηκε το ψεύτικο στοιχείο και πώς μπορούμε να ελέγξουμε τα κινούμενα σχέδια.

Βήμα 1: Εισαγωγή και έξοδος κινούμενων εικόνων

Έχω δύο οθόνες εδώ: Πλέγμα και Λεπτομέρειες. Από την οθόνη πλέγματος, μπορούμε να ξεκινήσουμε την οθόνη λεπτομερειών κάνοντας κλικ σε μία από τις εικόνες στο πλέγμα. Στη συνέχεια μπορούμε να επιστρέψουμε στην οθόνη του πλέγματος πατώντας το κουμπί πίσω.

Όταν μεταβαίνουμε από την οθόνη πλέγματος στην οθόνη Λεπτομέρεια, έχουμε την ευκαιρία να τρέξουμε δύο σύνολα κινούμενων εικόνων μετάβασης - την έξοδο μετάβασης για την οθόνη πλέγματος και την οθόνη Μεταβατική είσοδος για την προβολή Λεπτομέρεια.

Ας δούμε πώς εφαρμόζουμε αυτό.

Χωρίς καμία μετάβαση, έτσι φαίνεται η εφαρμογή. Κάνοντας κλικ στην ατομική φωτογραφία θα μεταφερθείτε σε μια οθόνη λεπτομερειών.

Ας προσθέσουμε μια μετάβαση εξόδου στην πρώτη οθόνη πλέγματος. Εδώ χρησιμοποιούμε μια απλή μετάβαση με ξεθωριασμό χρησιμοποιώντας το Animated api, το οποίο παρεμβάλλει την ιδιότητα αδιαφάνειας του δοχείου οθόνης από 1 έως 0.

Τώρα που το κάναμε αυτό, βλέπουμε πώς φαίνεται:

ΟΧΙ άσχημα. Βλέπουμε ότι το πλέγμα είναι ξεθωριασμένο καθώς μετακινείται στην οθόνη λεπτομερειών.

Ας προσθέσουμε τώρα μια άλλη μετάβαση στο περιεχόμενο της οθόνης λεπτομέρειας καθώς έρχεται μέσα. Ας σπρώξουμε το κείμενο στη θέση από κάτω.

Αυτό γίνεται με την αντιστοίχιση μιας παρεμβαλλόμενης κινούμενης τιμής στην ιδιότητα translateY του δοχείου κειμένου.

Και εδώ φαίνεται:

Καλά ο τίτλος και η περιγραφή διαφάνειες σε πολύ ωραία, αλλά η εικόνα εμφανίζεται απότομα. Αυτό συμβαίνει επειδή η μετάβασή μας δεν στοχεύει ειδικά στην εικόνα. Θα το διορθώσουμε σύντομα.

Βήμα 2: Μεταβατικό στρώμα για το κοινόχρηστο στοιχείο

Προσθέτουμε τώρα ένα μεταβατικό επίπεδο που εμφανίζεται κατά τη διάρκεια της μετάβασης και περιέχει μόνο το κοινόχρηστο στοιχείο.

Αυτό το στρώμα ενεργοποιείται όταν πατήσετε την εικόνα στο πλέγμα. Λαμβάνει πληροφορίες σχετικά με το κοινόχρηστο στοιχείο, όπως η θέση και το μέγεθος του από την οθόνη Πλέγμα και την οθόνη Λεπτομέρειες.

Βήμα 3: Animation στο μεταβατικό στρώμα

Έχουμε τις πληροφορίες στο επίπεδο μετάβασης σχετικά με τη θέση προέλευσης και προορισμού του κοινόχρηστου στοιχείου. Πρέπει απλώς να τα ζωντανέψουμε.

Ας ρυθμίσουμε πρώτα το στοιχείο με βάση τη θέση και το μέγεθος της πηγής και στη συνέχεια να το ζωντανέψουμε στη θέση προορισμού. Αυτό μπορεί να γίνει με δύο τρόπους. Ας ρίξουμε μια ματιά και στα δύο.

Με παρεμβολή σε πλάτος, ύψος, πάνω και αριστερά

Αυτή είναι μια απλή προσέγγιση. Αν θέλουμε ένα στοιχείο να αλλάζει από ένα μέγεθος σε άλλο και από μία θέση σε άλλη, τροποποιούμε τις ιδιότητες πλάτους, ύψους, κορυφής και αριστερού ύφους του στοιχείου.

Και εδώ φαίνεται:

Ανάλυση απόδοσης

Όταν χρησιμοποιούμε το Animated, δηλώνουμε ένα γράφημα κόμβων που αντιπροσωπεύουν τις κινούμενες εικόνες που θέλουμε να εκτελέσουμε και, στη συνέχεια, χρησιμοποιήστε ένα πρόγραμμα οδήγησης για να ενημερώσετε μια τιμή Animated χρησιμοποιώντας μια προκαθορισμένη καμπύλη.

Ακολουθεί ανάλυση των βημάτων μιας κινούμενης εικόνας και όπου συμβαίνει:

https://facebook.github.io/react-native/blog/2017/02/14/using-native-driver-for-animated.html
  • JavaScript: Το πρόγραμμα οδήγησης κίνησης χρησιμοποιεί το requestAnimationFrame για εκτέλεση σε κάθε καρέ και ενημερώνει την τιμή που οδηγεί χρησιμοποιώντας τη νέα τιμή που υπολογίζει βάσει της καμπύλης κίνησης.
  • JavaScript: Οι ενδιάμεσες τιμές υπολογίζονται και διαβιβάζονται σε έναν κόμβο στηρίξεων που είναι προσαρτημένος σε μια προβολή.
  • JavaScript: Η προβολή ενημερώνεται με τη χρήση setNativeProps.
  • JavaScript στην εγγενή γέφυρα.
  • Μητρική: Οι ενημερώσεις UIView ή android.View ενημερώνονται.

Όπως μπορείτε να δείτε, το μεγαλύτερο μέρος της εργασίας συμβαίνει στο νήμα JavaScript. Εάν έχει αποκλειστεί, η κινούμενη εικόνα θα παραλείψει πλαίσια. Πρέπει επίσης να περάσει από το JavaScript σε εγγενή γέφυρα σε κάθε καρέ για να ενημερώσετε τις εγγενείς προβολές.

Αυτό το πρόβλημα μπορεί να επιλυθεί χρησιμοποιώντας το useNativeDriver. Αυτό μετακινεί όλα αυτά τα βήματα στη μητρική.

Εφόσον το κινούμενο παράγει ένα γράφημα των κινούμενων κόμβων, μπορεί να σειριοποιηθεί και να αποσταλεί σε εγγενή μόνο μία φορά όταν αρχίσει η κινούμενη εικόνα. Αυτό εξαλείφει την ανάγκη για επανάκληση στο νήμα JavaScript. Ο εγγενής κώδικας μπορεί να φροντίσει για την ενημέρωση των προβολών απευθείας στο νήμα UI σε κάθε πλαίσιο.

Ο κύριος περιορισμός είναι ότι μπορούμε να ζωντανέψουμε μόνο τις ιδιότητες χωρίς διαμόρφωση. Πράγματα όπως ο μετασχηματισμός και η αδιαφάνεια θα λειτουργήσουν, αλλά οι ιδιότητες flexbox και θέσης όπως αυτή που χρησιμοποιήθηκε παραπάνω δεν θα είναι.

Interpolating σχετικά με το μετασχηματισμό και τη χρήση useNativeDriver

Ας ασκηθούμε τώρα με τη μετατροπή. Αυτό θα απαιτήσει κάποια μαθηματικά για τον υπολογισμό της κλίμακας, x και y θέσης.

Με αυτήν την εφαρμογή, αν κλιμακώσουμε από μια μικρότερη εικόνα σε μια μεγαλύτερη, η εικόνα θα pixel. Έτσι, θα καταστήσουμε τη μεγαλύτερη εικόνα, θα την κατατάξουμε στο αρχικό της μέγεθος, και θα την ζωντανέψουμε στο φυσικό μέγεθος.

Μπορούμε να πάρουμε την τιμή κλίμακας εκκίνησης με μια γραμμή JavaScript όπως αυτή:

άνοιγμαScale = sourceDimension.width / destinationDimension.width;

Βλέπετε ότι η κλιμακούμενη εικόνα και η πρωτότυπη εικόνα δεν φαίνονται το ίδιο, διότι ο λόγος διαστάσεων της εικόνας πηγής και της εικόνας προορισμού είναι διαφορετικοί, οπότε για να την λύσουμε, θα καταστήσουμε την εικόνα με αναλογία διαστάσεων πηγής βάσει της διάστασης προορισμού.

const πηγήAspectRatio = source.width / source.height;
const destAspectRatio = destination.width / destination.height;
αν (aspectRatio - destAspectRatio> 0) {
  // Εικόνα τοπίου
  const newWidth = aspectRatio * destination.height;
  άνοιγμαScale = source.width / newWidth;
} else {
  // Εικόνα πορτρέτου
  const newHeight = destination.width / aspectRatio;
  άνοιγμαScale = source.height / newHeight;
}}

Τώρα που η κλίμακα είναι σωστή, πρέπει να βρούμε τη νέα θέση με βάση την εικόνα προορισμού. Αυτό μπορεί να υπολογιστεί από τη θέση προορισμού μείον το ήμισυ της διαφοράς μεταξύ της παλαιάς διάστασης και της νέας διάστασης. Η οποία θα ισοδυναμεί με:

αν (aspectRatio - destAspectRatio> 0) {
  // Εικόνα τοπίου
  destination.pageX - = (newWidth - destinationWidth) / 2.
} else {
  // Εικόνα πορτρέτου
  destination.pageY - = (newHeight - destinationHeight) / 2;
}}

Αυτό είναι τέλειο! Τώρα έχουμε τη σωστή διάσταση και θέση για την εικόνα μετάβασης.

Τώρα πρέπει να υπολογίσουμε τη θέση μετάφρασης από την οποία θα ζωντανέψει η εικόνα. Θα μεγεθύνουμε την εικόνα από το κέντρο, οπότε πρέπει να εφαρμόσουμε το μεταφρασμένο μας, δεδομένου ότι μετακινούμε ακριβώς το κέντρο της φωτογραφίας. Έτσι θα κάνουμε απλά σχετικά σχετικά μαθηματικά, παίρνοντας τη θέση πηγής και το ήμισυ της διάστασης της πηγής. Αυτό θα ισοδυναμούσε με αυτό:

const translateInitX = πηγή.σελίδαX + source.width / 2;
const translateInitY = source.pageY + source.height / 2;
const translateDestX = προορισμός.σελίδαX + destination.width / 2;
const translateDestY = προορισμός.σελίδαY + destination.height / 2;

Τώρα μπορούμε να υπολογίσουμε τη θέση μετάφρασης με τη διαφορά μεταξύ του κέντρου της εικόνας πηγής και της εικόνας προορισμού

const άνοιγμαItTranslateX = translateInitX - translateDestX;
const άνοιγμαItTranslateY = μεταφράζει - μεταφράζειDestY;

Με αυτήν την εύρη αρχής που βρέθηκε και μεταφράζουμε τις τιμές, μπορούμε να ζωντανέψουμε χρησιμοποιώντας το Animated api.

Αυτό είναι. Έχουμε τώρα τη μετάβαση εργασίας. Τώρα μπορούμε να χρησιμοποιήσουμε το useNativeDriver αφού κινούμαστε τώρα μόνο σε ιδιότητες που δεν έχουν σχεδιαστεί.

Βήμα 4: Απόκρυψη της εικόνας προέλευσης και προορισμού κατά τη μετάβαση

Στο προηγούμενο gif, είδαμε ότι κατά τη μετάβαση, η εικόνα που πατήθηκε ήταν ακόμα στην ίδια θέση και η εικόνα προορισμού εμφανίστηκε πριν ολοκληρωθεί η μετάβαση.

Αφήνει να αποκρύπτεται η εικόνα προέλευσης και προορισμού κατά τη διάρκεια της μετάβασης, έτσι ώστε να φαίνεται ότι η εικόνα που έχει πατηθεί είναι η εικόνα που εμφανίζεται στην οθόνη λεπτομερειών.

Ας δούμε τώρα την έξοδο.

Βήμα 5: Χειριστείτε το κουμπί πίσω

Κατά τη μετάβαση στην οθόνη λεπτομερειών χρησιμοποιώντας το Animated.timing (), αλλάζουμε το AnimatedValue από το 0 στο 1. Έτσι, όταν πατηθεί το κουμπί πίσω, πρέπει απλώς να αλλάξουμε το AnimatedValue από 1 σε o.

Αυτό είναι. Μπορείτε να ελέγξετε τον κώδικα στο Github και να δοκιμάσετε το demo στο Expo.

Επίσης ελέγξτε την εκπομπή του Eric Vicenti για τη μετάβαση σε κοινόχρηστο στοιχείο.

Σας ευχαριστώ για το χρόνο και την ανάγνωση αυτής της θέσης. Αν βρήκατε αυτό το χρήσιμο, παρακαλώ χτυπάτε και μοιραστείτε το. Μπορείτε να επικοινωνήσετε μαζί μου στο Twitter @narendra_shetty.