ui(nodes): unify idle background animation

This commit is contained in:
Peter Steinberger
2025-12-18 01:22:14 +01:00
parent d862ae17eb
commit f4b186a9d3
4 changed files with 53 additions and 139 deletions

View File

@@ -147,9 +147,50 @@ private val canvasHtml =
}
html,body { height:100%; margin:0; }
body {
background: transparent;
background: radial-gradient(1200px 900px at 15% 20%, rgba(42, 113, 255, 0.18), rgba(0,0,0,0) 55%),
radial-gradient(900px 700px at 85% 30%, rgba(255, 0, 138, 0.14), rgba(0,0,0,0) 60%),
radial-gradient(1000px 900px at 60% 90%, rgba(0, 209, 255, 0.10), rgba(0,0,0,0) 60%),
#000;
overflow: hidden;
}
body::before {
content:"";
position: fixed;
inset: -20%;
background:
repeating-linear-gradient(0deg, rgba(255,255,255,0.02) 0, rgba(255,255,255,0.02) 1px,
transparent 1px, transparent 48px),
repeating-linear-gradient(90deg, rgba(255,255,255,0.02) 0, rgba(255,255,255,0.02) 1px,
transparent 1px, transparent 48px);
transform: rotate(-7deg);
opacity: 0.45;
pointer-events: none;
animation: clawdis-grid-drift 140s ease-in-out infinite alternate;
}
body::after {
content:"";
position: fixed;
inset: -35%;
background:
radial-gradient(900px 700px at 30% 30%, rgba(42,113,255,0.16), rgba(0,0,0,0) 60%),
radial-gradient(800px 650px at 70% 35%, rgba(255,0,138,0.12), rgba(0,0,0,0) 62%),
radial-gradient(900px 800px at 55% 75%, rgba(0,209,255,0.10), rgba(0,0,0,0) 62%);
filter: blur(28px);
opacity: 0.52;
mix-blend-mode: screen;
pointer-events: none;
animation: clawdis-glow-drift 110s ease-in-out infinite alternate;
}
@keyframes clawdis-grid-drift {
0% { transform: translate3d(-12px, 8px, 0) rotate(-7deg); opacity: 0.40; }
50% { transform: translate3d( 10px,-7px, 0) rotate(-6.6deg); opacity: 0.56; }
100% { transform: translate3d(-8px, 6px, 0) rotate(-7.2deg); opacity: 0.42; }
}
@keyframes clawdis-glow-drift {
0% { transform: translate3d(-18px, 12px, 0) scale(1.02); opacity: 0.40; }
50% { transform: translate3d( 14px,-10px, 0) scale(1.05); opacity: 0.52; }
100% { transform: translate3d(-10px, 8px, 0) scale(1.03); opacity: 0.43; }
}
canvas {
display:block;
width:100vw;
@@ -167,7 +208,7 @@ private val canvasHtml =
text-align: center;
padding: 16px 18px;
border-radius: 14px;
background: rgba(18, 18, 22, 0.70);
background: rgba(18, 18, 22, 0.42);
border: 1px solid rgba(255,255,255,0.08);
box-shadow: 0 18px 60px rgba(0,0,0,0.55);
backdrop-filter: blur(14px);

View File

@@ -1,126 +0,0 @@
package com.steipete.clawdis.node.ui
import androidx.compose.animation.core.RepeatMode
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.infiniteRepeatable
import androidx.compose.animation.core.rememberInfiniteTransition
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.blur
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.BlendMode
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.drawscope.rotate
import androidx.compose.ui.unit.dp
@Composable
fun ClawdisIdleBackground(modifier: Modifier = Modifier) {
val t = rememberInfiniteTransition(label = "clawdis-bg")
val gridX =
t.animateFloat(
initialValue = -18f,
targetValue = 14f,
animationSpec = infiniteRepeatable(animation = tween(durationMillis = 22_000), repeatMode = RepeatMode.Reverse),
label = "gridX",
).value
val gridY =
t.animateFloat(
initialValue = 12f,
targetValue = -10f,
animationSpec = infiniteRepeatable(animation = tween(durationMillis = 22_000), repeatMode = RepeatMode.Reverse),
label = "gridY",
).value
val glowX =
t.animateFloat(
initialValue = -26f,
targetValue = 20f,
animationSpec = infiniteRepeatable(animation = tween(durationMillis = 18_000), repeatMode = RepeatMode.Reverse),
label = "glowX",
).value
val glowY =
t.animateFloat(
initialValue = 18f,
targetValue = -14f,
animationSpec = infiniteRepeatable(animation = tween(durationMillis = 18_000), repeatMode = RepeatMode.Reverse),
label = "glowY",
).value
Box(modifier = modifier.fillMaxSize().background(Color.Black)) {
Canvas(modifier = Modifier.fillMaxSize()) {
val w = size.width
val h = size.height
fun radial(cx: Float, cy: Float, r: Float, color: Color): Brush =
Brush.radialGradient(
colors = listOf(color, Color.Transparent),
center = Offset(cx, cy),
radius = r,
)
drawRect(
brush = radial(w * 0.15f, h * 0.20f, r = maxOf(w, h) * 0.85f, color = Color(0xFF2A71FF).copy(alpha = 0.18f)),
)
drawRect(
brush = radial(w * 0.85f, h * 0.30f, r = maxOf(w, h) * 0.75f, color = Color(0xFFFF008A).copy(alpha = 0.14f)),
)
drawRect(
brush = radial(w * 0.60f, h * 0.90f, r = maxOf(w, h) * 0.85f, color = Color(0xFF00D1FF).copy(alpha = 0.10f)),
)
rotate(degrees = -7f) {
val spacing = 48.dp.toPx()
val line = Color.White.copy(alpha = 0.02f)
val offset = Offset(gridX.dp.toPx(), gridY.dp.toPx())
var x = (-w * 0.6f) + (offset.x % spacing)
while (x < w * 1.6f) {
drawLine(color = line, start = Offset(x, -h * 0.6f), end = Offset(x, h * 1.6f))
x += spacing
}
var y = (-h * 0.6f) + (offset.y % spacing)
while (y < h * 1.6f) {
drawLine(color = line, start = Offset(-w * 0.6f, y), end = Offset(w * 1.6f, y))
y += spacing
}
}
}
// Glow drift layer (closer to iOS WebView scaffold: blur + screen blend).
Canvas(modifier = Modifier.fillMaxSize().blur(28.dp)) {
val w = size.width
val h = size.height
val glowOffset = Offset(glowX.dp.toPx(), glowY.dp.toPx())
fun radial(cx: Float, cy: Float, r: Float, color: Color): Brush =
Brush.radialGradient(
colors = listOf(color, Color.Transparent),
center = Offset(cx, cy),
radius = r,
)
drawRect(
brush = radial(w * 0.30f + glowOffset.x, h * 0.30f + glowOffset.y, r = maxOf(w, h) * 0.75f, color = Color(0xFF2A71FF).copy(alpha = 0.16f)),
blendMode = BlendMode.Screen,
alpha = 0.55f,
)
drawRect(
brush = radial(w * 0.70f + glowOffset.x, h * 0.35f + glowOffset.y, r = maxOf(w, h) * 0.70f, color = Color(0xFFFF008A).copy(alpha = 0.12f)),
blendMode = BlendMode.Screen,
alpha = 0.55f,
)
drawRect(
brush = radial(w * 0.55f + glowOffset.x, h * 0.75f + glowOffset.y, r = maxOf(w, h) * 0.85f, color = Color(0xFF00D1FF).copy(alpha = 0.10f)),
blendMode = BlendMode.Screen,
alpha = 0.55f,
)
}
}
}

View File

@@ -70,7 +70,6 @@ fun RootScreen(viewModel: MainViewModel) {
PackageManager.PERMISSION_GRANTED
Box(modifier = Modifier.fillMaxSize()) {
ClawdisIdleBackground(modifier = Modifier.fillMaxSize())
CanvasView(viewModel = viewModel, modifier = Modifier.fillMaxSize())
}

View File

@@ -130,9 +130,9 @@ final class ScreenController {
repeating-linear-gradient(90deg, rgba(255,255,255,0.02) 0, rgba(255,255,255,0.02) 1px,
transparent 1px, transparent 48px);
transform: rotate(-7deg);
opacity: 0.55;
opacity: 0.45;
pointer-events: none;
animation: clawdis-grid-drift 22s linear infinite;
animation: clawdis-grid-drift 140s ease-in-out infinite alternate;
}
body::after {
content:"";
@@ -143,20 +143,20 @@ final class ScreenController {
radial-gradient(800px 650px at 70% 35%, rgba(255,0,138,0.12), rgba(0,0,0,0) 62%),
radial-gradient(900px 800px at 55% 75%, rgba(0,209,255,0.10), rgba(0,0,0,0) 62%);
filter: blur(28px);
opacity: 0.55;
opacity: 0.52;
mix-blend-mode: screen;
pointer-events: none;
animation: clawdis-glow-drift 18s ease-in-out infinite alternate;
animation: clawdis-glow-drift 110s ease-in-out infinite alternate;
}
@keyframes clawdis-grid-drift {
0% { transform: translate3d(-18px, 12px, 0) rotate(-7deg); opacity: 0.50; }
50% { transform: translate3d( 14px,-10px, 0) rotate(-6.2deg); opacity: 0.62; }
100% { transform: translate3d(-10px, 8px, 0) rotate(-7.4deg); opacity: 0.52; }
0% { transform: translate3d(-12px, 8px, 0) rotate(-7deg); opacity: 0.40; }
50% { transform: translate3d( 10px,-7px, 0) rotate(-6.6deg); opacity: 0.56; }
100% { transform: translate3d(-8px, 6px, 0) rotate(-7.2deg); opacity: 0.42; }
}
@keyframes clawdis-glow-drift {
0% { transform: translate3d(-26px, 18px, 0) scale(1.02); opacity: 0.42; }
50% { transform: translate3d( 20px,-14px, 0) scale(1.05); opacity: 0.55; }
100% { transform: translate3d(-12px, 10px, 0) scale(1.03); opacity: 0.46; }
0% { transform: translate3d(-18px, 12px, 0) scale(1.02); opacity: 0.40; }
50% { transform: translate3d( 14px,-10px, 0) scale(1.05); opacity: 0.52; }
100% { transform: translate3d(-10px, 8px, 0) scale(1.03); opacity: 0.43; }
}
canvas {
display:block;