feat(animation): ColorTransition para transiciones suaves de color
Transiciones Suaves (Acabado Espectacular mejora #4): - Nuevo struct ColorTransition en animation.zig - Interpola colores en ~200ms usando lerp - Se inicializa automáticamente en primer uso - Exportado en zcatgui.zig junto con HoverTransition Uso: state.bg_transition.update(target_color, delta_ms) ctx.drawRect(..., state.bg_transition.current) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
c657583e06
commit
f0f9120da0
2 changed files with 99 additions and 0 deletions
|
|
@ -678,6 +678,103 @@ pub const HoverTransition = struct {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Color Transition (for panel backgrounds)
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
const Style = @import("../core/style.zig");
|
||||||
|
|
||||||
|
/// Smooth color transition for panel backgrounds.
|
||||||
|
/// Interpolates from current to target color over ~200ms.
|
||||||
|
///
|
||||||
|
/// Usage:
|
||||||
|
/// ```zig
|
||||||
|
/// pub const PanelState = struct {
|
||||||
|
/// bg_transition: ColorTransition = .{},
|
||||||
|
/// };
|
||||||
|
///
|
||||||
|
/// // In panel draw:
|
||||||
|
/// const target_color = if (has_focus) focus_color else normal_color;
|
||||||
|
/// state.bg_transition.update(target_color, ctx.delta_ms);
|
||||||
|
/// const display_color = state.bg_transition.current;
|
||||||
|
/// ctx.drawRect(..., display_color);
|
||||||
|
/// ```
|
||||||
|
pub const ColorTransition = struct {
|
||||||
|
/// Current displayed color
|
||||||
|
current: Style.Color = Style.Color.rgb(0, 0, 0),
|
||||||
|
/// Is initialized with a color?
|
||||||
|
initialized: bool = false,
|
||||||
|
/// Transition duration in milliseconds
|
||||||
|
duration_ms: f32 = 200.0,
|
||||||
|
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
/// Update transition towards target color
|
||||||
|
/// Returns true if color changed (for requesting redraw)
|
||||||
|
pub fn update(self: *Self, target: Style.Color, delta_ms: u64) bool {
|
||||||
|
// First call: snap to target immediately
|
||||||
|
if (!self.initialized) {
|
||||||
|
self.current = target;
|
||||||
|
self.initialized = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Already at target?
|
||||||
|
if (self.current.r == target.r and
|
||||||
|
self.current.g == target.g and
|
||||||
|
self.current.b == target.b and
|
||||||
|
self.current.a == target.a)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate interpolation factor (chase towards target)
|
||||||
|
const t = @min(1.0, @as(f32, @floatFromInt(delta_ms)) / self.duration_ms);
|
||||||
|
|
||||||
|
// Interpolate each channel
|
||||||
|
self.current = Style.Color{
|
||||||
|
.r = lerpU8(self.current.r, target.r, t),
|
||||||
|
.g = lerpU8(self.current.g, target.g, t),
|
||||||
|
.b = lerpU8(self.current.b, target.b, t),
|
||||||
|
.a = lerpU8(self.current.a, target.a, t),
|
||||||
|
};
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reset to uninitialized state
|
||||||
|
pub fn reset(self: *Self) void {
|
||||||
|
self.initialized = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Interpolate between two u8 values
|
||||||
|
fn lerpU8(a: u8, b: u8, t: f32) u8 {
|
||||||
|
const af: f32 = @floatFromInt(a);
|
||||||
|
const bf: f32 = @floatFromInt(b);
|
||||||
|
return @intFromFloat(af + (bf - af) * t);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "ColorTransition basic" {
|
||||||
|
var trans = ColorTransition{};
|
||||||
|
const black = Style.Color.rgb(0, 0, 0);
|
||||||
|
const white = Style.Color.rgb(255, 255, 255);
|
||||||
|
|
||||||
|
// First update snaps to target
|
||||||
|
_ = trans.update(black, 16);
|
||||||
|
try std.testing.expect(trans.initialized);
|
||||||
|
try std.testing.expectEqual(@as(u8, 0), trans.current.r);
|
||||||
|
|
||||||
|
// Transition towards white over time
|
||||||
|
_ = trans.update(white, 100); // ~50% transition
|
||||||
|
try std.testing.expect(trans.current.r > 0);
|
||||||
|
try std.testing.expect(trans.current.r < 255);
|
||||||
|
|
||||||
|
// Full duration reaches target
|
||||||
|
_ = trans.update(white, 200);
|
||||||
|
try std.testing.expectEqual(@as(u8, 255), trans.current.r);
|
||||||
|
}
|
||||||
|
|
||||||
test "HoverTransition basic" {
|
test "HoverTransition basic" {
|
||||||
var hover = HoverTransition{};
|
var hover = HoverTransition{};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -109,6 +109,8 @@ pub const lerp = render.animation.lerp;
|
||||||
pub const lerpInt = render.animation.lerpInt;
|
pub const lerpInt = render.animation.lerpInt;
|
||||||
pub const Spring = render.animation.Spring;
|
pub const Spring = render.animation.Spring;
|
||||||
pub const SpringConfig = render.animation.SpringConfig;
|
pub const SpringConfig = render.animation.SpringConfig;
|
||||||
|
pub const HoverTransition = render.animation.HoverTransition;
|
||||||
|
pub const ColorTransition = render.animation.ColorTransition;
|
||||||
|
|
||||||
// Effects re-exports
|
// Effects re-exports
|
||||||
pub const Shadow = render.effects.Shadow;
|
pub const Shadow = render.effects.Shadow;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue