Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
import android.content.res.Configuration
import androidx.compose.animation.core.*
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Path
import androidx.compose.ui.graphics.drawscope.Fill
import androidx.compose.ui.graphics.drawscope.clipPath
import androidx.compose.ui.graphics.drawscope.clipRect
import androidx.compose.ui.tooling.preview.Preview
import kotlin.math.sin
import kotlin.random.Random
@Composable
fun WavyBackground(
topWaveColor: Color,
middleWaveColor: Color,
bottomWaveColor: Color
) {
Box(modifier = Modifier.fillMaxSize()) {
Wave(
waveData = WaveData(
color = topWaveColor,
closeOnTop = false,
frequency = 420f,
durationInMillis = 250_000L,
amplitude = 50f
),
yFunctionOverXPercentage = {
0.6f
}
)
Wave(
waveData = WaveData(
color = middleWaveColor,
closeOnTop = false,
frequency = 760f,
durationInMillis = 200_000L,
amplitude = 60f
),
yFunctionOverXPercentage = {
0.65f
}
)
Wave(
waveData = WaveData(
color = bottomWaveColor,
closeOnTop = false,
frequency = 960f,
durationInMillis = 300_000L
),
yFunctionOverXPercentage = { 0.7f }
)
}
}
@Composable
fun Wave(
modifier: Modifier = Modifier,
waveData: WaveData,
startXPercentage: Float = 0f,
yFunctionOverXPercentage: (Float) -> Float
) {
val randomSeed = remember {
Random.nextDouble(0.0, waveData.frequency.toDouble()).toFloat()
}
val infTransition = rememberInfiniteTransition()
val frequency by infTransition.animateFloat(
initialValue = 0f,
targetValue = waveData.frequency,
animationSpec = infiniteRepeatable(
animation = tween((waveData.durationInMillis).toInt(), easing = LinearEasing),
repeatMode = RepeatMode.Reverse
)
)
Canvas(modifier = modifier.fillMaxSize()) {
val width = size.width.toInt()
var minHeight = 0f
val path = Path().apply {
if (waveData.closeOnTop) {
moveTo(0f, 0.0f)
} else {
moveTo(0f, size.height)
}
for (i in (startXPercentage * width).toInt()..width) {
val y =
size.height * yFunctionOverXPercentage(i / size.width) +
(sin(i * waveData.length + (frequency + randomSeed) % waveData.frequency) * waveData.amplitude)
minHeight = minOf(minHeight, y)
lineTo(i.toFloat(), y)
}
if (waveData.closeOnTop) {
lineTo(size.width, minHeight)
} else {
lineTo(size.width, size.height)
}
close()
}
clipRect {
clipPath(path) {
drawPath(path, waveData.color, style = Fill)
}
}
}
}
data class WaveData(
val color: Color,
val amplitude: Float = 67f,
val length: Float = 0.005f,
val frequency: Float = 360f,
val durationInMillis: Long = 300_000L,
val closeOnTop: Boolean = true
)
@Preview(
name = "Light Mode",
widthDp = 320,
heightDp = 480,
uiMode = Configuration.UI_MODE_NIGHT_NO
)
@Preview(
name = "Dark Mode",
widthDp = 320,
heightDp = 480,
uiMode = Configuration.UI_MODE_NIGHT_YES
)
@Composable
fun PomodoroFullScreenPreview() {
Surface(color = MaterialTheme.colors.background) {
Box(modifier = Modifier.fillMaxSize()) {
WavyBackground(
Color(0x5, 0x18, 0x63, 0xFF),
Color(0x15, 0x6F, 0xBE, 0xFF),
Color(0xA0, 0xC1, 0xE9, 0xFF),
)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment