İçeriğe geç
academia.sh

Ders 15 / 17

Animasyonlar

Bir animasyonun iki hareket taşımasının ölçümü: hedef değer durumun sahibinden aşağı gider, kare başına ara değer ise ağacın dışındaki kare saatinden gelir ve düğüme dış bir hareketle ulaşır.

Yayın Güncelleme

Bu derste

  • Animasyonun hedef değerinin durumun sahibinden aşağı gittiğini ölçer
  • Kare başına ara değerin ağaç dışındaki saatten geldiğini gösterir
  • Bir animasyonun neden iki ayrı hareket taşıdığını açıklar
  • Doğrusal ara değer hesabını modelin hesabı olarak ayırt eder
İçindekiler

Düğme basıldığında bir panelin genişliği 0’dan 100’e çıkıyor olsun. Durum tek adımda değişir: genişlik 100 olmalıdır. Ekranda ise değişim bir dizi karede görünür ve her karede genişlik bir öncekinden biraz farklıdır. Bu ara değerleri üreten şey durumun sahibi değildir, çünkü sahibi yalnız hedefi bilir.

Bu ders, animasyonun taşıdığı iki veriyi ayırır. Biri hedeftir ve ağaçtaki bir atadan gelir. Öteki kare başına değerdir ve ağacın dışındaki bir saatten gelir. Aynı animasyon, aynı düğüme iki farklı yönden ulaşır.

// Direction.kt — bir verinin arayuz agacinda hangi yone gittigini siniflandirir
class Tree(private val parentOf: Map<String, String>) {
    fun contains(node: String): Boolean = node in parentOf.keys || node in parentOf.values
    fun parent(node: String): String? = parentOf[node]
    fun ancestors(node: String): List<String> {
        val chain = mutableListOf<String>()
        var current = parentOf[node]
        while (current != null) {
            chain.add(current)
            current = parentOf[current]
        }
        return chain
    }
}

class Move(val label: String, val from: String, val to: String, tree: Tree) {
    val direction: String = when {
        !tree.contains(from) || !tree.contains(to) -> "dis"
        to in tree.ancestors(from) -> "yukari"
        tree.parent(to) == from -> "asagi"
        from in tree.ancestors(to) -> "derin"
        else -> error("akrabasiz: $from ve $to")
    }

    fun describe() {
        println("$label: kimden=$from | kime=$to -> $direction")
    }
}

fun countDirections(moves: List<Move>) {
    for (name in listOf("asagi", "yukari", "derin", "dis")) {
        println("$name: ${moves.count { it.direction == name }}")
    }
}

Hedef ve Ara Değer

Durum tabanlı animasyonlar bir hedef değer alır ve geçerli değeri hedefe doğru yürütür. Hedef bir durumdur ve durumun sahibi ağaçtaki bir bileşen işlevidir. Platformun kare saati ise ağaç dışında çalışır: ekran her yenilendiğinde saat bir kare ilerler ve animasyon o karedeki değeri hesaplar.

// Platform kodu: hedefe dogru animasyon
@Composable
fun ExpandingPanel(expanded: Boolean) {
    val width by animateDpAsState(
        targetValue = if (expanded) 100.dp else 0.dp,
        label = "panelWidth",
    )
    Box(Modifier.width(width))
}

expanded, ExpandingPanel’e dışarıdan verilen durumdur ve hedefi belirler. animateDpAsState, o hedefe giden bir ara değer üretir. Box her karede bu değerle yeniden ölçülür. Hedef değişince animasyon yeniden başlar; kare saati devam eder.

Model, platformun bu kuralını kurar; model platformun kendisi değildir. Yardımcı dosya iki şey yapar: kare saatinin ürettiği ara değerleri doğrusal olarak hesaplar ve animasyonlu bir paneli ekranın altına yerleştirir. Doğrusal ara değer hesabı modelin hesabıdır; platformun yumuşatma eğrisi ve süre ayarı burada yoktur. Ölçümlerin uçlarını da iki işlev verir: targetOwner panelin ebeveynini ağaçtan okur, clockSource kare saatinin adını döndürür. Bu derste hareketin bir ucu ağaçtan bulunur (targetOwner); öteki uç, modelin adlandırdığı ve ağaçta olmayan bir kaynaktır (clockSource sabit bir ad döndürür).

// Frames.kt — kare saatinin urettigi ara degerler (modelin hesabi)
fun frameValues(from: Int, to: Int, steps: Int): List<Int> =
    (0..steps).map { from + (to - from) * it / steps }

fun animatedTree(): Tree = Tree(mapOf("Screen" to "App", "Panel" to "Screen"))

// hedefin sahibi: panelin ebeveyni, agactan okunur
fun targetOwner(tree: Tree): String = tree.parent("Panel") ?: error("panel kokte")

// ara degerleri ureten kaynak: agacin disindaki kare saati
fun clockSource(): String = "FrameClock"

Hedef Aşağı Gider

İlk ölçüm hedefi izler. Screen, panelin açık ya da kapalı olduğunu bilir ve hedef genişliği ona verir.

// Main.kt — animasyonun hedef degeri
fun main() {
    val tree = animatedTree()
    println("hedef: ${frameValues(0, 100, 2).last()}")
    Move("hedef", targetOwner(tree), "Panel", tree).describe()
}
hedef: 100
hedef: kimden=Screen | kime=Panel -> asagi

Hedef Screen’den Panel’e aşağı gitti. Bu, önceki derslerdeki durum akışıyla aynı harekettir: sahibi atadadır, tüketici çocuktur. Animasyon varlığı hedefin yönünü değiştirmez.

AD1. Model hedefi tek bir tamsayı olarak taşır. Hedefin bir renk, bir uzaklık ya da bir saydamlık olması, ve animasyonun her türde ayrı bir işlevle yazılması modelde yoktur.

Ara Değer Dışarıdan Gelir

İkinci ölçüm kare başına değerleri izler. Panel her karede 0 ile 100 arasında yeni bir genişlik alır. Bu değerleri üreten kare saati ağaçta bir düğüm değildir.

// Main.kt — kare saatinden gelen ara degerler
fun main() {
    val tree = animatedTree()
    val frames = frameValues(0, 100, 2)
    println("kareler: ${frames.joinToString(",")}")
    val moves = frames.map { Move("kare-$it", clockSource(), "Panel", tree) }
    for (move in moves) {
        move.describe()
    }
    countDirections(moves)
}
kareler: 0,50,100
kare-0: kimden=FrameClock | kime=Panel -> dis
kare-50: kimden=FrameClock | kime=Panel -> dis
kare-100: kimden=FrameClock | kime=Panel -> dis
asagi: 0
yukari: 0
derin: 0
dis: 3

Üç karenin üçü de dış. Saat ağaçta olmadığı için hareketin bir ucu ağaç dışıdır; hedef değerin aksine ara değer, ağaçtaki hiçbir atadan gelmez. Panel her kareyi bir ata aracılığıyla değil, doğrudan saatten alır.

AD2. Model üç kare üretir ve araları eşit böler. Platformun karelerinin sayısı ve süresi, yavaşlayıp hızlanan eğrileri ve animasyon sürerken hedefin değişmesi modelde ölçülmedi.

Ara Değeri Kim Tutar

Ara değerin dış olması bir tasarım sonucudur. Ara değer her karede değişir; sahibi bir ata olsaydı, her kare o atanın altındaki her şeyin yeniden hesaplanmasına yol açardı. Bunun yerine ara değeri okuyan düğüm, değeri doğrudan saatten izler ve yalnız kendisi yeniden çizilir. Hedef ise nadiren değişir ve değiştiğinde ağaçtan inmesi doğaldır. Hız farkı iki verinin farklı yollardan gelmesinin gerekçesidir; bu derste hız ölçülmez, yalnız yolun yönü ölçülür.

Sınırlayıcı Ölçüm

Bir animasyonun iki hareket taşıması, dersin ana bulgusudur. Üçüncü bir hareket de vardır: animasyon bittiğinde ekrana bildirim gider. Aşağıdaki ölçüm üçünü bir arada gösterir.

// Main.kt — bir animasyonun tasidigi hareketler
fun main() {
    val tree = animatedTree()
    val frames = frameValues(0, 100, 2)
    val moves = listOf(
        Move("hedef", targetOwner(tree), "Panel", tree),
        Move("kare-${frames[1]}", clockSource(), "Panel", tree),
        Move("bitis", "Panel", targetOwner(tree), tree),
    )
    for (move in moves) {
        move.describe()
    }
    countDirections(moves)
}
hedef: kimden=Screen | kime=Panel -> asagi
kare-50: kimden=FrameClock | kime=Panel -> dis
bitis: kimden=Panel | kime=Screen -> yukari
asagi: 1
yukari: 1
derin: 0
dis: 1

Bir aşağı, bir yukarı, bir dış. Hedef ağaç içi bir hareket, ara değer ağaç dışı bir hareket, bitiş bildirimi ise yine ağaç içi ama ters yönde bir harekettir. Dersin yönü animasyona özgü olan harekettir: ara değer. Hedef ve bitiş bildirimi, animasyonun olmadığı bir ekranda da aynı biçimde bulunurdu. Ara değer yalnız animasyonda vardır, ve her zaman ağacın dışından gelir. Dersin yönü: dış.

Görünürlük animasyonu ve geçiş nesnesi aynı yapıdadır. Görünürlük animasyonu, bir ögenin ağaca girişini ve çıkışını animasyonla sarar; geçiş nesnesi (updateTransition), tek bir hedef durumdan birden çok değer türetir. İkisinde de hedef ağaçtan gelir, kare başına değerler saatten.

AD3. Model bitiş bildirimini tek bir hareketle kurar. Bildirimin hangi karede tetiklendiği ve animasyon kesildiğinde ne olduğu modelde ölçülmedi. Görünürlük animasyonu ve geçiş nesnesi bu derste yalnız kaynak parçasıyla anlatılır, ölçülmez.

Özet

  • Bu dersin yönü: dış. Ara değeri üreten kare saati ağaçta olmadığı için dersin ölçtüğü hareket ağaç dışından gelir.
  • Animasyon iki veri taşır: hedef değer ve kare başına ara değer.
  • Hedef durumun sahibinden gelir ve çocuğa aşağı gider.
  • Ara değeri kare saati üretir; saat ağaçta olmadığı için hareket dıştır.
  • Animasyon bitince ekrana giden bildirim yukarı gider.

Sonraki Adım

Animasyon, ekranı herkese aynı değişimle gösterir. Ekran okuyucu kullanan biri için ise ekran, ağaçtan türetilen ayrı bir anlam ağacıdır. Sonraki ders, bu anlam ağacında çocukların etiketlerinin ataya nasıl birleştiğini ve bunun yönünü ölçer.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ ↵ Esc gezin · aç · kapat