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) {},
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
// --- LAYER 1: Ambient Outer Halo (Subtle larger glow) ---
|
||||
// This is the ONLY layer that is blurred, providing a soft background glow.
|
||||
// --- LAYER 1: Ambient Outer Halo ---
|
||||
Box(
|
||||
Modifier
|
||||
.size(size * 1.5f)
|
||||
|
|
@ -147,137 +146,143 @@ fun KaizenOrb(
|
|||
.background(
|
||||
Brush.radialGradient(
|
||||
colors = if (recording) {
|
||||
listOf(Color(0xFFE0A23E).copy(alpha = 0.45f), Color.Transparent)
|
||||
listOf(accent.primary.copy(alpha = 0.45f), Color.Transparent)
|
||||
} 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,
|
||||
),
|
||||
)
|
||||
|
||||
// --- LAYER 2: Glass Sphere body with refracted backgrounds & conic caustics ---
|
||||
// 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) ---
|
||||
// --- LAYER 2: Glass body — transparent tint so background bleeds through ---
|
||||
Canvas(modifier = Modifier.fillMaxSize()) {
|
||||
val w = size.toPx()
|
||||
val h = size.toPx()
|
||||
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
|
||||
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!
|
||||
// Base: very subtle theme tint (glass, not paint)
|
||||
drawCircle(
|
||||
brush = Brush.radialGradient(
|
||||
colors = listOf(Color.White, Color.Transparent),
|
||||
center = specularCenter1,
|
||||
radius = radius * 0.38f
|
||||
)
|
||||
colorStops = arrayOf(
|
||||
0.0f to accent.primary.copy(alpha = if (isDark) 0.22f else 0.15f),
|
||||
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(
|
||||
brush = Brush.radialGradient(
|
||||
colors = listOf(
|
||||
Color(0xFFFFD180).copy(alpha = 0.80f), // Bright warm amber light refraction
|
||||
Color.Transparent
|
||||
accent.primary.copy(alpha = if (isDark) 0.50f else 0.35f),
|
||||
Color.Transparent,
|
||||
),
|
||||
center = specularCenter2,
|
||||
radius = radius * 0.28f
|
||||
)
|
||||
center = Offset(w * 0.72f + specShiftX, h * 0.80f + specShiftY),
|
||||
radius = radius * 0.25f,
|
||||
),
|
||||
)
|
||||
|
||||
// 3. Ultra-sharp outer rim (1px hairline highlight for crisp crystal reflection)
|
||||
// Crisp rim hairline
|
||||
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,
|
||||
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