feat: 标记待办为已完成后的彩带庆祝

This commit is contained in:
Super12138 2025-01-13 18:02:45 +08:00
parent 70a1febce3
commit 0e54c94572
5 changed files with 124 additions and 4 deletions

View file

@ -6,8 +6,11 @@ import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.lifecycle.viewmodel.compose.viewModel
import cn.super12138.todo.ui.components.Konfetti
import cn.super12138.todo.ui.navigation.ToDoNavigation
import cn.super12138.todo.ui.theme.ToDoTheme
import cn.super12138.todo.ui.viewmodels.MainViewModel
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
@ -16,7 +19,10 @@ class MainActivity : ComponentActivity() {
setContent {
ToDoTheme {
Surface(color = MaterialTheme.colorScheme.background) {
ToDoNavigation()
val mainViewModel: MainViewModel = viewModel()
val showConfetti = mainViewModel.showConfetti
ToDoNavigation(viewModel = mainViewModel)
Konfetti(state = showConfetti)
}
}
}

View file

@ -0,0 +1,106 @@
package cn.super12138.todo.ui.components
import androidx.annotation.FloatRange
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
import androidx.core.graphics.ColorUtils
import nl.dionsegijn.konfetti.compose.KonfettiView
import nl.dionsegijn.konfetti.compose.OnParticleSystemUpdateListener
import nl.dionsegijn.konfetti.core.Angle
import nl.dionsegijn.konfetti.core.Party
import nl.dionsegijn.konfetti.core.PartySystem
import nl.dionsegijn.konfetti.core.Position
import nl.dionsegijn.konfetti.core.Spread
import nl.dionsegijn.konfetti.core.emitter.Emitter
import java.util.concurrent.TimeUnit
/**
* 参照于https://github.com/hushenghao/AndroidEasterEggs/blob/main/app/src/main/java/com/dede/android_eggs/views/main/compose/Konfetti.kt
*/
@Composable
fun Konfetti(
state: MutableState<Boolean>,
primary: Color = MaterialTheme.colorScheme.primary,
modifier: Modifier = Modifier
) {
var visible by state
if (!visible) {
return
}
val listener = remember(state) {
object : OnParticleSystemUpdateListener {
override fun onParticleSystemEnded(system: PartySystem, activeSystems: Int) {
if (activeSystems == 0)
visible = false
}
}
}
KonfettiView(
modifier = Modifier
.fillMaxSize()
.then(modifier),
parties = remember { particles(primary.toArgb()) },
updateListener = listener
)
}
private val defaultColors = listOf(
0xFCE18A,
0x009688,
0xFF726D,
0xF4306D,
0xB48DEF,
0x95FF82,
0x82ECFF,
0xFF9800,
0x0E008A,
)
private const val colorBlendFraction = 0.3f
private fun particles(primary: Int) = listOf(
Party(
speed = 0f,
maxSpeed = 12f,
damping = 0.9f,
angle = Angle.BOTTOM,
spread = Spread.ROUND,
colors = defaultColors.map { it.blend(primary, colorBlendFraction) },
emitter = Emitter(duration = 2, TimeUnit.SECONDS).perSecond(100),
position = Position.Relative(0.0, 0.0).between(Position.Relative(1.0, 0.0)),
),
Party(
speed = 10f,
maxSpeed = 30f,
damping = 0.9f,
angle = Angle.RIGHT - 55,
spread = 60,
colors = defaultColors.map { it.blend(primary, colorBlendFraction) },
emitter = Emitter(duration = 2, TimeUnit.SECONDS).perSecond(100),
position = Position.Relative(0.0, 1.0)
),
Party(
speed = 10f,
maxSpeed = 30f,
damping = 0.9f,
angle = Angle.RIGHT - 125,
spread = 60,
colors = defaultColors.map { it.blend(primary, colorBlendFraction) },
emitter = Emitter(duration = 2, TimeUnit.SECONDS).perSecond(100),
position = Position.Relative(1.0, 1.0)
)
)
fun Int.blend(
color: Int,
@FloatRange(from = 0.0, to = 1.0) fraction: Float = 0.5f,
): Int = ColorUtils.blendARGB(this, color, fraction)

View file

@ -3,17 +3,18 @@ package cn.super12138.todo.ui.navigation
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import cn.super12138.todo.ui.pages.main.MainPage
import cn.super12138.todo.ui.viewmodels.MainViewModel
@Composable
fun ToDoNavigation(
navController: NavHostController = rememberNavController(),
startDestination: String = ToDoScreen.Main.name,
viewModel: MainViewModel,
modifier: Modifier = Modifier
) {
NavHost(
@ -23,7 +24,7 @@ fun ToDoNavigation(
) {
composable(ToDoScreen.Main.name) {
MainPage(
viewModel = viewModel(),
viewModel = viewModel,
modifier = Modifier.fillMaxSize()
)
}

View file

@ -54,7 +54,6 @@ fun MainPage(viewModel: MainViewModel, modifier: Modifier = Modifier) {
val bottomSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
val scope = rememberCoroutineScope()
// TODO: 寻找更好的适配方式
val configuration = LocalConfiguration.current
val orientation = when (configuration.orientation) {
@ -134,6 +133,7 @@ fun MainPage(viewModel: MainViewModel, modifier: Modifier = Modifier) {
id = id
)
)
viewModel.playConfetti()
}
},
modifier = Modifier
@ -170,6 +170,7 @@ fun MainPage(viewModel: MainViewModel, modifier: Modifier = Modifier) {
id = id
)
)
viewModel.playConfetti()
}
},
modifier = Modifier

View file

@ -1,5 +1,6 @@
package cn.super12138.todo.ui.viewmodels
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import cn.super12138.todo.logic.Repository
@ -9,6 +10,7 @@ import kotlinx.coroutines.launch
class MainViewModel : ViewModel() {
val toDos: Flow<List<TodoEntity>> = Repository.getAllTodos()
val showConfetti = mutableStateOf(false)
fun addTodo(toDo: TodoEntity) {
viewModelScope.launch {
@ -21,4 +23,8 @@ class MainViewModel : ViewModel() {
Repository.updateTodo(toDo)
}
}
fun playConfetti() {
showConfetti.value = true
}
}