Created
June 15, 2025 17:05
-
-
Save Kyriakos-Georgiopoulos/9430d837f9d2fdc4300c4bee2a7ef7c2 to your computer and use it in GitHub Desktop.
RealisticCd
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import androidx.compose.animation.core.LinearEasing | |
import androidx.compose.animation.core.RepeatMode | |
import androidx.compose.animation.core.animateFloat | |
import androidx.compose.animation.core.infiniteRepeatable | |
import androidx.compose.animation.core.rememberInfiniteTransition | |
import androidx.compose.animation.core.tween | |
import androidx.compose.foundation.background | |
import androidx.compose.foundation.layout.Box | |
import androidx.compose.foundation.layout.fillMaxSize | |
import androidx.compose.foundation.layout.size | |
import androidx.compose.runtime.* | |
import androidx.compose.ui.Alignment | |
import androidx.compose.ui.Modifier | |
import androidx.compose.ui.draw.drawBehind | |
import androidx.compose.ui.geometry.Offset | |
import androidx.compose.ui.graphics.* | |
import androidx.compose.ui.graphics.drawscope.DrawScope | |
import androidx.compose.ui.graphics.drawscope.Stroke | |
import androidx.compose.ui.graphics.drawscope.rotate | |
import androidx.compose.ui.graphics.graphicsLayer | |
import androidx.compose.ui.tooling.preview.Preview | |
import androidx.compose.ui.unit.dp | |
import kotlin.math.cos | |
import kotlin.math.sin | |
import kotlin.math.PI | |
@Composable | |
fun RealisticCd( | |
modifier: Modifier = Modifier | |
) { | |
val infiniteTransition = rememberInfiniteTransition("CD Sway") | |
val rotation by infiniteTransition.animateFloat( | |
initialValue = -10f, | |
targetValue = 10f, | |
animationSpec = infiniteRepeatable( | |
animation = tween(3000, easing = LinearEasing), | |
repeatMode = RepeatMode.Reverse | |
), | |
label = "Sway Angle" | |
) | |
Box( | |
modifier = modifier | |
.size(360.dp) | |
.graphicsLayer { rotationZ = rotation } | |
.drawBehind { | |
val radius = size.minDimension / 2f | |
val center = Offset(size.width / 2f, size.height / 2f) | |
drawCircle( | |
brush = Brush.radialGradient( | |
colors = listOf( | |
Color(0xFFD7D7D7), | |
Color(0xFFAFAFAF), | |
Color(0xFF7F7F7F) | |
), | |
center = center, | |
radius = radius | |
), | |
center = center, | |
radius = radius | |
) | |
// Outer rim with a glossy highlight | |
drawCircle( | |
brush = Brush.verticalGradient( | |
colors = listOf( | |
Color.White.copy(alpha = 0.4f), | |
Color.Transparent, | |
Color.White.copy(alpha = 0.1f) | |
), | |
startY = center.y - radius, | |
endY = center.y + radius | |
), | |
radius = radius, | |
center = center, | |
style = Stroke(width = radius * 0.02f) | |
) | |
// Rainbow rays | |
drawRainbowBeam(start = 50f, sweep = 36f, center, radius) | |
drawRainbowBeam(start = 230f, sweep = 36f, center, radius) | |
// Light-colored rays | |
drawBeam(start = 140f, sweep = 34f, center, radius) | |
drawBeam(start = 320f, sweep = 34f, center, radius) | |
// Inner circles to define CD hole | |
drawCircle(color = Color(0xFFCCCCCC), center = center, radius = radius * 0.25f) | |
drawCircle(color = Color.Black, center = center, radius = radius * 0.1f) | |
// Animated sheen highlight | |
val sheen = Offset( | |
(center.x + cos(Math.toRadians(rotation.toDouble())) * radius * 0.25f).toFloat(), | |
(center.y + sin(Math.toRadians(rotation.toDouble())) * radius * 0.25f).toFloat() | |
) | |
drawCircle( | |
brush = Brush.radialGradient( | |
colors = listOf(Color.White.copy(alpha = 0.15f), Color.Transparent), | |
center = sheen, | |
radius = radius * 0.8f | |
), | |
center = center, | |
radius = radius | |
) | |
// Inner rim highlight | |
drawCircle( | |
brush = Brush.verticalGradient( | |
colors = listOf( | |
Color.White.copy(alpha = 0.5f), | |
Color.Transparent, | |
Color.White.copy(alpha = 0.08f) | |
), | |
startY = center.y - radius / 3.8f, | |
endY = center.y + radius / 3.8f | |
), | |
radius = radius * 0.23f, | |
center = center, | |
style = Stroke(width = radius * 0.015f) | |
) | |
} | |
) | |
} | |
private fun DrawScope.drawRainbowBeam(start: Float, sweep: Float, center: Offset, radius: Float) { | |
val lines = 200 | |
val angleStep = sweep / lines | |
for (i in 0 until lines) { | |
val ang = start + i * angleStep | |
val rad = Math.toRadians(ang.toDouble()).toFloat() | |
val t = i / (lines - 1f) | |
val hue = 320 * t + 20 | |
val color = Color.hsv(hue, 0.9f, 0.9f).copy(alpha = 0.4f * sin(PI * t).toFloat()) | |
val inner = Offset( | |
center.x + cos(rad) * radius * 0.2f, | |
center.y + sin(rad) * radius * 0.2f | |
) | |
val outer = Offset( | |
center.x + cos(rad) * radius, | |
center.y + sin(rad) * radius | |
) | |
drawLine( | |
color = color, | |
start = inner, | |
end = outer, | |
strokeWidth = 0.3f, | |
cap = StrokeCap.Butt, | |
blendMode = BlendMode.Plus | |
) | |
} | |
} | |
private fun DrawScope.drawBeam(start: Float, sweep: Float, center: Offset, radius: Float) { | |
val lines = 200 | |
val angleStep = sweep / lines | |
for (i in 0 until lines) { | |
val ang = start + i * angleStep | |
val rad = Math.toRadians(ang.toDouble()).toFloat() | |
val t = i / (lines - 1f) | |
val r = lerp(1.0f, 0.9f, t) | |
val g = 0.95f // nearly constant | |
val b = lerp(0.8f, 1.0f, t) | |
val color = Color(r, g, b, 0.4f * sin(PI * t).toFloat()) | |
val inner = Offset( | |
center.x + cos(rad) * radius * 0.2f, | |
center.y + sin(rad) * radius * 0.2f | |
) | |
val outer = Offset( | |
center.x + cos(rad) * radius, | |
center.y + sin(rad) * radius | |
) | |
drawLine( | |
color = color, | |
start = inner, | |
end = outer, | |
strokeWidth = 0.3f, | |
cap = StrokeCap.Butt, | |
blendMode = BlendMode.Plus | |
) | |
} | |
} | |
private fun lerp(start: Float, end: Float, t: Float): Float = start + t * (end - start) | |
@Preview | |
@Composable | |
fun RealisticCdPreview() { | |
Box( | |
modifier = Modifier | |
.fillMaxSize() | |
.background(Color(0xFF101010)), | |
contentAlignment = Alignment.Center | |
) { | |
RealisticCd() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment