feat: add dynamic blur effect to avatar based on proximity to notch in ProfileMetaballOverlay
This commit is contained in:
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user