From f0f9120da088abfb1975644c075941ac22981d0f Mon Sep 17 00:00:00 2001 From: "R.Eugenio" Date: Tue, 30 Dec 2025 15:34:43 +0100 Subject: [PATCH] feat(animation): ColorTransition para transiciones suaves de color MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- src/render/animation.zig | 97 ++++++++++++++++++++++++++++++++++++++++ src/zcatgui.zig | 2 + 2 files changed, 99 insertions(+) diff --git a/src/render/animation.zig b/src/render/animation.zig index 7f90d7c..c852d6f 100644 --- a/src/render/animation.zig +++ b/src/render/animation.zig @@ -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" { var hover = HoverTransition{}; diff --git a/src/zcatgui.zig b/src/zcatgui.zig index 97afc31..b019261 100644 --- a/src/zcatgui.zig +++ b/src/zcatgui.zig @@ -109,6 +109,8 @@ pub const lerp = render.animation.lerp; pub const lerpInt = render.animation.lerpInt; pub const Spring = render.animation.Spring; pub const SpringConfig = render.animation.SpringConfig; +pub const HoverTransition = render.animation.HoverTransition; +pub const ColorTransition = render.animation.ColorTransition; // Effects re-exports pub const Shadow = render.effects.Shadow;