Update border
This commit is contained in:
1 parent
9a1421c1d8
commit
6082a468f6
5 files changed
+63
-129
No files matched your search
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
+4
-24
@@ -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) {
|
||||
Reference in new issue
Block a user