├── .gitattributes ├── .github └── workflows │ └── dotnetcore.yml ├── AvaloniaRipple.sln ├── AvaloniaRipple ├── App.xaml ├── App.xaml.cs ├── AvaloniaRipple.csproj ├── MainWindow.xaml ├── MainWindow.xaml.cs ├── Program.cs └── nuget.config ├── README.md └── Ripple ├── Ripple.csproj └── RippleEffect ├── RippleEffect.cs └── RippleEffect.xaml /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.github/workflows/dotnetcore.yml: -------------------------------------------------------------------------------- 1 | name: .NET Core 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v1 12 | - name: Setup .NET Core 13 | uses: actions/setup-dotnet@v1 14 | with: 15 | dotnet-version: 3.0 16 | - name: Build with dotnet 17 | run: dotnet build --configuration Release 18 | -------------------------------------------------------------------------------- /AvaloniaRipple.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29409.12 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AvaloniaRipple", "AvaloniaRipple\AvaloniaRipple.csproj", "{ECB440AE-658B-4602-87CD-BE7FE8285362}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ripple", "Ripple\Ripple.csproj", "{ECBAA8F5-8837-4F09-A33E-9157EF1C3FE2}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {ECB440AE-658B-4602-87CD-BE7FE8285362}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {ECB440AE-658B-4602-87CD-BE7FE8285362}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {ECB440AE-658B-4602-87CD-BE7FE8285362}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {ECB440AE-658B-4602-87CD-BE7FE8285362}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {ECBAA8F5-8837-4F09-A33E-9157EF1C3FE2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {ECBAA8F5-8837-4F09-A33E-9157EF1C3FE2}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {ECBAA8F5-8837-4F09-A33E-9157EF1C3FE2}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {ECBAA8F5-8837-4F09-A33E-9157EF1C3FE2}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {CF585909-D4B7-4ABC-B2B5-EF4699EF6943} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /AvaloniaRipple/App.xaml: -------------------------------------------------------------------------------- 1 |  5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /AvaloniaRipple/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Markup.Xaml; 3 | 4 | namespace AvaloniaRipple 5 | { 6 | public class App : Application 7 | { 8 | public override void Initialize() 9 | { 10 | AvaloniaXamlLoader.Load(this); 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /AvaloniaRipple/AvaloniaRipple.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | winExe 4 | netcoreapp3.0 5 | 6 | 7 | 8 | %(Filename) 9 | 10 | 11 | Designer 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /AvaloniaRipple/MainWindow.xaml: -------------------------------------------------------------------------------- 1 |  12 | 15 | 17 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /AvaloniaRipple/MainWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Markup.Xaml; 4 | 5 | namespace AvaloniaRipple 6 | { 7 | public class MainWindow : Window 8 | { 9 | public MainWindow() 10 | { 11 | InitializeComponent(); 12 | #if DEBUG 13 | this.AttachDevTools(); 14 | #endif 15 | } 16 | 17 | private void InitializeComponent() 18 | { 19 | AvaloniaXamlLoader.Load(this); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /AvaloniaRipple/Program.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Logging.Serilog; 4 | 5 | namespace AvaloniaRipple 6 | { 7 | internal class Program 8 | { 9 | // Initialization code. Don't use any Avalonia, third-party APIs or any 10 | // SynchronizationContext-reliant code before AppMain is called: things aren't initialized 11 | // yet and stuff might break. 12 | public static void Main(string[] args) => BuildAvaloniaApp().Start(AppMain, args); 13 | 14 | // Avalonia configuration, don't remove; also used by visual designer. 15 | public static AppBuilder BuildAvaloniaApp() 16 | => AppBuilder.Configure() 17 | .UsePlatformDetect() 18 | .LogToDebug(); 19 | 20 | // Your application's entry point. Here you can initialize your MVVM framework, DI 21 | // container, etc. 22 | private static void AppMain(Application app, string[] args) 23 | { 24 | app.Run(new MainWindow()); 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /AvaloniaRipple/nuget.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Avalonia Ripple Effect 2 | 3 | [![N|Solid](https://i.imgur.com/SJo17KT.gif)](https://i.imgur.com/SJo17KT.gif) 4 | 5 | Sample of ripple effect using Avalonia. 6 | All things which wrapped in RippleEffect will be effected 7 | 8 | ```sh 9 | 13 | 15 | 20 | 21 | 22 | ``` 23 | -------------------------------------------------------------------------------- /Ripple/Ripple.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.0 5 | 6 | 7 | 8 | 9 | Designer 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Ripple/RippleEffect/RippleEffect.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Animation; 3 | using Avalonia.Controls; 4 | using Avalonia.Controls.Primitives; 5 | using Avalonia.Controls.Shapes; 6 | using Avalonia.Media; 7 | using Avalonia.Styling; 8 | using System; 9 | 10 | namespace Ripple 11 | { 12 | public class RippleEffect : ContentControl 13 | { 14 | #region Styled properties 15 | 16 | public static readonly StyledProperty RippleFillProperty = 17 | AvaloniaProperty.Register(nameof(RippleFill)); 18 | 19 | public Brush RippleFill 20 | { 21 | get { return GetValue(RippleFillProperty); } 22 | set { SetValue(RippleFillProperty, value); } 23 | } 24 | 25 | public static readonly StyledProperty RippleOpacityProperty = 26 | AvaloniaProperty.Register(nameof(RippleOpacity)); 27 | 28 | public double RippleOpacity 29 | { 30 | get { return GetValue(RippleOpacityProperty); } 31 | set { SetValue(RippleOpacityProperty, value); } 32 | } 33 | 34 | #endregion Styled properties 35 | 36 | private Ellipse _circle; 37 | private Animation _ripple; 38 | private Point _pointer; 39 | private IAnimationSetter _toWidth; 40 | private IAnimationSetter _fromMargin; 41 | private IAnimationSetter _toMargin; 42 | private bool _isRunning; 43 | 44 | public RippleEffect() 45 | { 46 | this.AddHandler(PointerPressedEvent, async (s, e) => 47 | { 48 | if (_isRunning) 49 | { 50 | return; 51 | } 52 | _pointer = e.GetPosition(this); 53 | _isRunning = true; 54 | var maxWidth = Math.Max(Bounds.Width, Bounds.Height) * 2.2D; 55 | _toWidth.Value = maxWidth; 56 | _fromMargin.Value = _circle.Margin = new Thickness(_pointer.X, _pointer.Y, 0, 0); 57 | _toMargin.Value = new Thickness(_pointer.X - maxWidth / 2, _pointer.Y - maxWidth / 2, 0, 0); 58 | 59 | await _ripple.RunAsync(_circle); 60 | 61 | _isRunning = false; 62 | }); 63 | } 64 | 65 | protected override void OnTemplateApplied(TemplateAppliedEventArgs e) 66 | { 67 | base.OnTemplateApplied(e); 68 | 69 | _circle = e.NameScope.Find("Circle"); 70 | 71 | var style = _circle.Styles[0] as Style; 72 | _ripple = style.Animations[0] as Animation; 73 | _toWidth = _ripple.Children[1].Setters[1]; 74 | _fromMargin = _ripple.Children[0].Setters[0]; 75 | _toMargin = _ripple.Children[1].Setters[0]; 76 | 77 | style.Animations.Remove(_ripple); 78 | } 79 | } 80 | } -------------------------------------------------------------------------------- /Ripple/RippleEffect/RippleEffect.xaml: -------------------------------------------------------------------------------- 1 |  4 | 5 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | --------------------------------------------------------------------------------