feat: add dynamic blur effect to avatar based on proximity to notch in ProfileMetaballOverlay

This commit is contained in:
k1ngsterr1
2026-02-01 03:54:01 +05:00
parent a61887cc5b
commit 67920cf695

View File

@@ -260,6 +260,16 @@ fun ProfileMetaballOverlay(
// Should we draw the connector? (when avatar gets close enough) // Should we draw the connector? (when avatar gets close enough)
val showConnector = collapseProgress > 0.3f && avatarState.showBlob && distance < maxDist * 2f val showConnector = collapseProgress > 0.3f && avatarState.showBlob && distance < maxDist * 2f
// Calculate blur intensity for avatar based on distance to notch (like Telegram)
// When avatar is far - no blur, when close - more blur
val avatarBlurRadius = if (showConnector) {
// More blur as avatar gets closer to notch
val nearProgress = (1f - (distance / maxDist).coerceIn(0f, 1f))
(nearProgress * ProfileMetaballConstants.BLUR_RADIUS * 0.5f).coerceIn(0f, 10f)
} else {
0f
}
Box(modifier = modifier.fillMaxSize()) { Box(modifier = modifier.fillMaxSize()) {
// LAYER 1: Metaball shapes with blur effect (BLACK shapes only) // LAYER 1: Metaball shapes with blur effect (BLACK shapes only)
Canvas( Canvas(
@@ -267,18 +277,16 @@ fun ProfileMetaballOverlay(
.fillMaxSize() .fillMaxSize()
.graphicsLayer { .graphicsLayer {
metaShader.setFloatUniform("cutoff", ProfileMetaballConstants.CUTOFF) metaShader.setFloatUniform("cutoff", ProfileMetaballConstants.CUTOFF)
renderEffect = RenderEffect // IMPORTANT: First blur, THEN threshold shader
.createBlurEffect( // createChainEffect(outer, inner) - inner is applied first
ProfileMetaballConstants.BLUR_RADIUS, val blurEffect = RenderEffect.createBlurEffect(
ProfileMetaballConstants.BLUR_RADIUS, ProfileMetaballConstants.BLUR_RADIUS,
Shader.TileMode.DECAL ProfileMetaballConstants.BLUR_RADIUS,
) Shader.TileMode.DECAL
.let { blurEffect -> )
RenderEffect.createChainEffect( val thresholdEffect = RenderEffect.createRuntimeShaderEffect(metaShader, "composable")
RenderEffect.createRuntimeShaderEffect(metaShader, "composable"), // Chain: blur first (inner), then threshold (outer)
blurEffect renderEffect = RenderEffect.createChainEffect(thresholdEffect, blurEffect)
)
}
.asComposeRenderEffect() .asComposeRenderEffect()
} }
) { ) {
@@ -343,7 +351,7 @@ fun ProfileMetaballOverlay(
} }
} }
// LAYER 2: Actual avatar content (NO BLUR!) - rendered on top // LAYER 2: Actual avatar content - with blur when close to notch
if (avatarState.showBlob) { if (avatarState.showBlob) {
val avatarSizeDp = with(density) { (avatarState.radius * 2f).toDp() } val avatarSizeDp = with(density) { (avatarState.radius * 2f).toDp() }
val avatarOffsetX = with(density) { (avatarState.centerX - avatarState.radius).toDp() } val avatarOffsetX = with(density) { (avatarState.centerX - avatarState.radius).toDp() }
@@ -357,6 +365,14 @@ fun ProfileMetaballOverlay(
.clip(RoundedCornerShape(avatarSizeDp / 2)) .clip(RoundedCornerShape(avatarSizeDp / 2))
.graphicsLayer { .graphicsLayer {
alpha = avatarState.opacity alpha = avatarState.opacity
// Apply blur to avatar when close to notch (like Telegram)
if (avatarBlurRadius > 0.5f) {
renderEffect = RenderEffect.createBlurEffect(
avatarBlurRadius,
avatarBlurRadius,
Shader.TileMode.DECAL
).asComposeRenderEffect()
}
}, },
contentAlignment = Alignment.Center, contentAlignment = Alignment.Center,
content = avatarContent content = avatarContent