rewrite orb from opaque painted ball to transparent glass lens
The web orb looks like glass because backdrop-filter refracts the background through it. Android has no backdrop-filter, so the old approach painted an opaque amber ball with harsh colors (#FFF1D6, #8D4F00 at 85% alpha) that looked like a sticker, not glass. New approach: very low alpha accent tint (15-22%) so background blobs bleed through, creating a lens effect. Light source at top-left with subtle white radial, warm accent bleed bottom-right, gentle bottom shadow for volume (not 85% dark brown). Elliptical specular highlight instead of circular. Double rim (outer hairline + inner inset) matching the web's box-shadow approach. Caustic sweep reduced to 25-30% alpha (was 50-65%).
This commit is contained in:
parent
2d57afff61
commit
f7849025d7
1 changed files with 114 additions and 109 deletions
|
|
@ -137,8 +137,7 @@ fun KaizenOrb(
|
||||||
.pointerInput(Unit) {},
|
.pointerInput(Unit) {},
|
||||||
contentAlignment = Alignment.Center
|
contentAlignment = Alignment.Center
|
||||||
) {
|
) {
|
||||||
// --- LAYER 1: Ambient Outer Halo (Subtle larger glow) ---
|
// --- LAYER 1: Ambient Outer Halo ---
|
||||||
// This is the ONLY layer that is blurred, providing a soft background glow.
|
|
||||||
Box(
|
Box(
|
||||||
Modifier
|
Modifier
|
||||||
.size(size * 1.5f)
|
.size(size * 1.5f)
|
||||||
|
|
@ -147,137 +146,143 @@ fun KaizenOrb(
|
||||||
.background(
|
.background(
|
||||||
Brush.radialGradient(
|
Brush.radialGradient(
|
||||||
colors = if (recording) {
|
colors = if (recording) {
|
||||||
listOf(Color(0xFFE0A23E).copy(alpha = 0.45f), Color.Transparent)
|
listOf(accent.primary.copy(alpha = 0.45f), Color.Transparent)
|
||||||
} else {
|
} else {
|
||||||
listOf(accent.primary.copy(alpha = if (streaming) 0.55f else 0.32f), Color.Transparent)
|
listOf(accent.primary.copy(alpha = if (streaming) 0.50f else 0.28f), Color.Transparent)
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
CircleShape,
|
CircleShape,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
// --- LAYER 2: Glass Sphere body with refracted backgrounds & conic caustics ---
|
// --- LAYER 2: Glass body — transparent tint so background bleeds through ---
|
||||||
// DRAWN PERFECTLY SHARP (No blur modifier on the Canvas!) to ensure razor-sharp,
|
|
||||||
// solid circular boundaries that give the orb its heavy, crystal-glass volume.
|
|
||||||
Canvas(
|
|
||||||
modifier = Modifier.fillMaxSize()
|
|
||||||
) {
|
|
||||||
val w = size.toPx()
|
|
||||||
val h = size.toPx()
|
|
||||||
val radius = w / 2f
|
|
||||||
|
|
||||||
// Shift centers slightly in direction of gravity to simulate holographic refractive parallax
|
|
||||||
val centerShiftX = -tiltX * 0.12f * w
|
|
||||||
val centerShiftY = tiltY * 0.12f * h
|
|
||||||
|
|
||||||
// 1. Base Gradient Fill (Refraction glow matching active theme)
|
|
||||||
val baseGradientCenter = Offset(w * 0.35f + centerShiftX, h * 0.30f + centerShiftY)
|
|
||||||
drawCircle(
|
|
||||||
brush = Brush.radialGradient(
|
|
||||||
colorStops = arrayOf(
|
|
||||||
0.0f to Color(0xFFFFF1D6), // Ultra bright glowing cream-white core
|
|
||||||
0.35f to Color(0xFFFFCC80), // Rich luminous warm amber
|
|
||||||
0.75f to accent.primaryDeep, // Saturated accent
|
|
||||||
1.0f to Color(0xFF8D4F00) // Deep bronze-amber shadow edge (creates high contrast!)
|
|
||||||
),
|
|
||||||
center = baseGradientCenter,
|
|
||||||
radius = radius * 1.1f
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
// 2. Heavy 3D Inner Volume Shadow (Sphere Bottom Shading)
|
|
||||||
// Adds massive physical 3D weight by darkening the sphere's lower edge.
|
|
||||||
drawCircle(
|
|
||||||
brush = Brush.radialGradient(
|
|
||||||
colorStops = arrayOf(
|
|
||||||
0.0f to Color.Transparent,
|
|
||||||
0.5f to Color.Transparent,
|
|
||||||
1.0f to Color(0xFF4A2300).copy(alpha = 0.85f) // Deep dark brown shadow edge
|
|
||||||
),
|
|
||||||
center = Offset(w * 0.35f + centerShiftX, h * 0.30f + centerShiftY),
|
|
||||||
radius = radius * 1.0f
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
// Shift inner core in the opposite direction (parallax depth)
|
|
||||||
val coreShiftX = tiltX * 0.05f * w
|
|
||||||
val coreShiftY = -tiltY * 0.05f * h
|
|
||||||
val coreCenter = Offset(w / 2f + coreShiftX, h / 2f + coreShiftY)
|
|
||||||
|
|
||||||
// 3. Conic Caustic Layer #1 (SweepGradient at 30° blended via BlendMode.Overlay)
|
|
||||||
val sweepBrush1 = Brush.sweepGradient(
|
|
||||||
colorStops = arrayOf(
|
|
||||||
0.0f to Color.Transparent,
|
|
||||||
0.16f to accent.primary.copy(alpha = 0.65f),
|
|
||||||
0.33f to Color.White.copy(alpha = 0.55f),
|
|
||||||
0.50f to Color.Transparent,
|
|
||||||
0.66f to accent.primaryDeep.copy(alpha = 0.50f),
|
|
||||||
1.0f to Color.Transparent
|
|
||||||
),
|
|
||||||
center = coreCenter
|
|
||||||
)
|
|
||||||
drawCircle(
|
|
||||||
brush = sweepBrush1,
|
|
||||||
radius = radius * 0.84f,
|
|
||||||
blendMode = BlendMode.Overlay
|
|
||||||
)
|
|
||||||
|
|
||||||
// 4. Conic Caustic Layer #2 (Secondary parallax SweepGradient at 200° via BlendMode.Softlight)
|
|
||||||
val sweepBrush2 = Brush.sweepGradient(
|
|
||||||
colorStops = arrayOf(
|
|
||||||
0.0f to Color.Transparent,
|
|
||||||
0.25f to Color.White.copy(alpha = 0.40f),
|
|
||||||
0.50f to Color.Transparent,
|
|
||||||
0.75f to accent.primary.copy(alpha = 0.50f),
|
|
||||||
1.0f to Color.Transparent
|
|
||||||
),
|
|
||||||
center = coreCenter
|
|
||||||
)
|
|
||||||
drawCircle(
|
|
||||||
brush = sweepBrush2,
|
|
||||||
radius = radius * 0.72f,
|
|
||||||
blendMode = BlendMode.Softlight
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- LAYER 3: Specular Highlights & Crisp Rim (Sharp reflections) ---
|
|
||||||
Canvas(modifier = Modifier.fillMaxSize()) {
|
Canvas(modifier = Modifier.fillMaxSize()) {
|
||||||
val w = size.toPx()
|
val w = size.toPx()
|
||||||
val h = size.toPx()
|
val h = size.toPx()
|
||||||
val radius = w / 2f
|
val radius = w / 2f
|
||||||
|
val shiftX = -tiltX * 0.12f * w
|
||||||
|
val shiftY = tiltY * 0.12f * h
|
||||||
|
val lightCenter = Offset(w * 0.30f + shiftX, h * 0.25f + shiftY)
|
||||||
|
|
||||||
// Specular shifts with gravity to give a high-gloss interactive reflection
|
// Base: very subtle theme tint (glass, not paint)
|
||||||
val specularShiftX = -tiltX * 0.08f * w
|
|
||||||
val specularShiftY = tiltY * 0.08f * h
|
|
||||||
val specularCenter1 = Offset(w * 0.28f + specularShiftX, h * 0.22f + specularShiftY)
|
|
||||||
val specularCenter2 = Offset(w * 0.70f + specularShiftX, h * 0.82f + specularShiftY)
|
|
||||||
|
|
||||||
// 1. Primary "wet" gloss highlight (top-left) - extremely bright, solid, and sharp!
|
|
||||||
drawCircle(
|
drawCircle(
|
||||||
brush = Brush.radialGradient(
|
brush = Brush.radialGradient(
|
||||||
colors = listOf(Color.White, Color.Transparent),
|
colorStops = arrayOf(
|
||||||
center = specularCenter1,
|
0.0f to accent.primary.copy(alpha = if (isDark) 0.22f else 0.15f),
|
||||||
radius = radius * 0.38f
|
0.6f to accent.primaryDeep.copy(alpha = if (isDark) 0.18f else 0.10f),
|
||||||
)
|
1.0f to accent.primaryDeep.copy(alpha = if (isDark) 0.30f else 0.20f),
|
||||||
|
),
|
||||||
|
center = lightCenter,
|
||||||
|
radius = radius * 1.1f,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
// 2. Secondary ambient bounce highlight (bottom-right edge glow)
|
// Top-left brightness (light source)
|
||||||
|
drawCircle(
|
||||||
|
brush = Brush.radialGradient(
|
||||||
|
colorStops = arrayOf(
|
||||||
|
0.0f to Color.White.copy(alpha = if (isDark) 0.25f else 0.40f),
|
||||||
|
0.5f to Color.White.copy(alpha = 0.05f),
|
||||||
|
1.0f to Color.Transparent,
|
||||||
|
),
|
||||||
|
center = lightCenter,
|
||||||
|
radius = radius * 0.8f,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Bottom-right accent bleed
|
||||||
|
drawCircle(
|
||||||
|
brush = Brush.radialGradient(
|
||||||
|
colorStops = arrayOf(
|
||||||
|
0.0f to accent.primary.copy(alpha = if (isDark) 0.45f else 0.35f),
|
||||||
|
0.6f to Color.Transparent,
|
||||||
|
),
|
||||||
|
center = Offset(w * 0.72f, h * 0.78f),
|
||||||
|
radius = radius * 0.65f,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Bottom shadow for volume (subtle, not harsh)
|
||||||
|
drawCircle(
|
||||||
|
brush = Brush.radialGradient(
|
||||||
|
colorStops = arrayOf(
|
||||||
|
0.0f to Color.Transparent,
|
||||||
|
0.55f to Color.Transparent,
|
||||||
|
0.85f to Color.Black.copy(alpha = if (isDark) 0.20f else 0.08f),
|
||||||
|
1.0f to Color.Black.copy(alpha = if (isDark) 0.35f else 0.15f),
|
||||||
|
),
|
||||||
|
center = Offset(w * 0.45f, h * 0.40f),
|
||||||
|
radius = radius,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Caustic conic layer (depth structure)
|
||||||
|
val coreShiftX = tiltX * 0.05f * w
|
||||||
|
val coreShiftY = -tiltY * 0.05f * h
|
||||||
|
val coreCenter = Offset(w / 2f + coreShiftX, h / 2f + coreShiftY)
|
||||||
|
drawCircle(
|
||||||
|
brush = Brush.sweepGradient(
|
||||||
|
colorStops = arrayOf(
|
||||||
|
0.0f to Color.Transparent,
|
||||||
|
0.16f to accent.primary.copy(alpha = 0.30f),
|
||||||
|
0.33f to Color.White.copy(alpha = 0.20f),
|
||||||
|
0.50f to Color.Transparent,
|
||||||
|
0.66f to accent.primaryDeep.copy(alpha = 0.25f),
|
||||||
|
1.0f to Color.Transparent,
|
||||||
|
),
|
||||||
|
center = coreCenter,
|
||||||
|
),
|
||||||
|
radius = radius * 0.84f,
|
||||||
|
blendMode = BlendMode.Overlay,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- LAYER 3: Specular highlights + rim ---
|
||||||
|
Canvas(modifier = Modifier.fillMaxSize()) {
|
||||||
|
val w = size.toPx()
|
||||||
|
val h = size.toPx()
|
||||||
|
val radius = w / 2f
|
||||||
|
val specShiftX = -tiltX * 0.08f * w
|
||||||
|
val specShiftY = tiltY * 0.08f * h
|
||||||
|
|
||||||
|
// Primary specular — elliptical for realism
|
||||||
|
drawOval(
|
||||||
|
brush = Brush.radialGradient(
|
||||||
|
colors = listOf(
|
||||||
|
Color.White.copy(alpha = if (isDark) 0.70f else 0.90f),
|
||||||
|
Color.Transparent,
|
||||||
|
),
|
||||||
|
center = Offset(w * 0.30f + specShiftX, h * 0.24f + specShiftY),
|
||||||
|
radius = radius * 0.35f,
|
||||||
|
),
|
||||||
|
topLeft = Offset(w * 0.12f + specShiftX, h * 0.10f + specShiftY),
|
||||||
|
size = androidx.compose.ui.geometry.Size(radius * 0.7f, radius * 0.55f),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Secondary bounce (bottom-right, warm)
|
||||||
drawCircle(
|
drawCircle(
|
||||||
brush = Brush.radialGradient(
|
brush = Brush.radialGradient(
|
||||||
colors = listOf(
|
colors = listOf(
|
||||||
Color(0xFFFFD180).copy(alpha = 0.80f), // Bright warm amber light refraction
|
accent.primary.copy(alpha = if (isDark) 0.50f else 0.35f),
|
||||||
Color.Transparent
|
Color.Transparent,
|
||||||
),
|
),
|
||||||
center = specularCenter2,
|
center = Offset(w * 0.72f + specShiftX, h * 0.80f + specShiftY),
|
||||||
radius = radius * 0.28f
|
radius = radius * 0.25f,
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
// 3. Ultra-sharp outer rim (1px hairline highlight for crisp crystal reflection)
|
// Crisp rim hairline
|
||||||
drawCircle(
|
drawCircle(
|
||||||
color = Color.White.copy(alpha = if (isDark) 0.12f else 0.45f),
|
color = Color.White.copy(alpha = if (isDark) 0.10f else 0.30f),
|
||||||
radius = radius - 0.5f,
|
radius = radius - 0.5f,
|
||||||
style = Stroke(width = 1.5f)
|
style = Stroke(width = 1f),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Inner rim (inset edge for refraction feel)
|
||||||
|
drawCircle(
|
||||||
|
color = Color.White.copy(alpha = if (isDark) 0.05f else 0.12f),
|
||||||
|
radius = radius - 2f,
|
||||||
|
style = Stroke(width = 0.5f),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue