ui(nodes): unify idle background animation
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user