Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Round corners #7

Merged
merged 5 commits into from
Jun 9, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 39 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,27 @@ Efficient way to add a shimmering effect to your Xamarin.Forms applications.

# Documentation

![Alt Text](https://media.giphy.com/media/AgON7bzysYW9UdXpJF/giphy.gif)

# How To Use

ToDo the specifications. For now, check the sample. It's quite easy.
* Add nuget package Xamarin.Essentials to all projects
* Add Init method to ```App.cs``` constructor:
```
InitializeComponent();
var density = Xamarin.Essentials.DeviceDisplay.MainDisplayInfo.Density;
ShimmerLayout.Init(density);
```

* add reference:
```xml
xmlns:controls="clr-namespace:XFShimmerLayout.Controls;assembly=XFShimmerLayout"
```
* Paste content inside shimmerLayout:
```xml
<controls:ShimmerLayout Angle="-45" GradientSize=".2" IsLoading="True">
<!--Yours awesome view-->
</controls:ShimmerLayout>
```
# How it works

## Drawing Process
Expand All @@ -33,6 +48,27 @@ The ShimmerLayout will create a Canvas Layer above this StackLayout and will dra

You can have as deep Visual Tree wants, the shimmer layout will draw a right copy of it.

## Round Corners and Padding

You can set default CornerRadius and padding of overlay for every element,
```xml
<controls:ShimmerLayout
CornerRadiusOverlayDefault="10"
PaddingOverlayDefault="5" />
```
<img src="https://github.com/VasenevEA/XFShimmerLayout/blob/roundCorners/every.png" width="300">

or set for single element using Attached Property
```xml
<controls:ShimmerLayout ... >
<BoxView
controls:ShimmerLayout.PaddingOverlay="7,1"
controls:ShimmerLayout.CornerRadiusOverlay="5"
... />
</controls:ShimmerLayout>
```
<img src="https://github.com/VasenevEA/XFShimmerLayout/blob/roundCorners/single.png" width="300">

## Shader

Before drawing the Canvas, we must specify a Shader. We used a LinearGradient Shader.
Expand All @@ -52,4 +88,4 @@ To make the animation happen, we need startX and endX:
* The StartX will be 0 - GradientSizeInPixels
* The EndX will be the Width + GradientSizeInPixels

We created an animation that animates a value from the StartX to EndX and every time we ```InvalidateSurface``` of the ```Canvas```.
We created an animation that animates a value from the StartX to EndX and every time we ```InvalidateSurface``` of the ```Canvas```.
Binary file added every.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added single.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

94 changes: 82 additions & 12 deletions src/XFShimmerLayout/Controls/ShimmerLayout.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
using System;
using SkiaSharp;
using SkiaSharp.Views.Forms;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using SkiaSharp;
using SkiaSharp.Views.Forms;
using Xamarin.Forms;
using Xamarin.Forms.Internals;
using XFShimmerLayout.Extensions;
Expand Down Expand Up @@ -106,6 +106,56 @@ public int Angle
set => SetValue(AngleProperty, value);
}

public CornerRadius CornerRadiusOverlayDefault
{
get => (CornerRadius)GetValue(CornerRadiusOverlayDefaultProperty);
set => SetValue(CornerRadiusOverlayDefaultProperty, value);
}

public static readonly BindableProperty CornerRadiusOverlayDefaultProperty = BindableProperty.Create(
nameof(CornerRadiusOverlayDefault), typeof(CornerRadius), typeof(ShimmerLayout), default(CornerRadius));


public Thickness PaddingOverlayDefault
{
get => (Thickness)GetValue(PaddingOverlayDefaultProperty);
set => SetValue(PaddingOverlayDefaultProperty, value);
}

public static readonly BindableProperty PaddingOverlayDefaultProperty = BindableProperty.Create(
nameof(PaddingOverlayDefault), typeof(Thickness), typeof(ShimmerLayout), default(Thickness));


#endregion


#region Attached Properties

public static BindableProperty CornerRadiusOverlayProperty = BindableProperty.CreateAttached(
"CornerRadiusOverlay", typeof(CornerRadius), typeof(ShimmerLayout), default(CornerRadius));

public static CornerRadius GetCornerRadiusOverlay(BindableObject view)
{
return (CornerRadius)view.GetValue(CornerRadiusOverlayProperty);
}

public static void SetCornerRadiusOverlay(BindableObject view, CornerRadius value)
{
view.SetValue(CornerRadiusOverlayProperty, value);
}

public static BindableProperty PaddingOverlayProperty = BindableProperty.CreateAttached(
"PaddingOverlay", typeof(Thickness), typeof(ShimmerLayout), default(Thickness));

public static Thickness GetPaddingOverlay(BindableObject view)
{
return (Thickness)view.GetValue(PaddingOverlayProperty);
}

public static void SetPaddingOverlay(BindableObject view, Thickness value)
{
view.SetValue(PaddingOverlayProperty, value);
}
#endregion

private static double _density;
Expand All @@ -129,7 +179,7 @@ public ShimmerLayout()

SizeChanged += ElementSizeChanged;
}

/// <summary>
/// Initialized the ShimmerLayout by specifying the Device Density
/// </summary>
Expand Down Expand Up @@ -189,18 +239,18 @@ private void UpdatePackedView(View oldValue, View newValue)

Children.Insert(0, newValue);
}

private void UpdateGradient()
{
_gradientPositions = new []
_gradientPositions = new[]
{
0,
.5f - GradientSize / 2,
.5f + GradientSize / 2,
1
};

_gradientColors = new []
_gradientColors = new[]
{
BackgroundGradientColor.ToSKColor(),
ForegroundGradientColor.ToSKColor(),
Expand Down Expand Up @@ -260,7 +310,7 @@ private async Task StartAnimation()
var startValue = -gradientSizePixels;
var endValue = gradientSizePixels + widthPixels;

var tasks = new []
var tasks = new[]
{
Task.Run(async () => await _maskCanvasView.FadeTo(1, 250U, Easing.Linear)),
Task.Run(async () =>
Expand Down Expand Up @@ -354,13 +404,32 @@ private void OnMaskCanvasPaintSurface(object sender, SKPaintSurfaceEventArgs arg
paint.Shader = GetGradientShader();

/* Draw every VisualElement in our layout tree to the SKCanvas */
foreach (var view in _childVisualElements)
foreach (var skElement in _childVisualElements)
{
DrawSKVisualElement(view, args.Surface.Canvas, paint);
//Set cornerRadius and padding for every view
var corner = GetCornerRadiusOverlay(skElement.OriginalView);
var padding = GetPaddingOverlay(skElement.OriginalView);

//If not set - set default value
if (corner == default)
corner = CornerRadiusOverlayDefault;

if (padding == default)
padding = PaddingOverlayDefault;

//If default and specific not set, use box or frame cornerRadius
if(corner != default || CornerRadiusOverlayDefault != default)
skElement.CornerRadius = corner;

skElement.Padding = padding;

DrawSKVisualElement(skElement, args.Surface.Canvas, paint);
}
}
}



/// <summary>
/// Generates a Gradient Shader for the Canvas with the actual Shimmer
/// </summary>
Expand Down Expand Up @@ -433,9 +502,10 @@ added to it exactly or to parent layouts

/* Generate Radii from CornerRadius */
var radii = skVisualElement.CornerRadius.ToRadiiSKPoints(_density);

var padding = skVisualElement.Padding;
/* Using the SKRect constructor, in width and height, we must add the offset X and Y */
var rectangle = new SKRect(startX, startY, widthPixels + startX, heightPixels + startY);
var rectangle = new SKRect(startX - (float)padding.Left, startY - (float)padding.Top,
widthPixels + startX + (float)padding.Right, heightPixels + startY + (float)padding.Bottom);

/* Create the Round Rectangle */
var roundRectangle = new SKRoundRect();
Expand Down
5 changes: 2 additions & 3 deletions src/XFShimmerLayout/Extensions/SkiaExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ internal static class SkiaExtensions
{
public static SKVisualElement ToSKVisualElement(this View element)
{
var visualElement = new SKVisualElement((float)element.X, (float)element.Y, (float)element.Width, (float)element.Height, element.Margin);

var visualElement = new SKVisualElement((float)element.X, (float)element.Y, (float)element.Width, (float)element.Height, element.Margin, element);
switch (element)
{
case BoxView boxView:
Expand All @@ -28,7 +27,7 @@ public static SKVisualElement ToSKVisualElement(this View element)

public static SKLayout ToSKLayout(this Layout<View> layout)
{
var layoutView = new SKLayout((float)layout.X, (float)layout.Y, (float)layout.Width, (float)layout.Height, layout.Margin);
var layoutView = new SKLayout((float)layout.X, (float)layout.Y, (float)layout.Width, (float)layout.Height, layout.Margin, layout);

var children = new List<SKVisualElement>();

Expand Down
4 changes: 2 additions & 2 deletions src/XFShimmerLayout/Models/SkiaHelpers/SKLayout.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ internal class SKLayout : SKVisualElement
public Thickness Padding { get; set; }
public IList<SKVisualElement> Children { get; set; }

public SKLayout(float x, float y, float width, float height, Thickness margin)
: base(x, y, width, height, margin) { }
public SKLayout(float x, float y, float width, float height, Thickness margin, View view)
: base(x, y, width, height, margin, view) { }
}
}
5 changes: 4 additions & 1 deletion src/XFShimmerLayout/Models/SkiaHelpers/SKVisualElement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,20 @@ internal class SKVisualElement
public float Width { get; }
public float Height { get; }
public Thickness Margin { get; }
public Thickness Padding { get; set; }
public SKLayout Parent { get; set; }
public CornerRadius CornerRadius { get; set; }
public View OriginalView { get;}

public SKVisualElement(float x, float y, float width, float height, Thickness margin)
public SKVisualElement(float x, float y, float width, float height, Thickness margin, View view)
{
X = x;
Y = y;
Width = width;
Height = height;
Margin = margin;
CornerRadius = new CornerRadius(0);
OriginalView = view;
}
}
}