Update border

This commit is contained in:
Kyant0 committed 2025-06-23 20:40:54 +08:00
1 parent 9a1421c1d8
commit 6082a468f6
5 files changed
+63 -129

No files matched your search

+5
View File
@@ -90,5 +90,10 @@ dependencies {
implementation(libs.androidx.compose.ui)
implementation(libs.androidx.compose.ui.graphics)
implementation(libs.androidx.compose.material.ripple)
implementation("androidx.compose.runtime:runtime") {
version {
strictly("1.9.0-alpha04")
}
}
implementation(libs.androidx.graphics.shapes)
}
@@ -1,72 +0,0 @@
package com.kyant.glass.playground
import android.graphics.RenderEffect
import android.graphics.Shader
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedContentScope
import androidx.compose.animation.AnimatedContentTransitionScope
import androidx.compose.animation.ContentTransform
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.spring
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.scaleIn
import androidx.compose.animation.togetherWith
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.asComposeRenderEffect
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.drop
@Composable
fun <S> BlurredAnimatedContent(
targetState: () -> S,
modifier: Modifier = Modifier,
transitionSpec: AnimatedContentTransitionScope<S>.() -> ContentTransform = {
(fadeIn(animationSpec = tween(220, delayMillis = 90)) +
scaleIn(initialScale = 0.92f, animationSpec = tween(220, delayMillis = 90)))
.togetherWith(fadeOut(animationSpec = tween(90)))
},
contentAlignment: Alignment = Alignment.TopStart,
label: String = "AnimatedContent",
contentKey: (targetState: S) -> Any? = { it },
content: @Composable() AnimatedContentScope.(targetState: S) -> Unit
) {
val blurRadius = remember { Animatable(0f) }
LaunchedEffect(Unit) {
snapshotFlow { targetState() }
.drop(1)
.collectLatest {
blurRadius.animateTo(24f, spring(stiffness = 2000f))
blurRadius.animateTo(0f, spring(stiffness = 2000f))
}
}
AnimatedContent(
targetState(),
modifier = modifier.graphicsLayer {
val blurRadiusPx = blurRadius.value.dp.toPx()
if (blurRadiusPx > 0f) {
renderEffect =
RenderEffect.createBlurEffect(
blurRadiusPx,
blurRadiusPx,
Shader.TileMode.DECAL
).asComposeRenderEffect()
}
},
transitionSpec = transitionSpec,
contentAlignment = contentAlignment,
label = label,
contentKey = contentKey,
content = content
)
}
@@ -1,11 +1,10 @@
package com.kyant.glass.playground
import android.graphics.BlendMode
import android.graphics.RenderEffect
import android.graphics.RuntimeShader
import android.graphics.Shader
import androidx.compose.animation.AnimatedContent
import androidx.compose.foundation.Image
import androidx.compose.foundation.border
import androidx.compose.foundation.gestures.detectTransformGestures
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
@@ -20,10 +19,14 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.draw.drawWithCache
import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.graphics.BlendMode
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.asComposeRenderEffect
import androidx.compose.ui.graphics.drawOutline
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.graphics.drawscope.clipRect
import androidx.compose.ui.graphics.drawscope.translate
import androidx.compose.ui.graphics.graphicsLayer
@@ -161,11 +164,20 @@ fun Preview() {
clip = true
shape = RoundedRectangle(state.cornerRadius.value)
}
.border(
width = 2.dp,
color = Color.White.copy(alpha = 0.35f),
shape = RoundedRectangle(state.cornerRadius.value)
)
.drawWithCache { // border
val shape = RoundedRectangle(state.cornerRadius.value)
val outline = shape.createOutline(size, layoutDirection, this)
onDrawWithContent {
drawContent()
drawOutline(
outline = outline,
color = Color.White.copy(alpha = 0.2f),
style = Stroke(3.dp.toPx()),
blendMode = BlendMode.Plus
)
}
}
.graphicsLayer { // white point & chroma boost
val contrast = state.contrast.value
val whitePoint = state.whitePoint.value
@@ -408,7 +420,7 @@ fun Preview() {
RenderEffect.createBlendModeEffect(
blurredBleedRenderEffect,
refractionRenderEffect,
BlendMode.SRC_OVER
android.graphics.BlendMode.SRC_OVER
).asComposeRenderEffect()
} else {
refractionRenderEffect.asComposeRenderEffect()
@@ -470,8 +482,8 @@ fun Preview() {
)
}
BlurredAnimatedContent(
{ state.isInConfigurationMode },
AnimatedContent(
state.isInConfigurationMode,
Modifier
.padding(16.dp)
.safeDrawingPadding()
@@ -1,16 +1,22 @@
package com.kyant.liquidglass
import android.graphics.BlendMode
import android.graphics.RenderEffect
import android.graphics.Shader
import android.os.Build
import androidx.compose.foundation.border
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawWithCache
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.graphics.BlendMode
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.asComposeRenderEffect
import androidx.compose.ui.graphics.drawscope.withTransform
import androidx.compose.ui.graphics.drawOutline
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.graphics.drawscope.translate
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.graphics.layer.drawLayer
import androidx.compose.ui.layout.boundsInRoot
@@ -20,16 +26,13 @@ import androidx.compose.ui.unit.dp
@Composable
fun Modifier.liquidGlass(
style: LiquidGlassStyle,
providerState: LiquidGlassProviderState = LocalLiquidGlassProviderState.current,
state: LiquidGlassState = rememberLiquidGlassState()
providerState: LiquidGlassProviderState = LocalLiquidGlassProviderState.current
): Modifier =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
val shadersCache = remember { LiquidGlassShadersCache() }
var rect: Rect? by remember { mutableStateOf(null) }
this
.border(
width = 1.dp,
color = Color.White.copy(alpha = 0.2f),
shape = style.shape
)
.graphicsLayer {
clip = true
shape = style.shape
@@ -52,7 +55,7 @@ fun Modifier.liquidGlass(
val refractionRenderEffect =
RenderEffect.createChainEffect(
RenderEffect.createRuntimeShaderEffect(
state.refractionShader.apply {
shadersCache.refractionShader.apply {
setFloatUniform("size", size.width, size.height)
setFloatUniform("cornerRadius", cornerRadiusPx)
@@ -72,7 +75,7 @@ fun Modifier.liquidGlass(
val bleedRenderEffect =
RenderEffect.createChainEffect(
RenderEffect.createRuntimeShaderEffect(
state.bleedShader.apply {
shadersCache.bleedShader.apply {
setFloatUniform("size", size.width, size.height)
setFloatUniform("cornerRadius", cornerRadiusPx)
@@ -102,7 +105,7 @@ fun Modifier.liquidGlass(
RenderEffect.createBlendModeEffect(
blurredBleedRenderEffect,
refractionRenderEffect,
BlendMode.SRC_OVER
android.graphics.BlendMode.SRC_OVER
)
} else {
refractionRenderEffect
@@ -115,7 +118,7 @@ fun Modifier.liquidGlass(
if (contrast != 0f || whitePoint != 0f || chromaMultiplier != 1f) {
RenderEffect.createChainEffect(
RenderEffect.createRuntimeShaderEffect(
state.colorManipulationShader.apply {
shadersCache.colorManipulationShader.apply {
setFloatUniform("contrast", contrast)
setFloatUniform("whitePoint", whitePoint)
setFloatUniform("chromaMultiplier", chromaMultiplier)
@@ -128,23 +131,29 @@ fun Modifier.liquidGlass(
refractionAndBleedRenderEffect.asComposeRenderEffect()
}
state.graphicsLayer.renderEffect = renderEffect
val graphicsLayer = obtainGraphicsLayer()
graphicsLayer.renderEffect = renderEffect
val outline = style.shape.createOutline(size, layoutDirection, this)
onDrawBehind {
val rect = state.rect ?: return@onDrawBehind
state.graphicsLayer.record {
withTransform({
clipRect(0f, 0f, size.width, size.height)
translate(-rect.left, -rect.top)
}) {
val rect = rect ?: return@onDrawBehind
graphicsLayer.record {
translate(-rect.left, -rect.top) {
drawLayer(providerState.graphicsLayer)
}
}
drawLayer(state.graphicsLayer)
drawLayer(graphicsLayer)
drawOutline(
outline = outline,
color = Color.White.copy(alpha = 0.2f),
style = Stroke(3.dp.toPx()),
blendMode = BlendMode.Plus
)
}
}
.onGloballyPositioned { layoutCoordinates ->
state.rect = providerState.rect?.let {
rect = providerState.rect?.let {
layoutCoordinates.boundsInRoot().translate(-it.topLeft)
}
}
@@ -3,34 +3,14 @@ package com.kyant.liquidglass
import android.graphics.RuntimeShader
import android.os.Build
import androidx.annotation.RequiresApi
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.graphics.layer.GraphicsLayer
import androidx.compose.ui.graphics.rememberGraphicsLayer
@Composable
fun rememberLiquidGlassState(): LiquidGlassState {
val graphicsLayer = rememberGraphicsLayer()
return remember(graphicsLayer) {
LiquidGlassState(graphicsLayer)
}
}
class LiquidGlassState internal constructor(
internal val graphicsLayer: GraphicsLayer
) {
internal var rect: Rect? by mutableStateOf(null)
internal class LiquidGlassShadersCache() {
private var _colorManipulationShader: RuntimeShader? = null
private var _refractionShader: RuntimeShader? = null
private var _bleedShader: RuntimeShader? = null
internal val colorManipulationShader: RuntimeShader
val colorManipulationShader: RuntimeShader
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
get() {
if (_colorManipulationShader == null) {
@@ -39,7 +19,7 @@ class LiquidGlassState internal constructor(
return _colorManipulationShader!!
}
internal val refractionShader: RuntimeShader
val refractionShader: RuntimeShader
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
get() {
if (_refractionShader == null) {
@@ -48,7 +28,7 @@ class LiquidGlassState internal constructor(
return _refractionShader!!
}
internal val bleedShader: RuntimeShader
val bleedShader: RuntimeShader
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
get() {
if (_bleedShader == null) {