Főbb pontok
Hero Animation — egy felületi animációs technika, amelyben egy közös elem finoman mozog a forrásképernyőről a célképernyőre, megőrizve identitását. A név a „hős" metaforáját tükrözi — a kulcselemet, amely áthalad a képernyők közötti színpadi átmeneten, felkeltve a felhasználó figyelmét. A szokásos átmenetekkel ellentétben a Hero Animation nem szakítja meg a vizuális kapcsolatot: a felhasználó látja az elem folyamatos pályáját, ami javítja a navigációs hierarchia megértését.
A Hero Animation koncepciója a Material Design-ban gyökerezik, mint ajánlás lista-részlet (list-detail) képernyőkhöz, galériákhoz és kártyákhoz. Az animációs algoritmus három fázisból áll: a forrásképernyőn az elem kiemelésre kerül és rögzítésre kerül abszolút pozícióban; az átmenet során skálázódik, mozog és alakot vált a célkonténernek megfelelően; a végső képernyőn az elem beépül az új elrendezésbe. A Material Design 3 szerint a helyesen implementált hős animáció 22%-kal csökkenti a hibás „vissza" kattintások számát.
Flutter-ben a Hero Animation a Hero widgeten keresztül valósul meg, amely elfogadja a kötelező tag paramétert — egy egyedi azonosítót. A Flutter automatikusan megtalálja az azonos taggal rendelkező Hero párt a forrás- és célútvonalakon, és elindítja az animációt. A Hero widget nem igényel kézi animációkezelést — a keretrendszer maga számítja ki a pályát és alkalmazza a transzformációt a belső FlightShuttle objektumon keresztül.
FlightShuttle — egy láthatatlan „űrsikló", amely az átmenet pillanatában létrehozza a Hero widget átfedő másolatát, elhelyezi azt az abszolút koordináta-rendszerben, és animálja a transzformációt (scale, translate, rotate) a kezdeti pozíciótól a végső pozícióig. A FlightShuttle-be egyedi görbék adhatók át a flightShuttleBuilder tulajdonságon keresztül, lehetővé téve a repülési pálya vezérlését: ívben repülés, határokról való pattogás vagy lassulás érkezés előtt. Flutter 3.16+-ban a Hero támogatja az animációt a clipRect és borderRadius megőrzésével, ami lekerekített kártyák hatását kelti, amelyek téglalap alakú képernyőkké alakulnak.
SwiftUI-ban a Hero Animation megfelelője a matchedGeometryEffect módosító, amelyet iOS 14-ben vezettek be. Ez két elemet köt össze egy azonosító (id) és névtér (namespace) segítségével. Ellentétben Flutter-rel, ahol a Hero automatikusan elfogja az átmenetet, a SwiftUI megköveteli a namespace explicit deklarálását és a módosító alkalmazását a jelenet mindkét részére — forrásra és célra — ugyanazon a hierarchián belül vagy GeometryReader-en keresztül.
A matchedGeometryEffect animáció támogatja a pozíció, méret, forgatás és saroksugár (cornerRadius) transzformációját. A simaság érdekében az Animation beállításra kerül a withAnimation-ban. A SwiftUI automatikusan interpolálja a köztes állapotokat az alapértelmezett easeInOut görbe alapján. iOS 17+-ban hozzáadásra került a matchedTransitionSource támogatása TabView és NavigationStack számára — a Hero Animation rendszerbeli megfelelője előre meghatározott pályával a szabványos átmenetekhez.
Android-ban a Hero Animation-t Shared Element Transition-nek hívják, és az ActivityOptions.makeSceneTransitionAnimation vagy FragmentTransaction.addSharedElement segítségével valósul meg. Minden közös elem megjelölésre kerül a transitionName attribútummal XML-ben vagy kódon keresztül, és az Android felel az aktivitások közötti animációért. Android 5.0-tól (API 21) kezdve az átmenetek a keretrendszer részévé váltak; előtte egyedi animációkat kellett írni ValueAnimator segítségével.
Jetpack Compose-ban a Shared Element Transition a Modifier.sharedElement és Modifier.sharedBounds módosítókon keresztül valósul meg AnimatedContent vagy SharedTransitionLayout kombinációjával (elérhető Compose BOM 2024.01-től). Ellentétben a View rendszerrel, a Compose lustán rajzolja újra az elemeket, így a közös elem gyorsítótárazásra kerül a CompositionLocal-ban. A Compose támogatja továbbá a lista→részlet (list→detail) animációt a scrollOffset megőrzésével — kulcsfontosságú képesség a Hero átmenetekhez. A Google I/O 2024 szerint a Compose Shared Element Transition 15%-kal gyorsabban működik, mint a View verzió a RenderNode közvetlen kezelésének köszönhetően.
Alapvető Hero Animation példa Flutter-ben: egy miniatűr a lista képernyőn és egy teljes képernyős kép a részlet képernyőn. Az image-hero tag összeköti a két widgetet, és a FlightShuttle automatikusan animálja az átmenetet a kártyáról a teljes képernyőre.
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,
),
),
),
);
}
}
A kulcspont — ugyanaz a tag (image-hero) mindkét képernyőn. A Flutter a Hero widgeteket tag alapján párosítja az útvonal átmenet során. Ha a tagek különböznek, az animáció nem indul el — az elem egyszerűen megjelenik az új képernyőn átmenet nélkül. Bármilyen típus használható a taghez, de a karakterláncok a legmegbízhatóbb lehetőség.
Hero Animation példa SwiftUI-ban NavigationStack-kel. A matchedGeometryEffect módosító összeköti a képet a listában és a részlet képernyőn a namespace-en keresztül, a withAnimation pedig elindítja a finom átmenetet.
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 létrehoz egy névteret a matchedGeometryEffect azonosító számára. Mindkét elem ugyanazt az id-t (hero) és megosztott namespace-t használja. Amikor az isDetailShown változik, a SwiftUI animálja a méret, pozíció és cornerRadius transzformációját a két nézet között, létrehozva a hős hatást. A szabványos NavigationLink-hez iOS 16+-tól használja a navigationTransition(.hero)-t.
Shared Element Transition példa Jetpack Compose-ban SharedTransitionLayout-val. A sharedElement módosító animálja a közös elemet a képernyők között navigáció során.
@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()
)
}
}
}
}
}
A Compose SharedTransitionLayout (Compose BOM 2024.01+) beburkolja a hierarchiát, és a sharedElement módosító a hero kulccsal összeköti a kezdeti és végső elemeket. Az AnimatedContent váltja az állapotot, és a Compose automatikusan animálja a kép méretét, pozícióját és alakját. A kulcsfontosságú előny — a bounds és clipBounds animáció támogatása, ami a kártya lekerekítésének hatását adja az átmenet során.
Gyakran Ismételt Kérdések
Igen, a Hero Animation mindkét platformon elérhető. iOS-ben a matchedGeometryEffect SwiftUI-ban vagy az UINavigationController.transitioningDelegate használatos. Android-ban — az ActivityOptions.makeSceneTransitionAnimation átmenet vagy Shared Element Transition Jetpack Compose-ban Modifier.sharedElement-lel. A Flutter a Hero widgeten keresztül valósítja meg a Hero Animation-t, amely mindkét platformon azonosan működik.
Igen, a Hero Animation támogatja a több közös elemet. Flutter-ben adjon meg több Hero-t különböző tagekkel. SwiftUI-ban használjon matchedGeometryEffect-et különböző azonosítókkal minden elemen. Az összes animáció párhuzamosan fut — minden elem a saját pályáján utazik.
A teljesítmény az implementációtól függ. A Flutter FlightShuttle a képeket RenderObject-en keresztül dolgozza fel, ami 60fps-t biztosít. A SwiftUI matchedGeometryEffect vektorizált. Nagy képekhez használja a precacheImage-et Flutter-ben vagy a prefetch-et SwiftUI-ban — ez kiküszöböli a késéseket az első átmenetnél. Az Android Compose lusta betöltést igényel AsyncImage-en keresztül.
A szabványos push átmenet az egész képernyőt mozgatja, kártyaköteg illúzióját keltve. A Hero Animation egy elemre rögzíti a vizuális figyelmet — a felhasználó látja annak folyamatos mozgását, ami javítja a képernyők közötti kapcsolat megértését. A push a hierarchikus navigációhoz jó, a Hero — a tartalom kiemeléséhez.
Nem, a Hero Animation beépített eszközökkel van implementálva. Flutter-ben ez a Hero widget a flutter/material.dart csomagból. SwiftUI-ban — a matchedGeometryEffect módosító. Android Jetpack Compose-ban — SharedTransitionLayout és Modifier.sharedElement a szabványos Compose-ból. További könyvtárak csak nem szabványos pályákhoz vagy összetett transzformációkhoz szükségesek.
Összefoglalás
Kulcsrakész mobilalkalmazást fejlesztünk
Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.
Olvassa el is