Skip to content

Commit ae9363c

Browse files
authored
[Jetlagged] Migrate background AGSL modifier to use Modifier.Node (#1231)
Following our new guidance, the `Modifier.yellowBackground()` was migrated to use `Modifier.Node`. https://developer.android.com/jetpack/compose/custom-modifiers#animating_modifier
2 parents d8d341b + 73755dc commit ae9363c

File tree

1 file changed

+54
-33
lines changed

1 file changed

+54
-33
lines changed

JetLagged/app/src/main/java/com/example/jetlagged/Background.kt

Lines changed: 54 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -19,58 +19,79 @@ package com.example.jetlagged
1919
import android.graphics.Color
2020
import android.graphics.RuntimeShader
2121
import android.os.Build
22+
import androidx.annotation.RequiresApi
2223
import androidx.compose.animation.core.withInfiniteAnimationFrameMillis
23-
import androidx.compose.runtime.getValue
24-
import androidx.compose.runtime.produceState
24+
import androidx.compose.runtime.mutableFloatStateOf
2525
import androidx.compose.ui.Modifier
26-
import androidx.compose.ui.composed
2726
import androidx.compose.ui.draw.drawWithCache
2827
import androidx.compose.ui.graphics.Brush
2928
import androidx.compose.ui.graphics.ShaderBrush
29+
import androidx.compose.ui.graphics.drawscope.ContentDrawScope
30+
import androidx.compose.ui.node.DrawModifierNode
31+
import androidx.compose.ui.node.ModifierNodeElement
3032
import com.example.jetlagged.ui.theme.White
3133
import com.example.jetlagged.ui.theme.Yellow
3234
import com.example.jetlagged.ui.theme.YellowVariant
35+
import kotlinx.coroutines.launch
3336
import org.intellij.lang.annotations.Language
3437

35-
fun Modifier.yellowBackground(): Modifier = this.composed {
36-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
37-
// produce updating time in seconds variable to pass into shader
38-
val time by produceState(0f) {
38+
private data object YellowBackgroundElement : ModifierNodeElement<YellowBackgroundNode>() {
39+
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
40+
override fun create() = YellowBackgroundNode()
41+
override fun update(node: YellowBackgroundNode) {
42+
}
43+
}
44+
45+
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
46+
private class YellowBackgroundNode : DrawModifierNode, Modifier.Node() {
47+
48+
private val shader = RuntimeShader(SHADER)
49+
private val shaderBrush = ShaderBrush(shader)
50+
private val time = mutableFloatStateOf(0f)
51+
52+
init {
53+
shader.setColorUniform(
54+
"color",
55+
Color.valueOf(Yellow.red, Yellow.green, Yellow.blue, Yellow.alpha)
56+
)
57+
}
58+
59+
override fun ContentDrawScope.draw() {
60+
shader.setFloatUniform("resolution", size.width, size.height)
61+
shader.setFloatUniform("time", time.floatValue)
62+
drawRect(shaderBrush)
63+
drawContent()
64+
}
65+
66+
override fun onAttach() {
67+
coroutineScope.launch {
3968
while (true) {
4069
withInfiniteAnimationFrameMillis {
41-
value = it / 1000f
70+
time.floatValue = it / 1000f
4271
}
4372
}
4473
}
45-
Modifier.drawWithCache {
46-
val shader = RuntimeShader(SHADER)
47-
val shaderBrush = ShaderBrush(shader)
48-
shader.setFloatUniform("iResolution", size.width, size.height)
49-
// Pass the color to support color space automatically
50-
shader.setColorUniform(
51-
"iColor",
52-
Color.valueOf(Yellow.red, Yellow.green, Yellow.blue, Yellow.alpha)
53-
)
54-
onDrawBehind {
55-
shader.setFloatUniform("iTime", time)
56-
drawRect(shaderBrush)
57-
}
58-
}
74+
}
75+
}
76+
77+
fun Modifier.yellowBackground(): Modifier =
78+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
79+
this.then(YellowBackgroundElement)
5980
} else {
60-
Modifier.drawWithCache {
81+
drawWithCache {
82+
6183
val gradientBrush = Brush.verticalGradient(listOf(Yellow, YellowVariant, White))
6284
onDrawBehind {
6385
drawRect(gradientBrush)
6486
}
6587
}
6688
}
67-
}
6889

6990
@Language("AGSL")
7091
val SHADER = """
71-
uniform float2 iResolution;
72-
uniform float iTime;
73-
layout(color) uniform half4 iColor;
92+
uniform float2 resolution;
93+
uniform float time;
94+
layout(color) uniform half4 color;
7495
7596
float calculateColorMultiplier(float yCoord, float factor) {
7697
return step(yCoord, 1.0 + factor * 2.0) - step(yCoord, factor - 0.1);
@@ -84,23 +105,23 @@ val SHADER = """
84105
const float energy = 0.6;
85106
86107
// Calculated values
87-
float2 uv = fragCoord / iResolution.xy;
88-
float3 color = iColor.rgb;
89-
float timeOffset = iTime * speedMultiplier;
108+
float2 uv = fragCoord / resolution.xy;
109+
float3 rgbColor = color.rgb;
110+
float timeOffset = time * speedMultiplier;
90111
float hAdjustment = uv.x * 4.3;
91-
float3 loopColor = vec3(1.0 - color.r, 1.0 - color.g, 1.0 - color.b) / loops;
112+
float3 loopColor = vec3(1.0 - rgbColor.r, 1.0 - rgbColor.g, 1.0 - rgbColor.b) / loops;
92113
93114
for (float i = 1.0; i <= loops; i += 1.0) {
94115
float loopFactor = i * 0.1;
95116
float sinInput = (timeOffset + hAdjustment) * energy;
96117
float curve = sin(sinInput) * (1.0 - loopFactor) * 0.05;
97118
float colorMultiplier = calculateColorMultiplier(uv.y, loopFactor);
98-
color += loopColor * colorMultiplier;
119+
rgbColor += loopColor * colorMultiplier;
99120
100121
// Offset for next loop
101122
uv.y += curve;
102123
}
103124
104-
return float4(color, 1.0);
125+
return float4(rgbColor, 1.0);
105126
}
106127
""".trimIndent()

0 commit comments

Comments
 (0)