Skip to content

Instantly share code, notes, and snippets.

@oleksandrbalan
Created February 8, 2023 16:42
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save oleksandrbalan/ee9ab0c658a740bd6923222a9c5d54b4 to your computer and use it in GitHub Desktop.
Save oleksandrbalan/ee9ab0c658a740bd6923222a9c5d54b4 to your computer and use it in GitHub Desktop.
Gooey effect
import androidx.compose.animation.core.InfiniteRepeatableSpec
import androidx.compose.animation.core.RepeatMode
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.rememberInfiniteTransition
import androidx.compose.animation.core.tween
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.geometry.center
import androidx.compose.ui.graphics.BlendMode
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.compositeOver
import androidx.compose.ui.graphics.drawscope.DrawScope
import kotlin.math.min
@Composable
fun GooeyEffectDemo(modifier: Modifier = Modifier) {
val transition = rememberInfiniteTransition()
val animation by transition.animateFloat(
initialValue = 0f,
targetValue = 1f,
animationSpec = InfiniteRepeatableSpec(
tween(3_000), RepeatMode.Reverse,
)
)
GooeyEffectDemo(
animation = animation,
brush = { size ->
Brush.horizontalGradient(
colors = listOf(Color(0xFF6DD3FF), Color(0xFFFFF281)),
startX = size.width * 0.25f,
endX = size.width * 0.75f
)
},
modifier = modifier.fillMaxSize()
)
}
@Composable
private fun GooeyEffectDemo(
animation: Float,
brush: (Size) -> Brush,
modifier: Modifier = Modifier,
viscosity: Float = 70f,
) {
Canvas(modifier) {
val sideHalf = min(size.width, size.height) / 2f
val radiusBig = sideHalf / 3f
val radiusSmall = radiusBig / 2f
val offset = (sideHalf - radiusBig) * (animation - 0.5f)
drawCircleGradient(
radius = radiusBig,
viscosity = viscosity,
offset = size.center.copy(x = size.center.x - offset)
)
drawCircleGradient(
radius = radiusSmall,
viscosity = viscosity,
offset = size.center.copy(x = size.center.x + offset)
)
drawCircleGradient(
radius = radiusSmall,
viscosity = viscosity,
offset = size.center.copy(y = size.center.y - offset * 1.75f)
)
drawCircleGradient(
radius = radiusSmall,
viscosity = viscosity,
offset = size.center.copy(y = size.center.y + offset * 1.75f)
)
drawRect(
color = Color.Black.copy(alpha = 0.5f).compositeOver(Color.White),
blendMode = BlendMode.ColorDodge
)
drawRect(
color = Color.Black,
blendMode = BlendMode.ColorBurn
)
drawRect(
brush = brush(size),
blendMode = BlendMode.Screen
)
}
}
private fun DrawScope.drawCircleGradient(
radius: Float,
viscosity: Float,
offset: Offset = size.center
) {
val stop1 = (radius - viscosity * 0.5f) / radius
val stop2 = (radius + viscosity * 0.5f) / radius
drawCircle(
Brush.radialGradient(
0.00f to Color.Black,
stop1 to Color.Black,
stop2 to Color.Black.copy(alpha = 0f),
center = offset,
radius = radius + viscosity * 0.5f
),
radius + viscosity * 0.5f,
offset
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment