diff --git a/Maui.FreakyUXKit/Maui.FreakyUXKit/Coachmark/CoachmarkAnimationStyle.cs b/Maui.FreakyUXKit/Maui.FreakyUXKit/Coachmark/CoachmarkAnimationStyle.cs
index 32ddf2a..ec2da82 100644
--- a/Maui.FreakyUXKit/Maui.FreakyUXKit/Coachmark/CoachmarkAnimationStyle.cs
+++ b/Maui.FreakyUXKit/Maui.FreakyUXKit/Coachmark/CoachmarkAnimationStyle.cs
@@ -9,4 +9,4 @@ public enum CoachmarkAnimationStyle
Spotlight,
Arrow,
Morph
-}
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/Maui.FreakyUXKit/Maui.FreakyUXKit/Coachmark/FreakyPopupPage.xaml.cs b/Maui.FreakyUXKit/Maui.FreakyUXKit/Coachmark/FreakyPopupPage.xaml.cs
index 16cd679..273b114 100644
--- a/Maui.FreakyUXKit/Maui.FreakyUXKit/Coachmark/FreakyPopupPage.xaml.cs
+++ b/Maui.FreakyUXKit/Maui.FreakyUXKit/Coachmark/FreakyPopupPage.xaml.cs
@@ -156,8 +156,8 @@ private void OnPaintSurface(object sender, SKPaintSurfaceEventArgs e)
var info = e.Info;
var rect = info.Rect;
var highlightRect = _currentBounds;
- float highX = highlightRect.Left + highlightRect.Width / 2;
- float highY = highlightRect.Top + highlightRect.Height / 2;
+ float highX = highlightRect.Left + (highlightRect.Width / 2);
+ float highY = highlightRect.Top + (highlightRect.Height / 2);
if (CurrentTargetView == null || OverlayView == null)
return;
@@ -191,7 +191,6 @@ private void OnPaintSurface(object sender, SKPaintSurfaceEventArgs e)
RenderStaticHighlight(canvas, rect, highX, highY, highlightRect);
break;
}
-
}
private void RenderStaticHighlight(SKCanvas canvas, SKRect rect, float highX, float highY, SKRect highlightRect)
@@ -293,7 +292,7 @@ private void RenderArrowPointer(SKCanvas canvas, SKRect highlightRect, SKRect ov
// Direction vector and distance from overlay center to highlight center
var dx = highlightCenter.X - overlayCenter.X;
var dy = highlightCenter.Y - overlayCenter.Y;
- var totalDistance = MathF.Sqrt(dx * dx + dy * dy);
+ var totalDistance = MathF.Sqrt((dx * dx) + (dy * dy));
if (totalDistance == 0)
return;
@@ -303,18 +302,18 @@ private void RenderArrowPointer(SKCanvas canvas, SKRect highlightRect, SKRect ov
var edgePoint = GetClosestPointOnRect(highlightRect, overlayCenter);
// Distance from overlayCenter to edgePoint
- var distToEdge = MathF.Sqrt((edgePoint.X - overlayCenter.X) * (edgePoint.X - overlayCenter.X) +
- (edgePoint.Y - overlayCenter.Y) * (edgePoint.Y - overlayCenter.Y));
+ var distToEdge = MathF.Sqrt(((edgePoint.X - overlayCenter.X) * (edgePoint.X - overlayCenter.X)) +
+ ((edgePoint.Y - overlayCenter.Y) * (edgePoint.Y - overlayCenter.Y)));
// Calculate start point - padded 20% away from overlayCenter along direction
var start = new SKPoint(
- overlayCenter.X + direction.X * (totalDistance * paddingPercent),
- overlayCenter.Y + direction.Y * (totalDistance * paddingPercent));
+ overlayCenter.X + (direction.X * (totalDistance * paddingPercent)),
+ overlayCenter.Y + (direction.Y * (totalDistance * paddingPercent)));
// Calculate end point - 20% *before* edgePoint along the direction vector
var end = new SKPoint(
- overlayCenter.X + direction.X * (distToEdge * (1 - paddingPercent)),
- overlayCenter.Y + direction.Y * (distToEdge * (1 - paddingPercent)));
+ overlayCenter.X + (direction.X * (distToEdge * (1 - paddingPercent))),
+ overlayCenter.Y + (direction.Y * (distToEdge * (1 - paddingPercent))));
canvas.DrawArrow(start, end, ArrowColor.ToSKColor(), ArrowStyle, ArrowStrokeWidth);
}
@@ -399,16 +398,16 @@ private void DrawShapeStroke(SKCanvas canvas, HighlightShape shape, float center
canvas.DrawCircle(centerX, centerY, radius, paint);
break;
case HighlightShape.Ellipse:
- canvas.DrawOval(new SKRect(centerX - width / 2, centerY - height / 2,
- centerX + width / 2, centerY + height / 2), paint);
+ canvas.DrawOval(new SKRect(centerX - (width / 2), centerY - (height / 2),
+ centerX + (width / 2), centerY + (height / 2)), paint);
break;
case HighlightShape.Rectangle:
- canvas.DrawRect(new SKRect(centerX - width / 2, centerY - height / 2,
- centerX + width / 2, centerY + height / 2), paint);
+ canvas.DrawRect(new SKRect(centerX - (width / 2), centerY - (height / 2),
+ centerX + (width / 2), centerY + (height / 2)), paint);
break;
default: // RoundRectangle
- canvas.DrawRoundRect(new SKRoundRect(new SKRect(centerX - width / 2, centerY - height / 2,
- centerX + width / 2, centerY + height / 2), CornerRadius), paint);
+ canvas.DrawRoundRect(new SKRoundRect(new SKRect(centerX - (width / 2), centerY - (height / 2),
+ centerX + (width / 2), centerY + (height / 2)), CornerRadius), paint);
break;
}
}
diff --git a/Maui.FreakyUXKit/Maui.FreakyUXKit/Hosting.cs b/Maui.FreakyUXKit/Maui.FreakyUXKit/Hosting.cs
index 1b3f8c4..27936b5 100644
--- a/Maui.FreakyUXKit/Maui.FreakyUXKit/Hosting.cs
+++ b/Maui.FreakyUXKit/Maui.FreakyUXKit/Hosting.cs
@@ -8,7 +8,6 @@ public static class Hosting
public static MauiAppBuilder UseFreakyUXKit(this MauiAppBuilder builder)
{
builder
- .UseMauiCommunityToolkit()
.UseSkiaSharp();
return builder;
}
diff --git a/Maui.FreakyUXKit/Maui.FreakyUXKit/IntroView/FreakyIntroStep.cs b/Maui.FreakyUXKit/Maui.FreakyUXKit/IntroView/FreakyIntroStep.cs
new file mode 100644
index 0000000..f131dbc
--- /dev/null
+++ b/Maui.FreakyUXKit/Maui.FreakyUXKit/IntroView/FreakyIntroStep.cs
@@ -0,0 +1,29 @@
+using System.Windows.Input;
+using Microsoft.Maui.Controls.Shapes;
+namespace Maui.FreakyUXKit;
+
+public class FreakyIntroStep
+{
+ public string TitleText { get; set; }
+ public FormattedString TitleFormattedText { get; set; }
+ public string SubTitleText { get; set; }
+ public FormattedString SubtitleFormattedText { get; set; }
+ public ImageSource ImageSource { get; set; }
+ public Style ImageStyle { get; set; }
+ public Style TitleLabelStyle { get; set; }
+ public Style SubtitleLabelStyle { get; set; }
+ public string LeftButtonText { get; set; }
+ public ICommand LeftButtonCommand { get; set; }
+ public bool IsLeftButtonVisible { get; set; }
+ public string CenterButtonText { get; set; }
+ public ICommand CenterButtonCommand { get; set; }
+ public bool IsCenterButtonVisible { get; set; }
+ public string RightButtonText { get; set; }
+ public ICommand RightButtonCommand { get; set; }
+ public bool IsRightButtonVisible { get; set; }
+ public Brush BottomBackground { get; set; } = Colors.Transparent;
+ public Brush ImageBackground { get; set; } = Colors.Transparent;
+ public Brush Background { get; set; } = Brush.Transparent;
+ public Color BackgroundAnimationColor { get; set; } = Colors.Transparent;
+ public IShape BottomStrokeShape { get; set; } = new RoundRectangle() { CornerRadius = new CornerRadius(30, 30, 0, 0) };
+}
\ No newline at end of file
diff --git a/Maui.FreakyUXKit/Maui.FreakyUXKit/IntroView/FreakyIntroView.xaml b/Maui.FreakyUXKit/Maui.FreakyUXKit/IntroView/FreakyIntroView.xaml
new file mode 100644
index 0000000..261c159
--- /dev/null
+++ b/Maui.FreakyUXKit/Maui.FreakyUXKit/IntroView/FreakyIntroView.xaml
@@ -0,0 +1,112 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Maui.FreakyUXKit/Maui.FreakyUXKit/IntroView/FreakyIntroView.xaml.cs b/Maui.FreakyUXKit/Maui.FreakyUXKit/IntroView/FreakyIntroView.xaml.cs
new file mode 100644
index 0000000..f87ca3a
--- /dev/null
+++ b/Maui.FreakyUXKit/Maui.FreakyUXKit/IntroView/FreakyIntroView.xaml.cs
@@ -0,0 +1,689 @@
+using System.Windows.Input;
+using Microsoft.Maui.Controls.Shapes;
+using Microsoft.Maui.Layouts;
+using SkiaSharp;
+using SkiaSharp.Views.Maui;
+
+namespace Maui.FreakyUXKit;
+
+public partial class FreakyIntroView
+{
+ private float animationProgress = 0f;
+ private float animationRadius = 0f;
+ private float maxRadius = 0f;
+ private SKColor animationColor = SKColors.Transparent;
+ private bool isAnimating = false;
+ private readonly List particles = new();
+ private float wavePhase = 0f;
+ private DateTime animationStartTime;
+ private float rippleRadius = 0f;
+ private float rippleMaxRadius = 0f;
+ private SKColor rippleColor = SKColors.Transparent;
+ private bool isAnimatingRipple = false;
+
+ private class Particle
+ {
+ public SKPoint Position { get; set; }
+ public SKPoint Velocity { get; set; }
+ public float Life { get; set; } = 1f;
+ public float Size { get; set; }
+ public SKColor Color { get; set; }
+ }
+
+ public FreakyIntroView()
+ {
+ InitializeComponent();
+ }
+
+ public static readonly BindableProperty AnimationTypeProperty =
+ BindableProperty.Create(
+ nameof(AnimationType),
+ typeof(IntroAnimationType),
+ typeof(FreakyIntroView),
+ IntroAnimationType.Ripple);
+ public IntroAnimationType AnimationType
+ {
+ get => (IntroAnimationType)GetValue(AnimationTypeProperty);
+ set => SetValue(AnimationTypeProperty, value);
+ }
+
+ // Animation Duration Property
+ public static readonly BindableProperty AnimationDurationProperty =
+ BindableProperty.Create(
+ nameof(AnimationDuration),
+ typeof(int),
+ typeof(FreakyIntroView),
+ 2000);
+ public int AnimationDuration
+ {
+ get => (int)GetValue(AnimationDurationProperty);
+ set => SetValue(AnimationDurationProperty, value);
+ }
+
+ // Animation Speed Property
+ public static readonly BindableProperty AnimationSpeedProperty =
+ BindableProperty.Create(
+ nameof(AnimationSpeed),
+ typeof(double),
+ typeof(FreakyIntroView),
+ 1.0);
+ public double AnimationSpeed
+ {
+ get => (double)GetValue(AnimationSpeedProperty);
+ set => SetValue(AnimationSpeedProperty, value);
+ }
+
+ public static readonly BindableProperty ItemsSourceProperty =
+ BindableProperty.Create(
+ nameof(ItemsSource),
+ typeof(IEnumerable),
+ typeof(FreakyIntroView),
+ default(IEnumerable));
+ public IEnumerable ItemsSource
+ {
+ get => (IEnumerable)GetValue(ItemsSourceProperty);
+ set => SetValue(ItemsSourceProperty, value);
+ }
+
+ public static readonly BindableProperty PositionProperty =
+ BindableProperty.Create(
+ nameof(Position),
+ typeof(int),
+ typeof(FreakyIntroView),
+ default(int));
+ public int Position
+ {
+ get => (int)GetValue(PositionProperty);
+ set => SetValue(PositionProperty, value);
+ }
+
+ public static readonly BindableProperty IsSwipeEnabledProperty =
+ BindableProperty.Create(
+ nameof(IsSwipeEnabled),
+ typeof(bool),
+ typeof(FreakyIntroView),
+ true);
+ public bool IsSwipeEnabled
+ {
+ get => (bool)GetValue(IsSwipeEnabledProperty);
+ set => SetValue(IsSwipeEnabledProperty, value);
+ }
+
+ public static readonly BindableProperty IsScrollAnimatedProperty =
+ BindableProperty.Create(
+ nameof(IsScrollAnimated),
+ typeof(bool),
+ typeof(FreakyIntroView),
+ false);
+ public bool IsScrollAnimated
+ {
+ get => (bool)GetValue(IsScrollAnimatedProperty);
+ set => SetValue(IsScrollAnimatedProperty, value);
+ }
+
+ public static readonly BindableProperty ImageSourceProperty =
+ BindableProperty.Create(
+ nameof(ImageSource),
+ typeof(ImageSource),
+ typeof(FreakyIntroView),
+ default);
+ public ImageSource ImageSource
+ {
+ get => (ImageSource)GetValue(ImageSourceProperty);
+ set => SetValue(ImageSourceProperty, value);
+ }
+
+ public static readonly BindableProperty ImageStyleProperty =
+ BindableProperty.Create(
+ nameof(ImageStyle),
+ typeof(Style),
+ typeof(FreakyIntroView),
+ default(Style));
+ public Style ImageStyle
+ {
+ get => (Style)GetValue(ImageStyleProperty);
+ set => SetValue(ImageStyleProperty, value);
+ }
+
+ public static readonly BindableProperty TitleTextProperty =
+ BindableProperty.Create(
+ nameof(TitleText),
+ typeof(string),
+ typeof(FreakyIntroView),
+ default(string));
+ public string TitleText
+ {
+ get => (string)GetValue(TitleTextProperty);
+ set => SetValue(TitleTextProperty, value);
+ }
+
+ public static readonly BindableProperty TitleFormattedTextProperty =
+ BindableProperty.Create(
+ nameof(TitleFormattedText),
+ typeof(FormattedString),
+ typeof(FreakyIntroView),
+ default(FormattedString));
+
+ public FormattedString TitleFormattedText
+ {
+ get => (FormattedString)GetValue(TitleFormattedTextProperty);
+ set => SetValue(TitleFormattedTextProperty, value);
+ }
+
+ public static readonly BindableProperty TitleLabelStyleProperty =
+ BindableProperty.Create(
+ nameof(TitleLabelStyle),
+ typeof(Style),
+ typeof(FreakyIntroView),
+ default(Style));
+ public Style TitleLabelStyle
+ {
+ get => (Style)GetValue(TitleLabelStyleProperty);
+ set => SetValue(TitleLabelStyleProperty, value);
+ }
+
+ public static readonly BindableProperty SubTitleTextProperty =
+ BindableProperty.Create(
+ nameof(SubTitleText),
+ typeof(string),
+ typeof(FreakyIntroView),
+ default(string));
+ public string SubTitleText
+ {
+ get => (string)GetValue(SubTitleTextProperty);
+ set => SetValue(SubTitleTextProperty, value);
+ }
+
+ public static readonly BindableProperty SubtitleFormattedTextProperty =
+ BindableProperty.Create(
+ nameof(SubtitleFormattedText),
+ typeof(FormattedString),
+ typeof(FreakyIntroView),
+ default(FormattedString));
+
+ public FormattedString SubtitleFormattedText
+ {
+ get => (FormattedString)GetValue(SubtitleFormattedTextProperty);
+ set => SetValue(SubtitleFormattedTextProperty, value);
+ }
+
+ public static readonly BindableProperty SubtitleLabelStyleProperty =
+ BindableProperty.Create(
+ nameof(SubtitleLabelStyle),
+ typeof(Style),
+ typeof(FreakyIntroView),
+ default(Style));
+ public Style SubtitleLabelStyle
+ {
+ get => (Style)GetValue(SubtitleLabelStyleProperty);
+ set => SetValue(SubtitleLabelStyleProperty, value);
+ }
+
+ public static readonly BindableProperty LeftButtonTextProperty =
+ BindableProperty.Create(
+ nameof(LeftButtonText),
+ typeof(string),
+ typeof(FreakyIntroView),
+ default(string));
+ public string LeftButtonText
+ {
+ get => (string)GetValue(LeftButtonTextProperty);
+ set => SetValue(LeftButtonTextProperty, value);
+ }
+
+ public static readonly BindableProperty LeftButtonCommandProperty =
+ BindableProperty.Create(
+ nameof(LeftButtonCommand),
+ typeof(ICommand),
+ typeof(FreakyIntroView),
+ default(ICommand));
+ public ICommand LeftButtonCommand
+ {
+ get => (ICommand)GetValue(LeftButtonCommandProperty);
+ set => SetValue(LeftButtonCommandProperty, value);
+ }
+
+ public static readonly BindableProperty CenterButtonTextProperty =
+ BindableProperty.Create(
+ nameof(CenterButtonText),
+ typeof(string),
+ typeof(FreakyIntroView),
+ default(string));
+ public string CenterButtonText
+ {
+ get => (string)GetValue(CenterButtonTextProperty);
+ set => SetValue(CenterButtonTextProperty, value);
+ }
+
+ public static readonly BindableProperty CenterButtonCommandProperty =
+ BindableProperty.Create(
+ nameof(CenterButtonCommand),
+ typeof(ICommand),
+ typeof(FreakyIntroView),
+ default(ICommand));
+ public ICommand CenterButtonCommand
+ {
+ get => (ICommand)GetValue(CenterButtonCommandProperty);
+ set => SetValue(CenterButtonCommandProperty, value);
+ }
+
+ public static readonly BindableProperty RightButtonTextProperty =
+ BindableProperty.Create(
+ nameof(RightButtonText),
+ typeof(string),
+ typeof(FreakyIntroView),
+ default(string));
+ public string RightButtonText
+ {
+ get => (string)GetValue(RightButtonTextProperty);
+ set => SetValue(RightButtonTextProperty, value);
+ }
+
+ public static readonly BindableProperty RightButtonCommandProperty =
+ BindableProperty.Create(
+ nameof(RightButtonCommand),
+ typeof(ICommand),
+ typeof(FreakyIntroView),
+ default(ICommand));
+ public ICommand RightButtonCommand
+ {
+ get => (ICommand)GetValue(RightButtonCommandProperty);
+ set => SetValue(RightButtonCommandProperty, value);
+ }
+
+ public static readonly BindableProperty ButtonsDirectionProperty =
+ BindableProperty.Create(
+ nameof(ButtonsDirection),
+ typeof(FlexDirection),
+ typeof(FreakyIntroView),
+ FlexDirection.Row);
+ public FlexDirection ButtonsDirection
+ {
+ get => (FlexDirection)GetValue(ButtonsDirectionProperty);
+ set => SetValue(ButtonsDirectionProperty, value);
+ }
+
+ public static readonly BindableProperty IndicatorColorProperty =
+ BindableProperty.Create(
+ nameof(IndicatorColor),
+ typeof(Color),
+ typeof(FreakyIntroView),
+ Colors.White);
+ public Color IndicatorColor
+ {
+ get => (Color)GetValue(IndicatorColorProperty);
+ set => SetValue(IndicatorColorProperty, value);
+ }
+
+ public static readonly BindableProperty IndicatorsShapeProperty =
+ BindableProperty.Create(
+ nameof(IndicatorsShape),
+ typeof(IndicatorShape),
+ typeof(FreakyIntroView),
+ IndicatorShape.Circle);
+ public IndicatorShape IndicatorsShape
+ {
+ get => (IndicatorShape)GetValue(IndicatorsShapeProperty);
+ set => SetValue(IndicatorsShapeProperty, value);
+ }
+
+ public static readonly BindableProperty SelectedIndicatorColorProperty =
+ BindableProperty.Create(
+ nameof(SelectedIndicatorColor),
+ typeof(Color),
+ typeof(FreakyIntroView),
+ Colors.Transparent);
+ public Color SelectedIndicatorColor
+ {
+ get => (Color)GetValue(SelectedIndicatorColorProperty);
+ set => SetValue(SelectedIndicatorColorProperty, value);
+ }
+
+ public static readonly BindableProperty IndicatorMaximumVisibleProperty =
+ BindableProperty.Create(
+ nameof(IndicatorMaximumVisible),
+ typeof(int),
+ typeof(FreakyIntroView),
+ 3);
+ public int IndicatorMaximumVisible
+ {
+ get => (int)GetValue(IndicatorMaximumVisibleProperty);
+ set => SetValue(IndicatorMaximumVisibleProperty, value);
+ }
+
+ public static readonly BindableProperty IndicatorSizeProperty =
+ BindableProperty.Create(
+ nameof(IndicatorSize),
+ typeof(double),
+ typeof(FreakyIntroView),
+ 10.0);
+ public double IndicatorSize
+ {
+ get => (double)GetValue(IndicatorSizeProperty);
+ set => SetValue(IndicatorSizeProperty, value);
+ }
+
+ // Event to notify when the position changes
+ // Changed from int to PositionChangedEventArgs for more detailed information
+ public event EventHandler PositionChanged;
+
+ private void CarouselView_PositionChanged(object sender, PositionChangedEventArgs e)
+ {
+ PositionChanged?.Invoke(this, e);
+ var steps = this.ItemsSource.ElementAtOrDefault(e.CurrentPosition);
+ rippleColor = steps.BackgroundAnimationColor.ToSKColor();
+ // Use original ripple for Ripple type, new system for others
+ if (AnimationType == IntroAnimationType.Ripple)
+ {
+ StartRipple(steps.BackgroundAnimationColor);
+ }
+ else
+ {
+ StartAnimation(steps.BackgroundAnimationColor);
+ }
+ }
+
+ public void StartRipple(Color color)
+ {
+ var canvasSize = canvasView.CanvasSize;
+ rippleColor = color.ToSKColor();
+ rippleRadius = 0;
+ rippleMaxRadius = (float)Math.Sqrt((canvasSize.Width * canvasSize.Width) + (canvasSize.Height * canvasSize.Height));
+ isAnimatingRipple = true;
+
+ Dispatcher.StartTimer(TimeSpan.FromMilliseconds(16), () =>
+ {
+ rippleRadius += 30 * (float)AnimationSpeed; // Apply speed multiplier
+ canvasView.InvalidateSurface();
+
+ if (rippleRadius >= rippleMaxRadius)
+ {
+ isAnimatingRipple = false;
+ BackgroundColor = color; // set final BG
+ canvasView.InvalidateSurface(); // clear ripple
+ return false; // stop timer
+ }
+
+ return true;
+ });
+ }
+
+ public void StartAnimation(Color color)
+ {
+ if (AnimationType == IntroAnimationType.None) return;
+
+ var canvasSize = canvasView.CanvasSize;
+ animationColor = color.ToSKColor();
+ animationProgress = 0f;
+ animationRadius = 0f;
+ maxRadius = (float)Math.Sqrt((canvasSize.Width * canvasSize.Width) + (canvasSize.Height * canvasSize.Height));
+ isAnimating = true;
+ animationStartTime = DateTime.Now;
+
+ // Initialize particles for particle animation
+ if (AnimationType == IntroAnimationType.Particle)
+ {
+ InitializeParticles(canvasSize);
+ }
+
+ var updateInterval = TimeSpan.FromMilliseconds(16); // 60 FPS
+ Dispatcher.StartTimer(updateInterval, () =>
+ {
+ var elapsed = (DateTime.Now - animationStartTime).TotalMilliseconds;
+ animationProgress = Math.Min(1f, (float)(elapsed / (AnimationDuration / AnimationSpeed)));
+
+ UpdateAnimation();
+ canvasView.InvalidateSurface();
+
+ if (animationProgress >= 1f)
+ {
+ CompleteAnimation(color);
+ return false;
+ }
+
+ return true;
+ });
+ }
+
+ private void InitializeParticles(SKSize canvasSize)
+ {
+ particles.Clear();
+ var random = new Random();
+ var particleCount = 50;
+
+ for (int i = 0; i < particleCount; i++)
+ {
+ particles.Add(new Particle
+ {
+ Position = new SKPoint(canvasSize.Width / 2, canvasSize.Height),
+ Velocity = new SKPoint(
+ (float)(random.NextDouble() - 0.5) * 200,
+ (float)((random.NextDouble() * -300) - 100)),
+ Size = (float)((random.NextDouble() * 8) + 2),
+ Color = animationColor,
+ Life = 1f
+ });
+ }
+ }
+
+ private void UpdateAnimation()
+ {
+ switch (AnimationType)
+ {
+ case IntroAnimationType.Ripple:
+ animationRadius = maxRadius * EaseOutCubic(animationProgress);
+ break;
+
+ case IntroAnimationType.Particle:
+ UpdateParticles();
+ break;
+
+ case IntroAnimationType.Wave:
+ wavePhase += 0.1f * (float)AnimationSpeed;
+ break;
+ }
+ }
+
+ private void UpdateParticles()
+ {
+ for (int i = particles.Count - 1; i >= 0; i--)
+ {
+ var particle = particles[i];
+ particle.Position = new SKPoint(
+ particle.Position.X + (particle.Velocity.X * 0.016f),
+ particle.Position.Y + (particle.Velocity.Y * 0.016f));
+ particle.Life -= 0.02f * (float)AnimationSpeed;
+
+ if (particle.Life <= 0)
+ particles.RemoveAt(i);
+ }
+ }
+
+ private void CompleteAnimation(Color color)
+ {
+ isAnimating = false;
+ BackgroundColor = color;
+ particles.Clear();
+ canvasView.InvalidateSurface();
+ }
+
+ private void OnPaintSurface(object sender, SKPaintSurfaceEventArgs e)
+ {
+ var canvas = e.Surface.Canvas;
+ var info = e.Info;
+ canvas.Clear();
+
+ // Handle original ripple animation
+ if (isAnimatingRipple)
+ {
+ var center = new SKPoint(info.Width / 2, info.Height); // bottom center
+
+ using var paint = new SKPaint
+ {
+ Color = rippleColor,
+ Style = SKPaintStyle.Fill,
+ IsAntialias = true
+ };
+
+ canvas.DrawCircle(center, rippleRadius, paint);
+ return;
+ }
+
+ // Handle new animation types
+ if (!isAnimating) return;
+
+ switch (AnimationType)
+ {
+ case IntroAnimationType.Fade:
+ DrawFade(canvas, info);
+ break;
+ case IntroAnimationType.Slide:
+ DrawSlide(canvas, info);
+ break;
+ case IntroAnimationType.Scale:
+ DrawScale(canvas, info);
+ break;
+ case IntroAnimationType.Rotate:
+ DrawRotate(canvas, info);
+ break;
+ case IntroAnimationType.Bounce:
+ DrawBounce(canvas, info);
+ break;
+ case IntroAnimationType.Particle:
+ DrawParticles(canvas, info);
+ break;
+ case IntroAnimationType.Wave:
+ DrawWave(canvas, info);
+ break;
+ }
+ }
+
+ private void DrawRipple(SKCanvas canvas, SKImageInfo info)
+ {
+ var center = new SKPoint(info.Width / 2, info.Height);
+ using var paint = new SKPaint
+ {
+ Color = animationColor.WithAlpha((byte)(255 * (1 - animationProgress))),
+ Style = SKPaintStyle.Fill,
+ IsAntialias = true
+ };
+ canvas.DrawCircle(center, animationRadius, paint);
+ }
+
+ private void DrawFade(SKCanvas canvas, SKImageInfo info)
+ {
+ using var paint = new SKPaint
+ {
+ Color = animationColor.WithAlpha((byte)(255 * animationProgress)),
+ Style = SKPaintStyle.Fill
+ };
+ canvas.DrawRect(0, 0, info.Width, info.Height, paint);
+ }
+
+ private void DrawSlide(SKCanvas canvas, SKImageInfo info)
+ {
+ var slideOffset = info.Width * (1 - EaseOutCubic(animationProgress));
+ using var paint = new SKPaint { Color = animationColor, Style = SKPaintStyle.Fill };
+ canvas.DrawRect(slideOffset, 0, info.Width, info.Height, paint);
+ }
+
+ private void DrawScale(SKCanvas canvas, SKImageInfo info)
+ {
+ var scale = EaseOutBack(animationProgress);
+ var center = new SKPoint(info.Width / 2, info.Height / 2);
+ var size = Math.Max(info.Width, info.Height) * scale;
+
+ canvas.Save();
+ canvas.Translate(center.X, center.Y);
+ canvas.Scale(scale, scale);
+
+ using var paint = new SKPaint { Color = animationColor, Style = SKPaintStyle.Fill };
+ canvas.DrawCircle(0, 0, size / 2, paint);
+ canvas.Restore();
+ }
+
+ private void DrawRotate(SKCanvas canvas, SKImageInfo info)
+ {
+ var rotation = 360 * animationProgress;
+ var center = new SKPoint(info.Width / 2, info.Height / 2);
+
+ canvas.Save();
+ canvas.RotateDegrees(rotation, center.X, center.Y);
+
+ using var paint = new SKPaint { Color = animationColor, Style = SKPaintStyle.Fill };
+ canvas.DrawRect(0, 0, info.Width, info.Height, paint);
+ canvas.Restore();
+ }
+
+ private void DrawBounce(SKCanvas canvas, SKImageInfo info)
+ {
+ var bounceHeight = info.Height * EaseOutBounce(animationProgress);
+ using var paint = new SKPaint { Color = animationColor, Style = SKPaintStyle.Fill };
+ canvas.DrawRect(0, info.Height - bounceHeight, info.Width, bounceHeight, paint);
+ }
+
+ private void DrawParticles(SKCanvas canvas, SKImageInfo info)
+ {
+ using var paint = new SKPaint { Style = SKPaintStyle.Fill, IsAntialias = true };
+
+ foreach (var particle in particles)
+ {
+ paint.Color = particle.Color.WithAlpha((byte)(255 * particle.Life));
+ canvas.DrawCircle(particle.Position, particle.Size, paint);
+ }
+ }
+
+ private void DrawWave(SKCanvas canvas, SKImageInfo info)
+ {
+ using var paint = new SKPaint { Color = animationColor, Style = SKPaintStyle.Fill };
+ using var path = new SKPath();
+
+ var waveHeight = info.Height * animationProgress;
+ var amplitude = 50f;
+ var frequency = 0.01f;
+
+ path.MoveTo(0, info.Height);
+
+ for (int x = 0; x <= info.Width; x += 5)
+ {
+ var y = info.Height - waveHeight + (amplitude * (float)Math.Sin((frequency * x) + wavePhase));
+ path.LineTo(x, y);
+ }
+
+ path.LineTo(info.Width, info.Height);
+ path.Close();
+ canvas.DrawPath(path, paint);
+ }
+
+ // Easing functions
+ private static float EaseOutCubic(float t) => 1f - (float)Math.Pow(1f - t, 3);
+ private static float EaseOutBack(float t)
+ {
+ const float c1 = 1.70158f;
+ const float c3 = c1 + 1f;
+ return 1f + (c3 * (float)Math.Pow(t - 1f, 3)) + (c1 * (float)Math.Pow(t - 1f, 2));
+ }
+ private static float EaseOutBounce(float t)
+ {
+ const float n1 = 7.5625f;
+ const float d1 = 2.75f;
+
+ if (t < 1f / d1) return n1 * t * t;
+ if (t < 2f / d1) return (n1 * (t -= 1.5f / d1) * t) + 0.75f;
+ if (t < 2.5f / d1) return (n1 * (t -= 2.25f / d1) * t) + 0.9375f;
+ return (n1 * (t -= 2.625f / d1) * t) + 0.984375f;
+ }
+}
+
+public enum IntroAnimationType
+{
+ None,
+ Ripple,
+ Fade,
+ Slide,
+ Scale,
+ Rotate,
+ Bounce,
+ Particle,
+ Wave
+}
\ No newline at end of file
diff --git a/Maui.FreakyUXKit/Maui.FreakyUXKit/Maui.FreakyUXKit.csproj b/Maui.FreakyUXKit/Maui.FreakyUXKit/Maui.FreakyUXKit.csproj
index bfde18d..a82ac26 100644
--- a/Maui.FreakyUXKit/Maui.FreakyUXKit/Maui.FreakyUXKit.csproj
+++ b/Maui.FreakyUXKit/Maui.FreakyUXKit/Maui.FreakyUXKit.csproj
@@ -7,7 +7,7 @@
true
enable
disable
-
+ true
FreakyUXKit
latest
FreakyAli
@@ -41,4 +41,10 @@
+
+
+
+ FreakyIntroView.xaml
+
+
\ No newline at end of file
diff --git a/Maui.FreakyUXKit/Maui.FreakyUXKit/SkiaSharpExtensions.cs b/Maui.FreakyUXKit/Maui.FreakyUXKit/SkiaSharpExtensions.cs
index 9b0d4df..17f9fc5 100644
--- a/Maui.FreakyUXKit/Maui.FreakyUXKit/SkiaSharpExtensions.cs
+++ b/Maui.FreakyUXKit/Maui.FreakyUXKit/SkiaSharpExtensions.cs
@@ -19,7 +19,7 @@ public static void DrawRipple(this SKCanvas canvas, float cx, float cy, float ra
using var paint = new SKPaint
{
Style = SKPaintStyle.Stroke,
- Color = color.WithAlpha((byte)(255 * (1 - radius / 300f))),
+ Color = color.WithAlpha((byte)(255 * (1 - (radius / 300f)))),
StrokeWidth = 4,
IsAntialias = true
};
@@ -36,7 +36,7 @@ public static void DrawDarkOverlayWithSpotlight(this SKCanvas canvas, float cx,
BlendMode = SKBlendMode.Clear,
IsAntialias = true
};
- canvas.DrawRoundRect(new SKRect(cx - width / 2, cy - height / 2, cx + width / 2, cy + height / 2), 20, 20, eraserPaint);
+ canvas.DrawRoundRect(new SKRect(cx - (width / 2), cy - (height / 2), cx + (width / 2), cy + (height / 2)), 20, 20, eraserPaint);
}
public static void DrawArrow(this SKCanvas canvas, SKPoint start, SKPoint end, SKColor color, ArrowStyle style, float strokeWidth)
@@ -72,7 +72,7 @@ public static void DrawSketchedArrow(this SKCanvas canvas, SKPoint start, SKPoin
for (int i = 0; i < strokePasses; i++)
{
- float variation = (float)(rand.NextDouble() * 0.6 - 0.3); // +/-30%
+ float variation = (float)((rand.NextDouble() * 0.6) - 0.3); // +/-30%
float strokeWidth = baseStrokeWidth * (1f + variation);
// Slight positional offset for realism
@@ -99,7 +99,7 @@ private static void DrawWobblyLine(SKCanvas canvas, SKPoint start, SKPoint end,
{
var dx = end.X - start.X;
var dy = end.Y - start.Y;
- var length = (float)Math.Sqrt(dx * dx + dy * dy);
+ var length = (float)Math.Sqrt((dx * dx) + (dy * dy));
var direction = new SKPoint(dx / length, dy / length);
int segments = 6;
@@ -109,8 +109,8 @@ private static void DrawWobblyLine(SKCanvas canvas, SKPoint start, SKPoint end,
for (int i = 1; i < segments; i++)
{
float t = i / (float)segments;
- var baseX = start.X + dx * t + offsetX;
- var baseY = start.Y + dy * t + offsetY;
+ var baseX = start.X + (dx * t) + offsetX;
+ var baseY = start.Y + (dy * t) + offsetY;
float wave = (float)(Math.Sin(t * Math.PI * 2) * 1.5f); // subtle wave
float perpX = -direction.Y * wave;
@@ -143,8 +143,8 @@ private static void DrawSketchedArrowHead(SKCanvas canvas, SKPoint start, SKPoin
for (int i = 0; i < 2; i++)
{
- float lengthVariation = (float)(rand.NextDouble() * 4f - 2f);
- float strokeWidth = baseStrokeWidth * (1f + (float)(rand.NextDouble() * 0.3 - 0.15f));
+ float lengthVariation = (float)((rand.NextDouble() * 4f) - 2f);
+ float strokeWidth = baseStrokeWidth * (1f + (float)((rand.NextDouble() * 0.3) - 0.15f));
using var paint = new SKPaint
{
@@ -159,8 +159,8 @@ private static void DrawSketchedArrowHead(SKCanvas canvas, SKPoint start, SKPoin
float len = headLength + lengthVariation;
var point = new SKPoint(
- end.X - len * (float)Math.Cos(angle + angleOffset),
- end.Y - len * (float)Math.Sin(angle + angleOffset)
+ end.X - (len * (float)Math.Cos(angle + angleOffset)),
+ end.Y - (len * (float)Math.Sin(angle + angleOffset))
);
canvas.DrawLine(end, point, paint);
@@ -181,7 +181,7 @@ public static void DrawHandDrawnArrow(this SKCanvas canvas, SKPoint start, SKPoi
// Vector and direction
var dx = end.X - start.X;
var dy = end.Y - start.Y;
- var length = (float)Math.Sqrt(dx * dx + dy * dy);
+ var length = (float)Math.Sqrt((dx * dx) + (dy * dy));
if (length == 0) return;
var unit = new SKPoint(dx / length, dy / length);
@@ -194,8 +194,8 @@ public static void DrawHandDrawnArrow(this SKCanvas canvas, SKPoint start, SKPoi
for (int i = 1; i < segments; i++)
{
float t = i / (float)segments;
- var baseX = start.X + dx * t;
- var baseY = start.Y + dy * t;
+ var baseX = start.X + (dx * t);
+ var baseY = start.Y + (dy * t);
// Smaller random offset for more subtle effect
float offset = strokeWidth * 0.5f; // reduced wobble
@@ -229,13 +229,13 @@ public static void DrawHandDrawnArrow(this SKCanvas canvas, SKPoint start, SKPoi
var angle = Math.Atan2(dy, dx);
var arrowPoint1 = new SKPoint(
- end.X - arrowHeadLength * (float)Math.Cos(angle - arrowHeadAngle),
- end.Y - arrowHeadLength * (float)Math.Sin(angle - arrowHeadAngle)
+ end.X - (arrowHeadLength * (float)Math.Cos(angle - arrowHeadAngle)),
+ end.Y - (arrowHeadLength * (float)Math.Sin(angle - arrowHeadAngle))
);
var arrowPoint2 = new SKPoint(
- end.X - arrowHeadLength * (float)Math.Cos(angle + arrowHeadAngle),
- end.Y - arrowHeadLength * (float)Math.Sin(angle + arrowHeadAngle)
+ end.X - (arrowHeadLength * (float)Math.Cos(angle + arrowHeadAngle)),
+ end.Y - (arrowHeadLength * (float)Math.Sin(angle + arrowHeadAngle))
);
canvas.DrawLine(end, arrowPoint1, paint);
@@ -266,13 +266,13 @@ public static void DrawDefaultArrow(this SKCanvas canvas, SKPoint start, SKPoint
// Calculate points for arrowhead
var arrowPoint1 = new SKPoint(
- end.X - arrowHeadLength * (float)Math.Cos(angle - arrowHeadAngle),
- end.Y - arrowHeadLength * (float)Math.Sin(angle - arrowHeadAngle)
+ end.X - (arrowHeadLength * (float)Math.Cos(angle - arrowHeadAngle)),
+ end.Y - (arrowHeadLength * (float)Math.Sin(angle - arrowHeadAngle))
);
var arrowPoint2 = new SKPoint(
- end.X - arrowHeadLength * (float)Math.Cos(angle + arrowHeadAngle),
- end.Y - arrowHeadLength * (float)Math.Sin(angle + arrowHeadAngle)
+ end.X - (arrowHeadLength * (float)Math.Cos(angle + arrowHeadAngle)),
+ end.Y - (arrowHeadLength * (float)Math.Sin(angle + arrowHeadAngle))
);
// Draw the arrowhead
@@ -305,13 +305,13 @@ private static void DrawFilledArrow(SKCanvas canvas, SKPoint start, SKPoint end,
float arrowHeadAngle = (float)(Math.PI / 7);
var arrowPoint1 = new SKPoint(
- end.X - arrowHeadLength * (float)Math.Cos(angle - arrowHeadAngle),
- end.Y - arrowHeadLength * (float)Math.Sin(angle - arrowHeadAngle)
+ end.X - (arrowHeadLength * (float)Math.Cos(angle - arrowHeadAngle)),
+ end.Y - (arrowHeadLength * (float)Math.Sin(angle - arrowHeadAngle))
);
var arrowPoint2 = new SKPoint(
- end.X - arrowHeadLength * (float)Math.Cos(angle + arrowHeadAngle),
- end.Y - arrowHeadLength * (float)Math.Sin(angle + arrowHeadAngle)
+ end.X - (arrowHeadLength * (float)Math.Cos(angle + arrowHeadAngle)),
+ end.Y - (arrowHeadLength * (float)Math.Sin(angle + arrowHeadAngle))
);
using var path = new SKPath();
@@ -338,7 +338,7 @@ public static void DrawDoubleLineArrow(this SKCanvas canvas, SKPoint start, SKPo
// Direction vector
var dx = end.X - start.X;
var dy = end.Y - start.Y;
- var length = (float)Math.Sqrt(dx * dx + dy * dy);
+ var length = (float)Math.Sqrt((dx * dx) + (dy * dy));
if (length == 0) return;
var unitDir = new SKPoint(dx / length, dy / length);
@@ -354,8 +354,8 @@ public static void DrawDoubleLineArrow(this SKCanvas canvas, SKPoint start, SKPo
// Base of arrowhead (end of shaft)
var shaftEnd = new SKPoint(
- end.X - unitDir.X * arrowHeadLength,
- end.Y - unitDir.Y * arrowHeadLength
+ end.X - (unitDir.X * arrowHeadLength),
+ end.Y - (unitDir.Y * arrowHeadLength)
);
// Two parallel lines leading to shaftEnd
@@ -374,13 +374,13 @@ public static void DrawDoubleLineArrow(this SKCanvas canvas, SKPoint start, SKPo
// Arrowhead
var angle = Math.Atan2(dy, dx);
var arrowPoint1 = new SKPoint(
- end.X - arrowHeadLength * (float)Math.Cos(angle - arrowHeadAngle),
- end.Y - arrowHeadLength * (float)Math.Sin(angle - arrowHeadAngle)
+ end.X - (arrowHeadLength * (float)Math.Cos(angle - arrowHeadAngle)),
+ end.Y - (arrowHeadLength * (float)Math.Sin(angle - arrowHeadAngle))
);
var arrowPoint2 = new SKPoint(
- end.X - arrowHeadLength * (float)Math.Cos(angle + arrowHeadAngle),
- end.Y - arrowHeadLength * (float)Math.Sin(angle + arrowHeadAngle)
+ end.X - (arrowHeadLength * (float)Math.Cos(angle + arrowHeadAngle)),
+ end.Y - (arrowHeadLength * (float)Math.Sin(angle + arrowHeadAngle))
);
canvas.DrawLine(end, arrowPoint1, paint);
@@ -410,13 +410,13 @@ private static void DrawDashedArrow(SKCanvas canvas, SKPoint start, SKPoint end,
float arrowHeadAngle = (float)(Math.PI / 6);
var arrowPoint1 = new SKPoint(
- end.X - arrowHeadLength * (float)Math.Cos(angle - arrowHeadAngle),
- end.Y - arrowHeadLength * (float)Math.Sin(angle - arrowHeadAngle)
+ end.X - (arrowHeadLength * (float)Math.Cos(angle - arrowHeadAngle)),
+ end.Y - (arrowHeadLength * (float)Math.Sin(angle - arrowHeadAngle))
);
var arrowPoint2 = new SKPoint(
- end.X - arrowHeadLength * (float)Math.Cos(angle + arrowHeadAngle),
- end.Y - arrowHeadLength * (float)Math.Sin(angle + arrowHeadAngle)
+ end.X - (arrowHeadLength * (float)Math.Cos(angle + arrowHeadAngle)),
+ end.Y - (arrowHeadLength * (float)Math.Sin(angle + arrowHeadAngle))
);
canvas.DrawLine(end, arrowPoint1, paint);
@@ -457,7 +457,7 @@ internal static CoachmarkPosition CalculateOptimalPosition(this SKRect targetBou
if (positions.ContainsKey(CoachmarkPosition.Top))
return CoachmarkPosition.Top;
- return positions.Any() ? positions.OrderByDescending(p => p.Value).First().Key : CoachmarkPosition.Bottom;
+ return positions.Count != 0 ? positions.OrderByDescending(p => p.Value).First().Key : CoachmarkPosition.Bottom;
}
///
@@ -539,16 +539,16 @@ private static void DrawShape(SKCanvas canvas, HighlightShape shape, float cente
canvas.DrawCircle(centerX, centerY, radius, paint);
break;
case HighlightShape.Ellipse:
- canvas.DrawOval(new SKRect(centerX - width / 2, centerY - height / 2,
- centerX + width / 2, centerY + height / 2), paint);
+ canvas.DrawOval(new SKRect(centerX - (width / 2), centerY - (height / 2),
+ centerX + (width / 2), centerY + (height / 2)), paint);
break;
case HighlightShape.Rectangle:
- canvas.DrawRect(new SKRect(centerX - width / 2, centerY - height / 2,
- centerX + width / 2, centerY + height / 2), paint);
+ canvas.DrawRect(new SKRect(centerX - (width / 2), centerY - (height / 2),
+ centerX + (width / 2), centerY + (height / 2)), paint);
break;
default: // RoundRectangle
- canvas.DrawRoundRect(new SKRoundRect(new SKRect(centerX - width / 2, centerY - height / 2,
- centerX + width / 2, centerY + height / 2), cornerRadius), paint);
+ canvas.DrawRoundRect(new SKRoundRect(new SKRect(centerX - (width / 2), centerY - (height / 2),
+ centerX + (width / 2), centerY + (height / 2)), cornerRadius), paint);
break;
}
}
@@ -585,7 +585,7 @@ internal static void DrawFocusRippleEffect(this SKCanvas canvas, SKRect canvasRe
// Convergence phase - show the ripple effect (only during first cycle)
float convergenceProgress = progress / convergencePhaseEnd;
float minRadius = Math.Max(targetWidth, targetHeight) / 2;
- float currentRadius = maxDistance - (maxDistance - minRadius) * convergenceProgress;
+ float currentRadius = maxDistance - ((maxDistance - minRadius) * convergenceProgress);
// Step 1: Draw the original dark overlay everywhere first
using var originalPaint = new SKPaint
@@ -670,14 +670,14 @@ private static SKPath CreateFocusClipPath(HighlightShape shape, float centerX, f
float ellipseHeight = radius * 2 * aspectRatio;
float convergeFactor = 1f - convergenceProgress;
- ellipseWidth = targetWidth + (ellipseWidth - targetWidth) * convergeFactor;
- ellipseHeight = targetHeight + (ellipseHeight - targetHeight) * convergeFactor;
+ ellipseWidth = targetWidth + ((ellipseWidth - targetWidth) * convergeFactor);
+ ellipseHeight = targetHeight + ((ellipseHeight - targetHeight) * convergeFactor);
clipPath.AddOval(new SKRect(
- centerX - ellipseWidth / 2,
- centerY - ellipseHeight / 2,
- centerX + ellipseWidth / 2,
- centerY + ellipseHeight / 2));
+ centerX - (ellipseWidth / 2),
+ centerY - (ellipseHeight / 2),
+ centerX + (ellipseWidth / 2),
+ centerY + (ellipseHeight / 2)));
}
break;
@@ -685,14 +685,14 @@ private static SKPath CreateFocusClipPath(HighlightShape shape, float centerX, f
if (isConverging)
{
float convergeFactor = 1f - convergenceProgress;
- float rectWidth = targetWidth + (radius * 2 - targetWidth) * convergeFactor;
- float rectHeight = targetHeight + (radius * 2 - targetHeight) * convergeFactor;
+ float rectWidth = targetWidth + (((radius * 2) - targetWidth) * convergeFactor);
+ float rectHeight = targetHeight + (((radius * 2) - targetHeight) * convergeFactor);
clipPath.AddRect(new SKRect(
- centerX - rectWidth / 2,
- centerY - rectHeight / 2,
- centerX + rectWidth / 2,
- centerY + rectHeight / 2));
+ centerX - (rectWidth / 2),
+ centerY - (rectHeight / 2),
+ centerX + (rectWidth / 2),
+ centerY + (rectHeight / 2)));
}
break;
@@ -700,15 +700,15 @@ private static SKPath CreateFocusClipPath(HighlightShape shape, float centerX, f
if (isConverging)
{
float convergeFactor = 1f - convergenceProgress;
- float roundRectWidth = targetWidth + (radius * 2 - targetWidth) * convergeFactor;
- float roundRectHeight = targetHeight + (radius * 2 - targetHeight) * convergeFactor;
- float currentCornerRadius = cornerRadius + (cornerRadius * 3 - cornerRadius) * convergeFactor;
+ float roundRectWidth = targetWidth + (((radius * 2) - targetWidth) * convergeFactor);
+ float roundRectHeight = targetHeight + (((radius * 2) - targetHeight) * convergeFactor);
+ float currentCornerRadius = cornerRadius + (((cornerRadius * 3) - cornerRadius) * convergeFactor);
clipPath.AddRoundRect(new SKRect(
- centerX - roundRectWidth / 2,
- centerY - roundRectHeight / 2,
- centerX + roundRectWidth / 2,
- centerY + roundRectHeight / 2),
+ centerX - (roundRectWidth / 2),
+ centerY - (roundRectHeight / 2),
+ centerX + (roundRectWidth / 2),
+ centerY + (roundRectHeight / 2)),
currentCornerRadius, currentCornerRadius);
}
break;
@@ -739,14 +739,14 @@ private static void DrawFocusRing(SKCanvas canvas, HighlightShape shape, float c
{
float aspectRatio = targetHeight / targetWidth;
float convergeFactor = 1f - convergenceProgress;
- float ellipseWidth = targetWidth + (radius * 2 - targetWidth) * convergeFactor;
- float ellipseHeight = targetHeight + (radius * 2 * aspectRatio - targetHeight) * convergeFactor;
+ float ellipseWidth = targetWidth + (((radius * 2) - targetWidth) * convergeFactor);
+ float ellipseHeight = targetHeight + (((radius * 2 * aspectRatio) - targetHeight) * convergeFactor);
canvas.DrawOval(new SKRect(
- centerX - ellipseWidth / 2,
- centerY - ellipseHeight / 2,
- centerX + ellipseWidth / 2,
- centerY + ellipseHeight / 2), ringPaint);
+ centerX - (ellipseWidth / 2),
+ centerY - (ellipseHeight / 2),
+ centerX + (ellipseWidth / 2),
+ centerY + (ellipseHeight / 2)), ringPaint);
}
break;
@@ -754,14 +754,14 @@ private static void DrawFocusRing(SKCanvas canvas, HighlightShape shape, float c
if (isConverging)
{
float convergeFactor = 1f - convergenceProgress;
- float rectWidth = targetWidth + (radius * 2 - targetWidth) * convergeFactor;
- float rectHeight = targetHeight + (radius * 2 - targetHeight) * convergeFactor;
+ float rectWidth = targetWidth + (((radius * 2) - targetWidth) * convergeFactor);
+ float rectHeight = targetHeight + (((radius * 2) - targetHeight) * convergeFactor);
canvas.DrawRect(new SKRect(
- centerX - rectWidth / 2,
- centerY - rectHeight / 2,
- centerX + rectWidth / 2,
- centerY + rectHeight / 2), ringPaint);
+ centerX - (rectWidth / 2),
+ centerY - (rectHeight / 2),
+ centerX + (rectWidth / 2),
+ centerY + (rectHeight / 2)), ringPaint);
}
break;
@@ -769,15 +769,15 @@ private static void DrawFocusRing(SKCanvas canvas, HighlightShape shape, float c
if (isConverging)
{
float convergeFactor = 1f - convergenceProgress;
- float roundRectWidth = targetWidth + (radius * 2 - targetWidth) * convergeFactor;
- float roundRectHeight = targetHeight + (radius * 2 - targetHeight) * convergeFactor;
- float currentCornerRadius = cornerRadius + (cornerRadius * 3 - cornerRadius) * convergeFactor;
+ float roundRectWidth = targetWidth + (((radius * 2) - targetWidth) * convergeFactor);
+ float roundRectHeight = targetHeight + (((radius * 2) - targetHeight) * convergeFactor);
+ float currentCornerRadius = cornerRadius + (((cornerRadius * 3) - cornerRadius) * convergeFactor);
canvas.DrawRoundRect(new SKRoundRect(new SKRect(
- centerX - roundRectWidth / 2,
- centerY - roundRectHeight / 2,
- centerX + roundRectWidth / 2,
- centerY + roundRectHeight / 2),
+ centerX - (roundRectWidth / 2),
+ centerY - (roundRectHeight / 2),
+ centerX + (roundRectWidth / 2),
+ centerY + (roundRectHeight / 2)),
currentCornerRadius), ringPaint);
}
break;
diff --git a/Maui.FreakyUXKit/Samples/AppShell.xaml.cs b/Maui.FreakyUXKit/Samples/AppShell.xaml.cs
index 00c9193..73451b1 100644
--- a/Maui.FreakyUXKit/Samples/AppShell.xaml.cs
+++ b/Maui.FreakyUXKit/Samples/AppShell.xaml.cs
@@ -1,9 +1,10 @@
namespace Samples;
public partial class AppShell : Shell
{
- internal const string arrow = "arrow";
- internal const string focus = "focus";
- internal const string spotlight = "spotlight";
+ internal const string arrow = "Arrow";
+ internal const string focus = "Focus";
+ internal const string spotlight = "Spotlight";
+ internal const string intro = "Intro";
public AppShell()
{
@@ -11,5 +12,6 @@ public AppShell()
Routing.RegisterRoute(arrow, typeof(Arrow.ArrowCoachmarkPage));
Routing.RegisterRoute(focus, typeof(Focus.FocusCoachmarkPage));
Routing.RegisterRoute(spotlight, typeof(Spotlight.SpotlightCoachmarkPage));
+ Routing.RegisterRoute(intro, typeof(Intro.IntroPage));
}
}
\ No newline at end of file
diff --git a/Maui.FreakyUXKit/Samples/Extensions.cs b/Maui.FreakyUXKit/Samples/Extensions.cs
new file mode 100644
index 0000000..3b02cfc
--- /dev/null
+++ b/Maui.FreakyUXKit/Samples/Extensions.cs
@@ -0,0 +1,14 @@
+using System;
+
+namespace Samples;
+
+public static class Extensions
+{
+ public static T GetResource(this ResourceDictionary dictionary, string key)
+ {
+ if (dictionary.TryGetValue(key, out var value) && value is T resource)
+ return resource;
+ else
+ return default;
+ }
+}
diff --git a/Maui.FreakyUXKit/Samples/FreakyBaseContentPage.xaml b/Maui.FreakyUXKit/Samples/FreakyBaseContentPage.xaml
index a8bb7de..fbfd24c 100644
--- a/Maui.FreakyUXKit/Samples/FreakyBaseContentPage.xaml
+++ b/Maui.FreakyUXKit/Samples/FreakyBaseContentPage.xaml
@@ -18,7 +18,7 @@
-
+
+
+
+
diff --git a/Maui.FreakyUXKit/Samples/Intro/IntroPage.xaml.cs b/Maui.FreakyUXKit/Samples/Intro/IntroPage.xaml.cs
new file mode 100644
index 0000000..55c24d6
--- /dev/null
+++ b/Maui.FreakyUXKit/Samples/Intro/IntroPage.xaml.cs
@@ -0,0 +1,10 @@
+namespace Samples.Intro;
+
+public partial class IntroPage : ContentPage
+{
+ public IntroPage()
+ {
+ InitializeComponent();
+ BindingContext = new IntroViewModel();
+ }
+}
\ No newline at end of file
diff --git a/Maui.FreakyUXKit/Samples/Intro/IntroViewModel.cs b/Maui.FreakyUXKit/Samples/Intro/IntroViewModel.cs
new file mode 100644
index 0000000..073ba7a
--- /dev/null
+++ b/Maui.FreakyUXKit/Samples/Intro/IntroViewModel.cs
@@ -0,0 +1,48 @@
+using Maui.FreakyUXKit;
+
+namespace Samples.Intro;
+
+public partial class IntroViewModel : FreakyBaseViewModel
+{
+ public List Steps { get; set; }
+ public IntroViewModel()
+ {
+ Steps =
+ [
+ new() {
+ TitleText = "Products from worldwide",
+ SubTitleText = "Its a long established fact that a reader will be distracted by the readable content of a page when looking at its layout.",
+ ImageSource = "people_on_bike.png",
+ LeftButtonText = "Skip",
+ CenterButtonText = "Next",
+ RightButtonText = "Learn More",
+ TitleLabelStyle = Application.Current.Resources.GetResource
-
-
+
+