diff --git a/app/src/main/java/com/aiosman/riderpro/ui/login/login.kt b/app/src/main/java/com/aiosman/riderpro/ui/login/login.kt index ae59922..63bf597 100644 --- a/app/src/main/java/com/aiosman/riderpro/ui/login/login.kt +++ b/app/src/main/java/com/aiosman/riderpro/ui/login/login.kt @@ -3,11 +3,16 @@ package com.aiosman.riderpro.ui.login import android.content.ContentValues.TAG import android.util.Log import android.widget.Toast +import androidx.compose.animation.core.LinearEasing +import androidx.compose.animation.core.animateFloatAsState +import androidx.compose.animation.core.infiniteRepeatable +import androidx.compose.animation.core.tween import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize @@ -16,20 +21,39 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.widthIn +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableFloatStateOf +import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha +import androidx.compose.ui.draw.clip +import androidx.compose.ui.draw.scale import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleEventObserver +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.compose.LocalLifecycleOwner import com.aiosman.riderpro.AppState import com.aiosman.riderpro.AppStore import com.aiosman.riderpro.LocalNavController @@ -42,6 +66,8 @@ import com.aiosman.riderpro.ui.modifiers.noRippleClickable import com.aiosman.riderpro.utils.GoogleLogin import com.google.accompanist.systemuicontroller.rememberSystemUiController import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.cancel +import kotlinx.coroutines.delay import kotlinx.coroutines.launch @Composable @@ -112,46 +138,19 @@ fun LoginPage() { } } Box( - modifier = Modifier.fillMaxSize().background(Color.White) + modifier = Modifier + .fillMaxSize() + .background(Color.White) ) { - // bg image Box( - modifier = Modifier.fillMaxWidth().height(900.dp).offset( - y = (-72).dp - ), + modifier = Modifier + .fillMaxSize() + .offset( + y = (-72).dp + ), ) { - Image( - painter = painterResource(id = R.mipmap.rider_pro_login_bg), - contentDescription = "Login Background", - modifier = Modifier.fillMaxWidth().fillMaxHeight(), - contentScale = androidx.compose.ui.layout.ContentScale.Crop, - ) - Box( - modifier = Modifier - .fillMaxWidth() - .fillMaxHeight() - .background(Color.White.copy(alpha = 0.8f)), - contentAlignment = Alignment.BottomCenter - ) { - Box( - modifier = Modifier - .fillMaxWidth() - .height(300.dp) - .background( - Brush.verticalGradient( - colors = listOf( - Color.Transparent, - Color.White - ), - startY = 0f, - endY = 300f - ) - ) - ) - } + MovingImageWall() } - - Column( modifier = Modifier .fillMaxSize() @@ -230,7 +229,209 @@ fun LoginPage() { Spacer(modifier = Modifier.height(120.dp)) } } - - } + +@Composable +fun MovingImageWall() { + val imageList1 = remember { mutableStateListOf(R.drawable.wall_1_1, R.drawable.wall_1_2, R.drawable.wall_1_3) } + val imageList2 = remember { mutableStateListOf(R.drawable.wall_2_1, R.drawable.wall_2_2, R.drawable.wall_2_3) } + val imageList3 = remember { mutableStateListOf(R.drawable.wall_3_1, R.drawable.wall_3_2, R.drawable.wall_3_3) } + + var offset1 by remember { mutableFloatStateOf(0f) } + var offset2 by remember { mutableFloatStateOf(0f) } + var offset3 by remember { mutableFloatStateOf(0f) } + + val lifecycleOwner = LocalLifecycleOwner.current + val coroutineScope = rememberCoroutineScope() // 使用 rememberCoroutineScope + LaunchedEffect(key1 = lifecycleOwner) { + lifecycleOwner.lifecycle.addObserver(object : LifecycleEventObserver { + override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) { + when (event) { + Lifecycle.Event.ON_RESUME -> { + coroutineScope.launch { // 在 coroutineScope 中启动协程 + animateImageWall(imageList1, offset1, speed = 1f) { offset1 = it } + } + coroutineScope.launch { + animateImageWall(imageList2, offset2, speed = 1.5f, reverse = true) { offset2 = it } + } + coroutineScope.launch { + animateImageWall(imageList3, offset3, speed = 2f) { offset3 = it } + } + } + Lifecycle.Event.ON_PAUSE -> { + // 可选: 在Composable暂停时取消协程 + coroutineScope.cancel() + } + else -> {} + } + } + }) + } + + Box( + modifier = Modifier + .fillMaxSize() + .offset(y = (-72).dp) + ) { + + Row (modifier = Modifier + .fillMaxWidth() + .height(700.dp)){ + // 第1列 + ImageColumn(imageList1, offset1, + Modifier + .fillMaxHeight() + .weight(1f)) + // 第2列 + ImageColumn(imageList2, offset2, + Modifier + .fillMaxHeight() + .weight(1f), reverse = true) + // 第3列 + ImageColumn(imageList3, offset3, + Modifier + .fillMaxHeight() + .weight(1f)) + } + + // 白色叠加层 + Box( + modifier = Modifier + .fillMaxWidth() + .fillMaxHeight() + .background(Color.White.copy(alpha = 0.5f)), + contentAlignment = Alignment.BottomCenter + ) { + Box( + modifier = Modifier + .fillMaxWidth() + .height(650.dp) + .background( + Brush.verticalGradient( + colors = listOf( + Color.Transparent, + Color.White + ), + startY = 0f, + endY = 650f + ) + ) + ) + } + } +} + +@Composable +fun ImageColumn( + imageList: List, + offset: Float, + modifier: Modifier, + reverse: Boolean = false +) { + val imageCount = imageList.size + val imageHeight = 208.dp + var currentImage by remember { mutableStateOf(0) } + val totalHeight = imageHeight.value * imageCount // 计算总高度 + Column(modifier = modifier) { + for (i in 0 until imageCount) { + Box(modifier = Modifier + .width(156.dp) + .height(208.dp) + .scale(1f) + .graphicsLayer { + var translation = if (reverse) { + offset + } else { + offset + } + // 检查是否超出屏幕范围 + if (translation < -imageHeight.value) { // 移出屏幕底部 + translation += totalHeight + } else if (translation > totalHeight) { // 移出屏幕顶部 + translation -= totalHeight + } + translationY = translation + } + .clip(RoundedCornerShape(16.dp)), + contentAlignment = Alignment.Center){ + Image( + painter = painterResource(id = imageList[(currentImage + i) % imageCount]), + contentDescription = "背景图片", + modifier = Modifier + .width(156.dp) + .height(208.dp) + .scale(0.9f) + .clip(RoundedCornerShape(16.dp)), + contentScale = ContentScale.Crop + ) + } + + } + } +} + +suspend fun animateImageWall( + imageList: MutableList, + initialOffset: Float, + speed: Float, + reverse: Boolean = false, + onUpdate: (Float) -> Unit, +) { + var currentOffset = initialOffset + val imageCount = imageList.size + val imageHeight = 208.dp + var currentIndex = 0 // 添加一个变量来跟踪当前显示的图片索引 + while (true) { + onUpdate(currentOffset) + if (reverse) { + currentOffset -= speed + if (currentOffset < -imageHeight.value * (imageCount - 1)) { +// val firstImage = imageList.first() // 将第一个元素移除 +// imageList.add(firstImage) // 将第一个元素添加到末尾 + currentOffset += imageHeight.value * imageCount // 将图片移动到末尾 +// currentIndex = (currentIndex - 1 + imageCount) % imageCount // 反向旋转 + } + } else { + currentOffset += speed + if (currentOffset > imageHeight.value * (imageCount - 1)) { +// val lastImage = imageList.last() // 将最后一个元素移除 +// imageList.add(0, lastImage) // 将最后一个元素添加到开头 + currentOffset -= imageHeight.value * imageCount // 将图片移动到开头 +// currentIndex = (currentIndex + 1) % imageCount // 正向旋转 + } + } + // 更新 ImageColumn 中的 currentImage +// onUpdate(currentIndex.toFloat()) // 将当前索引传递给 onUpdate + delay(16) + } +} + +// 计算图片的透明度 +private fun calculateAlpha( + offset: Float, + index: Int, + imageHeight: Float, + totalHeight: Float, + reverse: Boolean +): Float { + val offsetDp = if (reverse) { + -offset + } else { + offset + } + val imageTop = index * imageHeight + val imageBottom = imageTop + imageHeight + return when { + offsetDp in imageTop..imageBottom -> { + (offsetDp - imageTop) / imageHeight + } + (offsetDp + totalHeight) in imageTop..imageBottom -> { // 考虑循环滚动的情况 + (offsetDp + totalHeight - imageTop) / imageHeight + } + (offsetDp - totalHeight) in imageTop..imageBottom -> { // 考虑循环滚动的情况 + (offsetDp - totalHeight - imageTop) / imageHeight + } + else -> 0f + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/wall_1_1.jpg b/app/src/main/res/drawable/wall_1_1.jpg new file mode 100644 index 0000000..64d6ecd Binary files /dev/null and b/app/src/main/res/drawable/wall_1_1.jpg differ diff --git a/app/src/main/res/drawable/wall_1_2.jpg b/app/src/main/res/drawable/wall_1_2.jpg new file mode 100644 index 0000000..312ad71 Binary files /dev/null and b/app/src/main/res/drawable/wall_1_2.jpg differ diff --git a/app/src/main/res/drawable/wall_1_3.jpg b/app/src/main/res/drawable/wall_1_3.jpg new file mode 100644 index 0000000..6586d57 Binary files /dev/null and b/app/src/main/res/drawable/wall_1_3.jpg differ diff --git a/app/src/main/res/drawable/wall_2_1.jpg b/app/src/main/res/drawable/wall_2_1.jpg new file mode 100644 index 0000000..9511a09 Binary files /dev/null and b/app/src/main/res/drawable/wall_2_1.jpg differ diff --git a/app/src/main/res/drawable/wall_2_2.jpg b/app/src/main/res/drawable/wall_2_2.jpg new file mode 100644 index 0000000..ddaa4cd Binary files /dev/null and b/app/src/main/res/drawable/wall_2_2.jpg differ diff --git a/app/src/main/res/drawable/wall_2_3.jpg b/app/src/main/res/drawable/wall_2_3.jpg new file mode 100644 index 0000000..b88001f Binary files /dev/null and b/app/src/main/res/drawable/wall_2_3.jpg differ diff --git a/app/src/main/res/drawable/wall_3_1.jpg b/app/src/main/res/drawable/wall_3_1.jpg new file mode 100644 index 0000000..f7d8643 Binary files /dev/null and b/app/src/main/res/drawable/wall_3_1.jpg differ diff --git a/app/src/main/res/drawable/wall_3_2.jpg b/app/src/main/res/drawable/wall_3_2.jpg new file mode 100644 index 0000000..8fab867 Binary files /dev/null and b/app/src/main/res/drawable/wall_3_2.jpg differ diff --git a/app/src/main/res/drawable/wall_3_3.jpg b/app/src/main/res/drawable/wall_3_3.jpg new file mode 100644 index 0000000..25a3214 Binary files /dev/null and b/app/src/main/res/drawable/wall_3_3.jpg differ diff --git a/app/src/main/res/drawable/wall_demo.jpg b/app/src/main/res/drawable/wall_demo.jpg new file mode 100644 index 0000000..b626775 Binary files /dev/null and b/app/src/main/res/drawable/wall_demo.jpg differ