A collection of pluggable visual effects for Avalonia controls (namespace: Flowery.Effects), designed for cross-platform compatibility including Browser/WASM.
Add the namespace to your AXAML:
xmlns:fx="clr-namespace:Flowery.Effects;assembly=Flowery.NET"
Entrance animation when element enters the visual tree. Supports 5 modes:
FadeReveal (default): Fades in while sliding into position.<Border fx:RevealBehavior.IsEnabled="True"
fx:RevealBehavior.Duration="0:0:0.6"
fx:RevealBehavior.Direction="Bottom"
fx:RevealBehavior.Distance="50">
<TextBlock Text="I fade in from below!"/>
</Border>
SlideIn: Slides in fully visible (no fade). Good for drawers.
<Border fx:RevealBehavior.Mode="SlideIn" .../>
FadeOnly: Pure fade-in with no movement. Good for overlays.
<Border fx:RevealBehavior.Mode="FadeOnly" fx:RevealBehavior.Duration="0:0:0.8"/>
Scale: Scales up from center (0.8→1) while fading. Good for modals.
<Border fx:RevealBehavior.Mode="Scale" fx:RevealBehavior.Duration="0:0:0.5"/>
ScaleSlide: Combined scale + slide + fade. Maximum impact entrance.
<Border fx:RevealBehavior.Mode="ScaleSlide"
fx:RevealBehavior.Direction="Bottom"
fx:RevealBehavior.Distance="60"/>
| Property | Type | Default | Description |
|---|---|---|---|
IsEnabled |
bool | false | Enable the effect |
Mode |
RevealMode | FadeReveal | FadeReveal, SlideIn, FadeOnly, Scale, ScaleSlide |
Duration |
TimeSpan | 500ms | Animation duration |
Direction |
RevealDirection | Bottom | Origin direction (Top/Bottom/Left/Right) |
Distance |
double | 30 | Slide distance in pixels (for translate modes) |
Easing |
Easing | QuadraticEaseOut | Easing function |
Text scramble/reveal effect on hover or click (mobile-friendly).
RevealOnHover (default): Text starts scrambled and progressively reveals on hover/click. Ideal for teasers, spoilers, or hidden content.<TextBlock Text="Secret Message"
fx:ScrambleHoverBehavior.IsEnabled="True"/>
ScrambleOnHover: Text starts readable and scrambles on hover. Decorative glitch effect.
<TextBlock Text="Glitch Me!"
fx:ScrambleHoverBehavior.IsEnabled="True"
fx:ScrambleHoverBehavior.Mode="ScrambleOnHover"/>
SplitFlap style: Airport departure board effect - characters cycle A to Z until settling.
<TextBlock Text="DEPARTURES"
fx:ScrambleHoverBehavior.IsEnabled="True"
fx:ScrambleHoverBehavior.RevealStyle="SplitFlap"
fx:ScrambleHoverBehavior.Duration="0:0:2.5"/>
| Property | Type | Default | Description |
|---|---|---|---|
IsEnabled |
bool | false | Enable the effect |
Mode |
ScrambleMode | RevealOnHover | RevealOnHover (start scrambled) or ScrambleOnHover (start readable) |
RevealStyle |
RevealStyle | Random | Random (random chars) or SplitFlap (alphabetical cycling like airport boards) |
ScrambleChars |
string | !@#$%^&*()[]{}... |
Characters used for Random style |
Duration |
TimeSpan | 500ms | Time to fully resolve/scramble text |
FrameRate |
int | 30 | Updates per second |
Infinite sine wave animation on the Y axis. Supports both whole-block and per-character modes.
<!-- Per-character ripple wave (SmoothUI style) -->
<TextBlock Text="Ripple!"
fx:WaveTextBehavior.IsEnabled="True"
fx:WaveTextBehavior.IsPerCharacter="True"
fx:WaveTextBehavior.Amplitude="8"
fx:WaveTextBehavior.StaggerDelay="0:0:0.05"/>
<!-- Simple whole-block wave -->
<TextBlock Text="Wave!"
fx:WaveTextBehavior.IsEnabled="True"
fx:WaveTextBehavior.Amplitude="5"/>
| Property | Type | Default | Description |
|---|---|---|---|
IsEnabled |
bool | false | Enable the effect |
IsPerCharacter |
bool | false | Enable character-level ripple wave (replaces Text with Inlines) |
Amplitude |
double | 5 | Maximum vertical movement (pixels) |
Duration |
TimeSpan | 1000ms | Wave cycle duration |
StaggerDelay |
TimeSpan | 50ms | Delay between characters (only for IsPerCharacter) |
Creates a follower element that tracks mouse position with spring physics.
<Panel Background="Transparent"
fx:CursorFollowBehavior.IsEnabled="True"
fx:CursorFollowBehavior.FollowerSize="20"
fx:CursorFollowBehavior.FollowerShape="Circle"
fx:CursorFollowBehavior.FollowerOpacity="0.75"
fx:CursorFollowBehavior.FollowerBrush="{DynamicResource DaisyPrimaryBrush}"/>
| Property | Type | Default | Description |
|---|---|---|---|
IsEnabled |
bool | false | Enable the effect |
FollowerSize |
double | 20 | Size of follower element |
FollowerShape |
FollowerShape | Circle | Circle, Square, or Ring |
FollowerOpacity |
double | 1.0 | Opacity (0.0 - 1.0) |
FollowerBrush |
IBrush | DodgerBlue | Fill/stroke brush |
Stiffness |
double | 0.15 | Spring stiffness (0-1) |
Damping |
double | 0.85 | Velocity damping (0-1) |
Panel (e.g., Grid, Canvas, StackPanel).
Desktop only: This effect is designed for mouse/cursor tracking and is hidden on Android/mobile where touch gestures scroll the page instead.
Container hit testing: If you want tracking to work across a container's full area (not just over child elements), the Panel must have Background="Transparent" (or any brush). Without a background, Avalonia only detects pointer events over rendered content.
<!-- ✅ Correct: Panel receives mouse events everywhere -->
<Panel Background="Transparent" fx:CursorFollowBehavior.IsEnabled="True">
<TextBlock Text="Content"/>
</Panel>
<!-- ❌ Wrong: Mouse events only fire over the TextBlock -->
<Panel fx:CursorFollowBehavior.IsEnabled="True">
<TextBlock Text="Content"/>
</Panel>
Sequential character reveal animation for TextBlock controls. Ideal for terminal simulations, loading messages, or storytelling UI.
<TextBlock Text="System initialized... Ready."
fx:TypewriterBehavior.IsEnabled="True"
fx:TypewriterBehavior.Speed="0:0:0.05"/>
| Property | Type | Default | Description |
|---|---|---|---|
IsEnabled |
bool | false | Enable the effect |
Speed |
TimeSpan | 50ms | Delay between each character |
Automatically re-triggers entrance animations when an element enters the scroll viewport. Works in conjunction with RevealBehavior.
<ScrollViewer>
<StackPanel>
<Border fx:RevealBehavior.IsEnabled="True"
fx:ScrollRevealBehavior.IsEnabled="True">
<TextBlock Text="I reveal when you scroll to me!"/>
</Border>
</StackPanel>
</ScrollViewer>
| Property | Type | Default | Description |
|---|---|---|---|
IsEnabled |
bool | false | Enable viewport-aware reveal |
All effects can be applied or modified at runtime using static helper methods:
using Flowery.Effects;
// Apply RevealBehavior to any Visual
RevealBehavior.SetIsEnabled(myBorder, true);
RevealBehavior.SetDuration(myBorder, TimeSpan.FromMilliseconds(800));
RevealBehavior.SetDirection(myBorder, RevealDirection.Left);
// Manually trigger a reveal animation
RevealBehavior.TriggerReveal(myBorder);
// Apply Typewriter effect programmatically
TypewriterBehavior.SetIsEnabled(myTextBlock, true);
TypewriterBehavior.SetSpeed(myTextBlock, TimeSpan.FromMilliseconds(30));
// Apply ScrollReveal behavior
ScrollRevealBehavior.SetIsEnabled(myElement, true);
// Apply ScrambleHover to a TextBlock (default: RevealOnHover)
ScrambleHoverBehavior.SetIsEnabled(myTextBlock, true);
ScrambleHoverBehavior.SetScrambleChars(myTextBlock, "█▓▒░");
// Use ScrambleOnHover mode for decorative glitch effect
ScrambleHoverBehavior.SetMode(myTextBlock, ScrambleMode.ScrambleOnHover);
// Use SplitFlap style for airport departure board effect
ScrambleHoverBehavior.SetRevealStyle(myTextBlock, RevealStyle.SplitFlap);
// Trigger animation programmatically (for demos)
ScrambleHoverBehavior.TriggerScramble(myTextBlock);
// Apply WaveText to a TextBlock
WaveTextBehavior.SetIsEnabled(myTextBlock, true);
WaveTextBehavior.SetAmplitude(myTextBlock, 10);
// Apply CursorFollow to a Panel
CursorFollowBehavior.SetIsEnabled(myPanel, true);
CursorFollowBehavior.SetFollowerBrush(myPanel, Brushes.Red);
CursorFollowBehavior.SetFollowerShape(myPanel, FollowerShape.Ring);
// Programmatically control cursor follower (for demos/automation)
CursorFollowBehavior.ShowFollower(myPanel);
CursorFollowBehavior.SetTargetPosition(myPanel, 100, 50); // Set target x,y
CursorFollowBehavior.HideFollower(myPanel);
// Animate cursor follower in a figure-8 pattern
private async Task AnimateInfinityPath(Panel panel, CancellationToken ct)
{
double t = 0;
while (!ct.IsCancellationRequested)
{
var w = panel.Bounds.Width;
var h = panel.Bounds.Height;
// Lemniscate of Bernoulli
var sinT = Math.Sin(t);
var cosT = Math.Cos(t);
var denom = 1 + sinT * sinT;
var x = (cosT / denom) * w / 3 + w / 2;
var y = (sinT * cosT / denom) * h / 2 + h / 2;
CursorFollowBehavior.SetTargetPosition(panel, x, y);
t += 0.03;
await Task.Delay(16, ct);
}
}
Core utility for WASM-compatible animations. Use this for custom effects:
using Flowery.Effects;
// Animate a single value
await AnimationHelper.AnimateAsync(
value => element.Opacity = value,
from: 0.0,
to: 1.0,
duration: TimeSpan.FromMilliseconds(500),
easing: new CubicEaseOut());
// Animate with progress callback (t = 0 to 1, eased)
await AnimationHelper.AnimateAsync(
t =>
{
element.Opacity = t;
transform.X = AnimationHelper.Lerp(startX, endX, t);
},
duration: TimeSpan.FromMilliseconds(500),
easing: new CubicEaseInOut());
All effects use manual Task.Delay + Dispatcher.UIThread interpolation instead of Avalonia's declarative Animation keyframes. This pattern ensures consistent behavior across:
// Standard Avalonia Animation - may have WASM issues
var animation = new Animation { Duration = duration, ... };
await animation.RunAsync(target);
// WASM-compatible pattern (used by Flowery.Effects)
for (int i = 0; i <= steps; i++)
{
var t = easing.Ease((double)i / steps);
await Dispatcher.UIThread.InvokeAsync(() => ApplyValue(t));
if (i < steps) await Task.Delay(stepDuration, ct);
}
This library is inspired by smoothui by Eduardo López, a React/Tailwind/Framer Motion component library.
smoothui is licensed under the MIT License.