- YT Video
This code is part of my project, ZZZ Archive, which will soon be open-sourced on GitHub and Figma Community. Stay tuned! My GitHub
package ui.components
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.asPaddingValues
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.statusBars
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
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.graphicsLayer
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.delay
import org.jetbrains.compose.resources.vectorResource
import ui.theme.AppTheme
import zzzarchive.composeapp.generated.resources.Res
import zzzarchive.composeapp.generated.resources.ic_bangboo
data class DvdState(
val offsetX: Float = 0f,
val offsetY: Float = 0f,
var velocityX: Float = 3f,
var velocityY: Float = 3f,
val size: Int = 24
)
@Composable
fun DVDScreensaver(modifier: Modifier, colors: List<Color>, imageSize: Int = 24) {
BoxWithConstraints(modifier = modifier) {
var currentColorIndex by remember { mutableStateOf(0) }
var dvdState by remember { mutableStateOf(DvdState(size = imageSize)) }
val statusBarHeight = WindowInsets.statusBars.asPaddingValues().calculateTopPadding().value
val screenWidth = with(LocalDensity.current) { maxWidth.toPx() }
val screenHeight = with(LocalDensity.current) { maxHeight.toPx() }
LaunchedEffect(key1 = dvdState) {
while (true) {
delay(8)
dvdState = updateDVDPosition(
dvdState,
screenWidth,
screenHeight,
statusBarHeight,
onBounce = {
currentColorIndex = (currentColorIndex + 1) % colors.size
})
}
}
Icon(
modifier = Modifier.size(imageSize.dp).graphicsLayer {
translationX = dvdState.offsetX
translationY = dvdState.offsetY
}.background(colors[currentColorIndex], CircleShape).padding(8.dp),
imageVector = vectorResource(Res.drawable.ic_bangboo),
contentDescription = "DVD screen saver logo",
tint = AppTheme.colors.surface
)
}
}
private fun updateDVDPosition(
dvdState: DvdState,
screenWidth: Float,
screenHeight: Float,
statusBarHeight: Float,
onBounce: () -> Unit
): DvdState {
var newOffsetX = dvdState.offsetX + dvdState.velocityX
var newOffsetY = dvdState.offsetY + dvdState.velocityY
if (newOffsetX + statusBarHeight + dvdState.size * 2 >= screenWidth || newOffsetX <= 0) {
dvdState.velocityX *= -1
newOffsetX = dvdState.offsetX + dvdState.velocityX
onBounce()
}
if (newOffsetY + statusBarHeight + dvdState.size * 2 >= screenHeight || newOffsetY <= 0) {
dvdState.velocityY *= -1
newOffsetY = dvdState.offsetY + dvdState.velocityY
onBounce()
}
return dvdState.copy(offsetX = newOffsetX, offsetY = newOffsetY)
}