Əsas məqamlar
Hero Animation — ümumi elementin mənbə ekrandan hədəf ekrana rəvan hərəkət edərək öz şəxsiyyətini qoruduğu interfeys animasiya texnikası. Ad „qəhrəman" metaforasını əks etdirir — əsas element səhnə keçidi vasitəsilə ekranlar arasında hərəkət edir, istifadəçinin diqqətini cəlb edir. Standart keçidlərdən fərqli olaraq, Hero Animation vizual əlaqəni qoparmır: istifadəçi elementin fasiləsiz traektoriyasını görür, bu da naviqasiya iyerarxiyasını anlamağı yaxşılaşdırır.
Hero Animation konsepsiyası Material Design-da siyahı-təfərrüat (list-detail) ekranları, qalereyalar və kartlar üçün tövsiyə olaraq təsbit edilmişdir. Animasiya alqoritmi üç mərhələdən ibarətdir: mənbə ekranda element vurğulanır və mütləq vəziyyətdə sabitlənir; keçid zamanı o, hədəf konteynerə uyğun olaraq miqyaslanır, hərəkət edir və formasını dəyişir; son ekranda element yeni tərtibata inteqrasiya olunur. Material Design 3 məlumatına görə, düzgün tətbiq edilmiş qəhrəman animasiyası səhv „geri" kliklərinin sayını 22% azaldır.
Flutter-da Hero Animation Hero vidjeti vasitəsilə həyata keçirilir, o məcburi tag parametrini qəbul edir — unikal identifikator. Flutter mənbə və hədəf marşrutlarında eyni teqə malik Hero cütünü avtomatik tapır və animasiyanı işə salır. Hero vidjeti əl ilə animasiya idarəetməsi tələb etmir — framework FlightShuttle daxili obyekti vasitəsilə traektoriyanı özü hesablayır və transformasiya tətbiq edir.
FlightShuttle — keçid anında Hero vidjetinin örtüklü surətini yaradan, onu mütləq koordinat sistemində yerləşdirən və başlanğıc mövqedən son mövqeyə qədər transformasiyanı (scale, translate, rotate) animasiya edən görünməz „məki"). FlightShuttle-a flightShuttleBuilder xassəsi vasitəsilə fərdi əyrilər ötürülə bilər ki, bu da uçuş traektoriyasını idarə etməyə imkan verir: qövslə uçmaq, sərhədlərdən əks olunmaq və ya gəlməzdən əvvəl yavaşlamaq. Flutter 3.16+-da Hero clipRect və borderRadius-u qoruyaraq animasiyanı dəstəkləyir, bu da düzbucaqlı ekranlara çevrilən yuvarlaq kartlar effekti verir.
SwiftUI-da Hero Animation-un ekvivalenti matchedGeometryEffect modifikatorudur, iOS 14-də təqdim edilmişdir. O, iki elementi identifikator (id) və ad sahəsi (namespace) vasitəsilə birləşdirir. Flutter-də Hero avtomatik olaraq keçidi ələ keçirdiyi halda, SwiftUI namespace-in açıq şəkildə bəyan edilməsini və modifikatorun səhnənin hər iki hissəsinə — mənbə və hədəf — eyni iyerarxiya daxilində və ya GeometryReader vasitəsilə tətbiq edilməsini tələb edir.
matchedGeometryEffect animasiyası mövqe, ölçü, fırlanma və künc radiusu (cornerRadius) transformasiyasını dəstəkləyir. Hamarlıq üçün withAnimation-da Animation təyin edilir. SwiftUI default easeInOut əyrisi əsasında aralıq vəziyyətləri avtomatik interpolyasiya edir. iOS 17+-də TabView və NavigationStack üçün matchedTransitionSource dəstəyi əlavə edilmişdir — standart keçidlər üçün əvvəlcədən təyin edilmiş traektoriyaya malik sistem analoqu.
Android-də Hero Animation Shared Element Transition adlanır və ActivityOptions.makeSceneTransitionAnimation və ya FragmentTransaction.addSharedElement vasitəsilə həyata keçirilir. Hər bir ümumi element XML-də transitionName atributu və ya kod vasitəsilə işarələnir, Android isə aktivitələr arasında animasiyaya cavabdehdir. Android 5.0-dan (API 21) etibarən keçidlər framework-un bir hissəsi oldu; bundan əvvəl ValueAnimator vasitəsilə fərdi animasiyalar yazmaq lazım idi.
Jetpack Compose-da Shared Element Transition Modifier.sharedElement və Modifier.sharedBounds modifikatorları vasitəsilə AnimatedContent və ya SharedTransitionLayout ilə birlikdə həyata keçirilir (Compose BOM 2024.01-dən etibarən mövcuddur). View sistemindən fərqli olaraq, Compose elementləri tənbəl şəkildə yenidən çəkir, ona görə də ümumi element CompositionLocal-da keşlənir. Compose həmçinin scrollOffset-u qoruyaraq siyahıdan təfərrüata (list→detail) animasiyanı dəstəkləyir — Hero keçidləri üçün əsas imkan. Google I/O 2024 məlumatına görə, Compose Shared Element Transition RenderNode-un birbaşa idarə edilməsi sayəsində View versiyasından 15% daha sürətli işləyir.
Flutter-da Hero Animation-un əsas nümunəsi: siyahı ekranında miniatür və təfərrüat ekranında tam ekran şəkli. image-hero teqi iki vidjeti birləşdirir, FlightShuttle isə kartdan tam ekrana keçidi avtomatik animasiya edir.
import 'package:flutter/material.dart';
class ListScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: ListView(
children: [
Hero(
tag: 'image-hero',
child: GestureDetector(
onTap: () {
Navigator.push(context,
MaterialPageRoute(
builder: (_) => DetailScreen(),
),
);
},
child: ClipRRect(
borderRadius: BorderRadius.circular(12),
child: Image.network(
'https://picsum.photos/200',
width: 100, height: 100,
fit: BoxFit.cover,
),
),
),
),
],
),
);
}
}
class DetailScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Detail')),
body: Center(
child: Hero(
tag: 'image-hero',
child: Image.network(
'https://picsum.photos/800',
width: 300, height: 400,
fit: BoxFit.cover,
),
),
),
);
}
}
Əsas məqam — hər iki ekranda eyni teq (image-hero). Flutter marşrut keçidi zamanı Hero vidjetlərini teq üzrə uyğunlaşdırır. Teqlər fərqlidirsə, animasiya işə düşməyəcək — element sadəcə yeni ekranda keçidsiz görünəcək. Teq üçün istənilən tipdən istifadə etmək olar, lakin sətirlər ən etibarlı seçimdir.
SwiftUI-da NavigationStack ilə Hero Animation nümunəsi. matchedGeometryEffect modifikatoru siyahıdakı və təfərrüat ekranındakı şəkli namespace vasitəsilə birləşdirir, withAnimation isə rəvan keçidi işə salır.
import SwiftUI
struct HeroExampleView: View {
@Namespace private var heroNamespace
@State private var isDetailShown = false
var body: some View {
VStack {
if isDetailShown {
DetailView(namespace: heroNamespace)
.transition(.identity)
} else {
ListView(namespace: heroNamespace)
.transition(.identity)
}
}
.animation(Animation.easeInOut(duration: 0.4),
value: isDetailShown)
}
}
struct ListView: View {
let namespace: Namespace.ID
var body: some View {
Image("thumbnail")
.resizable()
.frame(width: 100, height: 100)
.cornerRadius(12)
.matchedGeometryEffect(id: "hero", in: namespace)
}
}
struct DetailView: View {
let namespace: Namespace.ID
var body: some View {
Image("thumbnail")
.resizable()
.frame(width: 300, height: 400)
.cornerRadius(0)
.matchedGeometryEffect(id: "hero", in: namespace)
}
}
@Namespace matchedGeometryEffect identifikatoru üçün ad sahəsi yaradır. Hər iki element eyni id (hero) və ümumi namespace istifadə edir. isDetailShown dəyişdirildikdə, SwiftUI iki görünüş arasında ölçü, mövqe və cornerRadius transformasiyasını animasiya edərək qəhrəman effekti yaradır. iOS 16+ ilə standart NavigationLink üçün navigationTransition(.hero) istifadə edin.
SharedTransitionLayout ilə Jetpack Compose-da Shared Element Transition nümunəsi. sharedElement modifikatoru naviqasiya zamanı ümumi elementi ekranlar arasında animasiya edir.
@OptIn(ExperimentalSharedTransitionApi::class)
@Composable
fun HeroExample() {
SharedTransitionLayout {
var isDetail by remember { mutableStateOf(false) }
AnimatedContent(
targetState = isDetail,
transitionSpec = {
fadeIn() togetherWith fadeOut()
}
) { detail ->
if (detail) {
Box(
modifier = Modifier
.fillMaxSize()
.sharedElement(
state = rememberSharedContentState(key = "hero"),
animatedVisibilityScope = this
)
) {
AsyncImage(
model = "https://picsum.photos/800",
contentDescription = null,
modifier = Modifier.fillMaxSize()
)
}
} else {
Box(
modifier = Modifier
.size(100.dp)
.sharedElement(
state = rememberSharedContentState(key = "hero"),
animatedVisibilityScope = this
)
) {
AsyncImage(
model = "https://picsum.photos/200",
contentDescription = null,
modifier = Modifier.fillMaxSize()
)
}
}
}
}
}
Compose SharedTransitionLayout (Compose BOM 2024.01+) iyerarxiyanı əhatə edir, hero açarı ilə sharedElement modifikatoru isə başlanğıc və son elementləri birləşdirir. AnimatedContent vəziyyəti dəyişir və Compose avtomatik olaraq şəklin ölçüsünü, mövqeyini və formasını animasiya edir. Əsas üstünlük — keçid zamanı kartın yuvarlaqlaşdırılması effektini verən bounds və clipBounds animasiyasını dəstəkləməsidir.
Tez-tez verilən suallar
Bəli, Hero Animation hər iki platformada mövcuddur. iOS-da SwiftUI-də matchedGeometryEffect və ya UINavigationController.transitioningDelegate istifadə olunur. Android-də — ActivityOptions.makeSceneTransitionAnimation keçidi və ya Jetpack Compose-da Modifier.sharedElement ilə Shared Element Transition. Flutter Hero Animation-u hər iki platformada eyni şəkildə işləyən Hero vidjeti vasitəsilə həyata keçirir.
Bəli, Hero Animation çoxsaylı ümumi elementləri dəstəkləyir. Flutter-da fərqli teqlərlə bir neçə Hero təyin edin. SwiftUI-da hər elementdə fərqli identifikatorlarla matchedGeometryEffect istifadə edin. Bütün animasiyalar paralel olaraq yerinə yetirilir — hər bir element öz traektoriyası ilə hərəkət edir.
Performans tətbiqdən asılıdır. Flutter FlightShuttle şəkilləri RenderObject vasitəsilə emal edir, bu da 60fps təmin edir. SwiftUI matchedGeometryEffect vektorlaşdırılmışdır. Böyük şəkillər üçün Flutter-da precacheImage və ya SwiftUI-da prefetch istifadə edin — bu, ilk keçiddə gecikmələri aradan qaldırır. Android Compose AsyncImage vasitəsilə gec yükləmə tələb edir.
Standart push keçidi bütün ekranı hərəkət etdirərək kart yığını illüziyası yaradır. Hero Animation vizual diqqəti bir elementdə cəmləşdirir — istifadəçi onun fasiləsiz hərəkətini görür, bu da ekranlar arasındakı əlaqəni anlamağı yaxşılaşdırır. Push iyerarxik naviqasiya üçün yaxşıdır, Hero — məzmunu vurğulamaq üçün.
Xeyr, Hero Animation daxili vasitələrlə həyata keçirilir. Flutter-da bu, flutter/material.dart paketindən Hero vidjetidir. SwiftUI-da — matchedGeometryEffect modifikatoru. Android Jetpack Compose-da — standart Compose-dan SharedTransitionLayout və Modifier.sharedElement. Əlavə kitabxanalar yalnız qeyri-standart traektoriyalar və ya mürəkkəb transformasiya üçün tələb olunur.
Nəticə
Açar təslim mobil tətbiq hazırlayacağıq
IT Sectr 2017-ci ildən startaplar və bizneslər üçün iOS və Android tətbiqləri yaradır. Sizə məsləhət verəcəyik və ən yaxşı həlli təklif edəcəyik.
Həm də oxuyun