一些优化

This commit is contained in:
HapeLee
2026-05-17 21:03:25 +08:00
parent f4ca57f972
commit 3215adde42
3 changed files with 80 additions and 59 deletions
@@ -3,6 +3,8 @@ package io.legado.app.ui.widget.components.image.cover
import androidx.compose.animation.AnimatedVisibilityScope import androidx.compose.animation.AnimatedVisibilityScope
import androidx.compose.animation.ExperimentalSharedTransitionApi import androidx.compose.animation.ExperimentalSharedTransitionApi
import androidx.compose.animation.SharedTransitionScope import androidx.compose.animation.SharedTransitionScope
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
@@ -50,6 +52,18 @@ fun BookshelfCover(
sharedCoverKey = sharedCoverKey, sharedCoverKey = sharedCoverKey,
) )
// 使用 animatedVisibilityScope 的 animateEnterExit 为叠加层添加同步动画
val overlayModifier = Modifier.then(
if (animatedVisibilityScope != null) {
with(animatedVisibilityScope) {
Modifier.animateEnterExit(
enter = fadeIn(),
exit = fadeOut()
)
}
} else Modifier
)
if (!badgeText.isNullOrEmpty()) { if (!badgeText.isNullOrEmpty()) {
TextCard( TextCard(
text = badgeText, text = badgeText,
@@ -57,7 +71,8 @@ fun BookshelfCover(
iconSize = 12.dp, iconSize = 12.dp,
modifier = Modifier modifier = Modifier
.align(Alignment.TopEnd) .align(Alignment.TopEnd)
.padding(2.dp), .padding(2.dp)
.then(overlayModifier),
cornerRadius = 4.dp, cornerRadius = 4.dp,
horizontalPadding = 4.dp, horizontalPadding = 4.dp,
verticalPadding = 2.dp verticalPadding = 2.dp
@@ -71,7 +86,8 @@ fun BookshelfCover(
contentColor = LegadoTheme.colorScheme.onCardContainer, contentColor = LegadoTheme.colorScheme.onCardContainer,
modifier = Modifier modifier = Modifier
.align(Alignment.BottomStart) .align(Alignment.BottomStart)
.padding(2.dp), .padding(2.dp)
.then(overlayModifier),
cornerRadius = 4.dp, cornerRadius = 4.dp,
horizontalPadding = 4.dp, horizontalPadding = 4.dp,
verticalPadding = 2.dp verticalPadding = 2.dp
@@ -85,6 +101,7 @@ fun BookshelfCover(
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = 4.dp, vertical = 6.dp) .padding(horizontal = 4.dp, vertical = 6.dp)
.height(3.dp) .height(3.dp)
.then(overlayModifier)
) )
} }
} }
@@ -81,6 +81,17 @@ fun CoilBookCover(
Box( Box(
modifier = modifier modifier = modifier
.aspectRatio(5f / 7f) .aspectRatio(5f / 7f)
.then(
with(sharedTransitionScope) {
if (this != null && animatedVisibilityScope != null && sharedCoverKey != null) {
Modifier.sharedElement(
sharedContentState = rememberSharedContentState(sharedCoverKey),
animatedVisibilityScope = animatedVisibilityScope,
renderInOverlayDuringTransition = true
)
} else Modifier
}
)
.then( .then(
if (CoverConfig.coverShowShadow) { if (CoverConfig.coverShowShadow) {
Modifier.shadow(4.dp, RoundedCornerShape(4.dp)) Modifier.shadow(4.dp, RoundedCornerShape(4.dp))
@@ -94,67 +105,53 @@ fun CoilBookCover(
) )
.clip(RoundedCornerShape(4.dp)) .clip(RoundedCornerShape(4.dp))
) { ) {
val imageContentModifier = Modifier if (hasCustomDefault && !isOnlineCoverLoaded) {
.fillMaxSize() AsyncImage(
.then( model = buildCoverImageRequest(
with(sharedTransitionScope) { context = context,
if (this != null && animatedVisibilityScope != null && sharedCoverKey != null) { data = randomPath,
Modifier.sharedElement( sourceOrigin = null,
sharedContentState = rememberSharedContentState(sharedCoverKey), loadOnlyWifi = false,
animatedVisibilityScope = animatedVisibilityScope, crossfade = showLoadingPlaceholder,
renderInOverlayDuringTransition = true memoryCacheKey = randomPath,
) ),
} else Modifier contentDescription = null,
} imageLoader = koinInject(),
contentScale = ContentScale.Crop,
modifier = Modifier
.fillMaxSize()
.clip(RoundedCornerShape(4.dp))
) )
}
Box(modifier = imageContentModifier) { if (finalPath != null) {
if (hasCustomDefault && !isOnlineCoverLoaded) { AsyncImage(
AsyncImage( model = buildCoverImageRequest(
model = buildCoverImageRequest( context = context,
context = context, data = finalPath,
data = randomPath, sourceOrigin = sourceOrigin,
sourceOrigin = null, loadOnlyWifi = CoverConfig.loadCoverOnlyWifi,
loadOnlyWifi = false, crossfade = showLoadingPlaceholder,
crossfade = showLoadingPlaceholder, memoryCacheKey = finalPath,
), ),
contentDescription = null, contentDescription = null,
imageLoader = koinInject(), imageLoader = koinInject(),
contentScale = ContentScale.Crop, contentScale = ContentScale.Crop,
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.clip(RoundedCornerShape(4.dp)) .clip(RoundedCornerShape(4.dp)),
) onSuccess = {
} isOnlineCoverLoaded = true
onLoadFinish?.invoke()
if (finalPath != null) { },
AsyncImage( onError = {
model = buildCoverImageRequest( isOnlineCoverLoaded = false
context = context,
data = finalPath,
sourceOrigin = sourceOrigin,
loadOnlyWifi = CoverConfig.loadCoverOnlyWifi,
crossfade = showLoadingPlaceholder,
),
contentDescription = null,
imageLoader = koinInject(),
contentScale = ContentScale.Crop,
modifier = Modifier
.fillMaxSize()
.clip(RoundedCornerShape(4.dp)),
onSuccess = {
isOnlineCoverLoaded = true
onLoadFinish?.invoke()
},
onError = {
isOnlineCoverLoaded = false
onLoadFinish?.invoke()
}
)
} else {
LaunchedEffect(Unit) {
onLoadFinish?.invoke() onLoadFinish?.invoke()
} }
)
} else {
LaunchedEffect(Unit) {
onLoadFinish?.invoke()
} }
} }
@@ -9,11 +9,18 @@ fun buildCoverImageRequest(
sourceOrigin: String?, sourceOrigin: String?,
loadOnlyWifi: Boolean, loadOnlyWifi: Boolean,
crossfade: Boolean = true, crossfade: Boolean = true,
memoryCacheKey: String? = null,
configure: ImageRequest.Builder.() -> Unit = {}, configure: ImageRequest.Builder.() -> Unit = {},
): ImageRequest { ): ImageRequest {
return ImageRequest.Builder(context) return ImageRequest.Builder(context)
.data(data) .data(data)
.crossfade(crossfade) .crossfade(crossfade)
.apply {
if (memoryCacheKey != null) {
memoryCacheKey(memoryCacheKey)
placeholderMemoryCacheKey(memoryCacheKey)
}
}
.setParameter("sourceOrigin", sourceOrigin) .setParameter("sourceOrigin", sourceOrigin)
.setParameter("loadOnlyWifi", loadOnlyWifi) .setParameter("loadOnlyWifi", loadOnlyWifi)
.apply(configure) .apply(configure)