Skip to content

Instantly share code, notes, and snippets.

@pt2121
Last active October 23, 2022 19:22
Show Gist options
  • Save pt2121/d88a290a6efff4d59e6101700ee846ff to your computer and use it in GitHub Desktop.
Save pt2121/d88a290a6efff4d59e6101700ee846ff to your computer and use it in GitHub Desktop.
@Composable
fun JoyAnim(
color: Color = Color(245, 198, 189, 128),
modifier: Modifier = Modifier.fillMaxSize().background(Color.Black)
) {
var lines by remember {
mutableStateOf(generateLines(0f, 0f))
}
var paths by remember {
mutableStateOf(listOf<Path>())
}
val scope = rememberCoroutineScope()
Sketch(
modifier = modifier.onGloballyPositioned { layoutCoordinates ->
lines = generateLines(layoutCoordinates.size.width.toFloat(), layoutCoordinates.size.height.toFloat(), 14)
},
speed = 0.005f
) { time ->
scope.launch {
withContext(Dispatchers.Default) {
paths = lines.map { points ->
val p = Path().apply {
moveTo(points.first().x, points.first().y)
}
val mid = points.size / 2
points.forEachIndexed { index, offset ->
if (index + 1 <= points.lastIndex) {
val amplify = 1f - ((index - mid).absoluteValue.toFloat() / mid)
val noise = GLM.simplex(Vec3(offset.x, offset.y, time)) * TWO_PI
val sin = sin(time * 2f + noise * 10f)
val random = if (amplify > 0.7) Random.nextDouble(-1.0, 1.0).toFloat() else 0f
val dy = map(sin, -1f, 1f, -10f * amplify, 10f * amplify) + random
val dx = map(sin, -1f, 1f, -2f * amplify, 2f * amplify)
val ctrlX = (offset.x + dx + points[index + 1].x) / 2
val ctrlY = (offset.y + dy + points[index + 1].y) / 2
p.quadraticBezierTo(offset.x + dx, offset.y + dy, ctrlX, ctrlY)
} else {
p.lineTo(offset.x, offset.y)
}
}
p
}
}
}
paths.forEachIndexed { index, p ->
drawPath(
p,
Color.Black,
style = Fill,
)
val percent = 1f - (index.toFloat() / paths.size)
drawPath(
p,
color.copy(alpha = percent.coerceAtLeast(0.4f)),
style = Stroke(2f + 2f * percent),
)
}
}
}
private fun generateLines(
w: Float,
h: Float,
step: Int = 12
): List<List<Offset>> {
val line = mutableListOf<List<Offset>>()
for (i in step..(h.toInt() - step) step step) {
if (i < h * 0.2) continue
val points = mutableListOf<Offset>()
for (j in step..(w.toInt() - step) step step) {
val distanceToCenter = (j - w / 2).absoluteValue
val variance = (w / 2 - 50 - distanceToCenter).coerceAtLeast(0f)
val random = Random.nextDouble(0.0, 1.0).toFloat() * variance / 2 * -1
points.add(Offset(j.toFloat(), i.toFloat() + random))
}
line.add(points)
}
return line
}
private fun generatePaths(
w: Float,
h: Float,
step: Int = 12
): List<Path> {
val paths = mutableListOf<Path>()
for (i in step..(h.toInt() - step) step step) {
if (i < h * 0.3) continue
val p = Path().apply {
moveTo(step.toFloat(), i.toFloat())
}
val points = mutableListOf<Offset>()
for (j in step..(w.toInt() - step) step step) {
val distanceToCenter = (j - w / 2).absoluteValue
val variance = (w / 2 - 50 - distanceToCenter).coerceAtLeast(0f)
val random = Random.nextDouble(0.0, 1.0).toFloat() * variance / 2 * -1
points.add(Offset(j.toFloat(), i.toFloat() + random))
}
points.forEachIndexed { index, offset ->
if (index + 1 <= points.lastIndex) {
val ctrlX = (offset.x + points[index + 1].x) / 2
val ctrlY = (offset.y + points[index + 1].y) / 2
p.quadraticBezierTo(offset.x, offset.y, ctrlX, ctrlY)
} else {
p.lineTo(offset.x, offset.y)
}
}
paths.add(p)
}
return paths
}
@Composable
fun Sketch(
modifier: Modifier = Modifier,
speed: Float = 1f,
animationSpec: AnimationSpec<Float> = tween(5000, 50, easing = LinearEasing),
onDraw: DrawScope.(Float) -> Unit
) {
val advance = remember { AnimationState(0f) }
LaunchedEffect(Unit) {
while (isActive) {
advance.animateTo(
targetValue = advance.value + speed,
animationSpec = animationSpec,
sequentialAnimation = true
)
}
}
Canvas(modifier = modifier) {
onDraw(advance.value)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment