diff --git a/shared/src/commonMain/kotlin/plus/rua/project/ui/DayCell.kt b/shared/src/commonMain/kotlin/plus/rua/project/ui/DayCell.kt index e6e2855..03bb0c5 100644 --- a/shared/src/commonMain/kotlin/plus/rua/project/ui/DayCell.kt +++ b/shared/src/commonMain/kotlin/plus/rua/project/ui/DayCell.kt @@ -1,8 +1,10 @@ package plus.rua.project.ui -import androidx.compose.foundation.BorderStroke -import androidx.compose.foundation.background -import androidx.compose.foundation.border +import androidx.compose.animation.animateColor +import androidx.compose.animation.core.FastOutSlowInEasing +import androidx.compose.animation.core.animateFloat +import androidx.compose.animation.core.tween +import androidx.compose.animation.core.updateTransition import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.aspectRatio @@ -10,13 +12,22 @@ import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import androidx.compose.ui.unit.dp +import androidx.compose.ui.draw.drawBehind +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.drawscope.Stroke import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp import kotlinx.datetime.LocalDate +enum class DayCellState { + NORMAL, OTHER_MONTH, TODAY, SELECTED, SELECTED_TODAY +} + /** * 单个日期单元格,显示日期数字并支持选中/今天/非当月状态。 * @@ -36,26 +47,84 @@ fun DayCell( onClick: () -> Unit, modifier: Modifier = Modifier ) { - val contentColor = when { - isSelected && isToday -> MaterialTheme.colorScheme.onPrimaryContainer - isSelected -> MaterialTheme.colorScheme.onPrimary - isToday -> MaterialTheme.colorScheme.primary - !isCurrentMonth -> MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f) - else -> MaterialTheme.colorScheme.onSurface + val currentState = when { + isSelected && isToday -> DayCellState.SELECTED_TODAY + isSelected -> DayCellState.SELECTED + isToday -> DayCellState.TODAY + !isCurrentMonth -> DayCellState.OTHER_MONTH + else -> DayCellState.NORMAL } + val transition = updateTransition(targetState = currentState, label = "dayCell") + + val revealProgress by transition.animateFloat( + transitionSpec = { tween(250, easing = FastOutSlowInEasing) }, + label = "revealProgress" + ) { state -> + when (state) { + DayCellState.SELECTED, DayCellState.SELECTED_TODAY -> 1f + else -> 0f + } + } + + val contentColor by transition.animateColor( + transitionSpec = { tween(250, easing = FastOutSlowInEasing) }, + label = "contentColor" + ) { state -> + when (state) { + DayCellState.SELECTED_TODAY -> MaterialTheme.colorScheme.onPrimaryContainer + DayCellState.SELECTED -> MaterialTheme.colorScheme.onPrimary + DayCellState.TODAY -> MaterialTheme.colorScheme.primary + DayCellState.OTHER_MONTH -> MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f) + DayCellState.NORMAL -> MaterialTheme.colorScheme.onSurface + } + } + + val selectedColor by transition.animateColor( + transitionSpec = { tween(250, easing = FastOutSlowInEasing) }, + label = "selectedColor" + ) { state -> + when (state) { + DayCellState.SELECTED_TODAY -> MaterialTheme.colorScheme.primaryContainer + DayCellState.SELECTED -> MaterialTheme.colorScheme.primary + else -> Color.Transparent + } + } + + val borderAlpha by transition.animateFloat( + transitionSpec = { tween(250, easing = FastOutSlowInEasing) }, + label = "borderAlpha" + ) { state -> + when (state) { + DayCellState.TODAY -> 1.5f + else -> 0f + } + } + + val todayBorderColor = MaterialTheme.colorScheme.primary + Box( modifier = modifier .aspectRatio(1f) .clip(CircleShape) - .then( - when { - isSelected && isToday -> Modifier.background(MaterialTheme.colorScheme.primaryContainer) - isSelected -> Modifier.background(MaterialTheme.colorScheme.primary) - isToday -> Modifier.border(BorderStroke(1.5.dp, MaterialTheme.colorScheme.primary), CircleShape) - else -> Modifier + .drawBehind { + if (revealProgress > 0f) { + val maxRadius = size.minDimension / 2f + drawCircle( + color = selectedColor, + radius = revealProgress * maxRadius, + center = Offset(size.width / 2f, size.height / 2f) + ) } - ) + if (borderAlpha > 0f) { + drawCircle( + color = todayBorderColor.copy(alpha = borderAlpha.coerceAtMost(1f)), + radius = size.minDimension / 2f, + center = Offset(size.width / 2f, size.height / 2f), + style = Stroke(width = borderAlpha.coerceAtMost(1.5f) * 1.5.dp.toPx()) + ) + } + } .clickable(onClick = onClick), contentAlignment = Alignment.Center ) {