Παίζοντας με διαδρομές

Πρόσφατα βοήθησα με ένα κινούμενο ήρωα σε μια εφαρμογή - δυστυχώς δεν μπορώ να μοιραστώ αυτό το animation ακόμα ... αλλά ήθελα να μοιραστώ αυτό που έμαθα κάνοντας. Σε αυτή τη θέση θα περάσω μέσα από την αναδημιουργία αυτής της συναρπαστικής κινούμενης εικόνας από το Dave 'beesandbombs' Whyte που δείχνει πολλές από τις ίδιες τεχνικές:

Πολύγωνο Laps από beesandbombs

Η πρώτη μου σκέψη όταν εξετάζω αυτό το θέμα (που ίσως να μην είναι μεγάλη έκπληξη σε όποιον γνωρίζει τη δουλειά μου) ήταν να φτάσει σε ένα AnimatedVectorDrawable (AVD στο εξής). Τα AVDs είναι υπέροχα, αλλά δεν είναι κατάλληλα για κάθε περίπτωση - ειδικά ότι είχαμε τις ακόλουθες απαιτήσεις:

  • Ήξερα ότι θα χρειαζόταν να σχεδιάσουμε ένα πολύγωνο, αλλά δεν είχαμε εγκαταστήσει το ακριβές σχήμα. Τα AVD είναι κινούμενα σχέδια "pre-baked", τα οποία ποικίλουν ανάλογα με το σχήμα και θα απαιτήσουν εκ νέου την κίνηση.
  • Μέρος της πτυχής "παρακολούθηση της προόδου", θα θέλαμε να σχεδιάσουμε μόνο ένα τμήμα του πολυγώνου. Τα αρχεία AVD είναι «πυρκαγιά και ξεχάστε» δηλ. Δεν μπορείτε να τα καθαρίσετε.
  • Θέλαμε να μετακινήσουμε άλλο αντικείμενο γύρω από το πολύγωνο. Αυτό είναι σίγουρα εφικτό με AVDs ... αλλά πάλι θα απαιτούσε πολλή προκαταρκτική εργασία για να προ-υπολογιστεί η σύνθεση.
  • Θέλαμε να ελέγξουμε την πρόοδο του αντικειμένου που κινείται γύρω από το πολύγωνο ξεχωριστά από το τμήμα του πολυγώνου που εμφανίζεται.

Αντ 'αυτού, επέλεξα να το εφαρμόσω ως προσαρμοσμένο Drawable, αποτελούμενο από αντικείμενα Path. Τα μονοπάτια αποτελούν μια θεμελιώδη αναπαράσταση ενός σχήματος (το οποίο χρησιμοποιούνται από την AVD κάτω από την κουκούλα!) Και τα API Canvas του Android προσφέρουν πολύ πλούσια υποστήριξη για τη δημιουργία ενδιαφέρουσων εφέ μαζί τους. Πριν περάσω από κάποιες από αυτές, θέλω να κάνω μια φωνή έξω σε αυτή την εξαιρετική θέση του Romain Guy, η οποία δείχνει πολλές από τις τεχνικές στις οποίες βασίζομαι σε αυτό το post:

Πολικός συντονισμός

Συνήθως, όταν καθορίζουμε 2δ σχήματα, δουλεύουμε σε συντεταγμένες (x, y) γνωστές ως καρτεσιανές συντεταγμένες. Ορίζουν τα σχήματα καθορίζοντας τα σημεία από την απόσταση τους από την προέλευση κατά μήκος των αξόνων x και y. Μια εναλλακτική λύση είναι το σύστημα πολικών συντεταγμένων που ορίζει τα σημεία με γωνία (θ) και ακτίνα (r) από την προέλευση.

Καρτεσιανές συντεταγμένες (αριστερά) έναντι πολικών συντεταγμένων (δεξιά)

Μπορούμε να μετατρέψουμε μεταξύ πολικών και καρτεσιανών συντεταγμένων με αυτόν τον τύπο:

val x = ακτίνα * Math.cos (γωνία);
val y = ακτίνα * Math.sin (γωνία);

Συνιστώ ανεπιφύλακτα αυτή τη θέση για να μάθετε περισσότερα σχετικά με τις πολικές συντεταγμένες:

Για να δημιουργηθούν κανονικά πολύγωνα (δηλαδή όπου κάθε εσωτερική γωνία είναι ίδια), οι πολικές συντεταγμένες είναι εξαιρετικά χρήσιμες. Μπορείτε να υπολογίσετε τη γωνία που απαιτείται για να παράγετε τον επιθυμητό αριθμό πλευρών (ως εσωτερικές γωνίες συνολικού 360º) και στη συνέχεια να χρησιμοποιήσετε πολλαπλάσια αυτής της γωνίας με την ίδια ακτίνα για να περιγράψετε κάθε σημείο. Στη συνέχεια μπορείτε να μετατρέψετε αυτά τα σημεία σε καρτεσιανές συντεταγμένες που λειτουργούν τα API γραφικών. Εδώ μπορείτε να δημιουργήσετε μια διαδρομή που περιγράφει ένα πολύγωνο με δεδομένο αριθμό πλευρών και ακτίνας:

fun createPath (πλευρές: Int, ακτίνα: Float): Διαδρομή {
  κυλιόμενη διαδρομή = Διαδρομή ()
  γωνία γωνίας = 2,0 * Math.PI / πλευρές
  path.moveTo (
      cx + (ακτίνα * Math.cos (0.0)) toFloat (),
      cy + (ακτίνα * Math.sin (0.0)) toFloat ())
  για (i σε 1 έως τις πλευρές) {
    path.lineTo (
        cx + (ακτίνα * Math.cos (γωνία * i)) toFloat (),
        cy + (ακτίνα * Math.sin (γωνία * i)) toFloat ())
    }}
  path.close ()
  μονοπάτι επιστροφής
}}

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

ιδιωτικά val polygons = listOf (
  Πολύγωνο (πλευρές = 3, ακτίνα = 45f, χρώμα = 0xffe84c65.toInt ()),
  Πολύγωνο (πλευρές = 4, ακτίνα = 53f, χρώμα = 0xffe79442.toInt ()),
  Πολύγωνο (πλευρές = 5, ακτίνα = 64f, χρώμα = 0xffefefbb.toInt ()),
  ...
)

Αποτελεσματική ζωγραφική διαδρομής

Η σχεδίαση ενός μονοπατιού είναι απλή με τη χρήση του Canvas.drawPath (διαδρομή, χρώμα), αλλά η παράμετρος Paint υποστηρίζει ένα PathEffect που μπορούμε να χρησιμοποιήσουμε για να αλλάξουμε τον τρόπο με τον οποίο θα σχεδιαστεί η διαδρομή. Για παράδειγμα, μπορούμε να χρησιμοποιήσουμε ένα CornerPathEffect για να στρογγυλοποιήσουμε τις γωνίες του πολυγώνου μας ή ένα DashPathEffect για να σχεδιάσουμε μόνο ένα τμήμα της διαδρομής (δείτε την ενότητα 'Παρακολούθηση διαδρομής' της παραπάνω θέσης για περισσότερες λεπτομέρειες σχετικά με αυτήν την τεχνική):

Μια εναλλακτική τεχνική για την κατάρτιση ενός υποτομέα μιας διαδρομής είναι η χρήση του PathMeasure # getSegment το οποίο αντιγράφει ένα τμήμα σε ένα νέο αντικείμενο Path. Χρησιμοποίησα την τεχνική παύλα καθώς η κίνηση των παραμέτρων διαστήματος και φάσης επέτρεψε ενδιαφέρουσες δυνατότητες.

Με την έκθεση των παραμέτρων που ελέγχουν αυτές τις επιδράσεις ως ιδιότητες του εφελκυσμού μας, μπορούμε εύκολα να τις ζωντανέψουμε:

αντικείμενο PROGRESS: FloatProperty  ("πρόοδος") {
  αντικαταστήστε τη διασκέδαση setValue (pld: PolygonLapsDrawable, progress: Float) {
    pld.progress = πρόοδος
  }}
  αντικατάσταση της διασκέδασης get (pld: PolygonLapsDrawable) = pld.progress
}}
...
ObjectAnimator.ofFloat (polygonLaps, PROGRESS, 0f, 1f) .apply {
  διάρκεια = 4000L
  interpolator = LinearInterpolator ()
  repeatCount = INFINITE
  repeatMode = RESTART
}.αρχή()

Για παράδειγμα, υπάρχουν διάφοροι τρόποι για την κίνηση της εξέλιξης των ομόκεντρων μονοπατιών πολυγώνου:

Προσέξτε στο μονοπάτι

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

Το PathDashPathEffect δέχεται παραμέτρους προόδου και φάσης - αυτό είναι το χάσμα μεταξύ των σφραγίδων και το πόσο μακριά να μετακινηθείτε κατά μήκος της διαδρομής πριν από την πρώτη σφραγίδα. Καθορίζοντας την πρόοδο στο μήκος ολόκληρης της διαδρομής (που λαμβάνεται μέσω του PathMeasure # getLength), μπορούμε να σχεδιάσουμε μια ενιαία σφραγίδα. Με την κίνηση της φάσης (εδώ ελέγχεται από μια παράμετρο dotProgress [0, 1]) μπορούμε να κάνουμε αυτή τη μοναδική σφραγίδα να κινείται κατά μήκος της διαδρομής.

φάση val = dotProgress * polygon.length
dotPaint.pathEffect = PathDashPathEffect (pathDot, polygon.length,
    φάση, TRANSLATE)
canvas.drawPath (polygon.path, dotPaint)

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

Μια επαναδημιουργία του αρχικού gif ως ανανεώσιμο Android

Μπορείτε να βρείτε την πηγή για αυτό το σχετικό εδώ:

https://gist.github.com/nickbutcher/b41da75b8b1fc115171af86c63796c5b#file-polygonlapsdrawable-kt

Εμφάνιση στυλ

Ο αετός που είναι ελεγμένος μεταξύ σας μπορεί να έχει παρατηρήσει την τελική παράμετρο στο PathDashPathEffect: Style. Αυτό το έντυπο ελέγχει τον τρόπο μεταμόρφωσης της σφραγίδας σε κάθε θέση που έχει σχεδιαστεί. Για να δείξει πώς λειτουργεί αυτή η παράμετρος, το παρακάτω παράδειγμα χρησιμοποιεί μια τριγωνική σφραγίδα αντί για έναν κύκλο και εμφανίζει τόσο τα στυλ μετάφρασης και περιστροφής:

Σύγκριση μεταφράσεων στυλ (αριστερά) με περιστροφή (δεξιά)

Παρατηρήστε ότι όταν χρησιμοποιείτε τη μετάφραση, η σφραγίδα του τριγώνου είναι πάντα στον ίδιο προσανατολισμό (προς τα αριστερά) ενώ με το στυλ περιστροφής τα τρίγωνα περιστρέφονται για να παραμείνουν εφαπτόμενα στη διαδρομή.

Υπάρχει ένα τελικό στυλ που ονομάζεται morph που μετατρέπει στην πραγματικότητα τη σφραγίδα. Για να δείξουμε αυτήν τη συμπεριφορά, έχω αλλάξει τη σφραγίδα σε μια γραμμή παρακάτω. Παρατηρήστε πώς λυγίζουν οι γραμμές κατά τη διασταύρωση των γωνιών:

Παρουσίαση του PathDashPathEffect.Style.MORPH

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

Σημειώστε ότι μπορείτε να συνδυάσετε PathEffects χρησιμοποιώντας ένα ComposePathEffect, έτσι ακολουθεί η σφραγίδα διαδρομής τις στρογγυλεμένες γωνίες, συνθέτοντας ένα PathDashPathEffect με ένα CornerPathEffect.

Πηγαίνοντας σε μια εφαπτομένη

Ενώ τα παραπάνω ήταν όλα που χρειαζόμαστε για να αναδημιουργήσουμε τη σύνθεση γύρων πολύγωνο, η αρχική πρόκληση μου κάλεσε για λίγο περισσότερη δουλειά. Ένα μειονέκτημα με τη χρήση του PathDashPathEffect είναι ότι τα γραμματόσημα μπορούν να έχουν μόνο ένα ενιαίο σχήμα και χρώμα. Η σύνθεση στην οποία εργαζόμουν κάλεσε έναν πιο εξελιγμένο δείκτη, οπότε έπρεπε να κινηθώ πέρα ​​από την τεχνική σφράγισης της διαδρομής. Αντ 'αυτού, χρησιμοποιώ ένα Drawable και υπολογίζω πού πρέπει να σχεδιαστεί κατά μήκος της Διαδρομής για μια δεδομένη πρόοδο.

Μετακίνηση ενός VectorDrawable κατά μήκος μιας διαδρομής

Για να το πετύχω αυτό, χρησιμοποίησα και πάλι την τάξη PathMeasure που προσφέρει μια μέθοδο getPosTan για τη λήψη συντεταγμένων θέσης και εφαπτομένη σε μια δεδομένη απόσταση κατά μήκος ενός μονοπατιού. Με αυτές τις πληροφορίες (και λίγα μαθηματικά), μπορούμε να μεταφράσουμε και να περιστρέψουμε τον καμβά για να τραβήξουμε τον δείκτη μας να είναι σχετικός στη σωστή θέση και προσανατολισμό:

pathMeasure.setPath (polygon.path, false)
pathMeasure.getPosTan (markerProgress * polygon.length, pos, tan)
canvas.translate (pos [0], pos [1])
γωνία val = Math.atan2 (tan [1] .toDouble (), tan [0] .toDouble ())
canvas.rotate (Math.toDegrees (γωνία) .toFloat ())
marker.draw (καμβά)

Βρείτε τη διαδρομή σας

Ας ελπίσουμε ότι αυτή η ανάρτηση έχει καταδείξει πώς μια προσαρμοσμένη σχεδίαση χρησιμοποιώντας τη διαδρομή και τη χειραγώγηση μπορεί να είναι χρήσιμη για την οικοδόμηση ενδιαφέρουσας γραφικών επιδράσεων. Η δημιουργία ενός προσαρμοσμένου σχεδίου σας δίνει τον απόλυτο έλεγχο για να αλλάξετε και να ζωντανέψετε διαφορετικά μέρη της σύνθεσης ανεξάρτητα. Αυτή η προσέγγιση σας επιτρέπει επίσης να προσφέρετε δυναμικά τιμές παρά να χρειάζεται να προετοιμάσετε προ-κονσερβοποιημένα κινούμενα σχέδια. Εντυπωσιάστηκα πολύ με αυτό που μπορείτε να επιτύχετε με τα API Path του Android και τα ενσωματωμένα εφέ, τα οποία είναι διαθέσιμα από το API 1.