top of page
Search
  • Writer's picturePavel Chuvak

Mortal Kombat App using Xamarin.Forms (MAUI)

Xamarin is a powerful cross-platform framework with wide abilities. And I will prove it to you!

You can create a beautiful parallax effect using CarouselView and CarouselViewBehaviour. In addition, I'll show you how to create your own complex views effortlessly.



Parallax

CarouselView has a Behaviors property, which means you can create custom behaviors for your CarouselView. I'm leaving some of the code from the gif example.

<CarouselView
    ItemsSource="{Binding MKCharacters}"
    CurrentItem="{Binding CurrentMKCharacter}"
    IsScrollAnimated="False"
    HorizontalScrollBarVisibility="Never">
    <CarouselView.Behaviors>
        <behaviors:ParallaxCarouselViewBehavior ParallaxOffset="500" />
    </CarouselView.Behaviors>
    <CarouselView.ItemsLayout>
        <LinearItemsLayout
	    Orientation="Horizontal"
	    SnapPointsAlignment="Center"
	    SnapPointsType="Mandatory" />
    </CarouselView.ItemsLayout>
    <CarouselView.ItemTemplate>
        <DataTemplate x:DataType="models:MKCharacter">
            <mkDemo:MKDemoCell Character="{Binding .}" />
	</DataTemplate>
    </CarouselView.ItemTemplate>
</CarouselView>

As you can see, this CarouselView has a ParallaxCarouselViewBehavior. The parallax behavior is created by the following code.

public class ParallaxCarouselViewBehavior : Behavior<CarouselView>
{
    #region ParallaxOffset Property

    public static readonly BindableProperty ParallaxOffsetProperty = BindableProperty.Create(
        nameof(ParallaxOffset),
        typeof(double),
        typeof(ParallaxCarouselViewBehavior),
        500d,
        BindingMode.TwoWay);

    public double ParallaxOffset
    {
        get => (double) GetValue(ParallaxOffsetProperty);
        set => SetValue(ParallaxOffsetProperty, value);
    }

    #endregion ParallaxOffset Property

    protected override void OnAttachedTo(CarouselView bindable)
    {
        base.OnAttachedTo(bindable);

        bindable.Scrolled += OnScrolled;
    }

    protected override void OnDetachingFrom(CarouselView bindable)
    {
        base.OnDetachingFrom(bindable);

        bindable.Scrolled -= OnScrolled;
    }

    private void OnScrolled(object sender, ItemsViewScrolledEventArgs e)
    {
        if (!(sender is CarouselView carouselView) || !(carouselView.ItemsLayout is ItemsLayout itemsLayout))
        {
            return;
        }

        var carouselItems = carouselView.ItemsSource.Cast<BaseParallaxCarouselItem>().ToList();

        var centerItemIndex = e.CenterItemIndex;
        var lastItemIndex = e.LastVisibleItemIndex;

        switch (itemsLayout.Orientation)
        {
            case ItemsLayoutOrientation.Horizontal:
            {
                var carouselWidth = carouselView.Width;

                var offset = carouselWidth * (centerItemIndex + 1) - e.HorizontalOffset;
                var position = offset * ParallaxOffset / carouselWidth - ParallaxOffset;

                var lastItem = carouselItems[lastItemIndex];
                lastItem.ParallaxTranslation = position + ParallaxOffset;

                var currentItem = carouselItems[centerItemIndex];
                currentItem.ParallaxTranslation = position;

                break;
            }
            case ItemsLayoutOrientation.Vertical:
            {
                var carouselHeight = carouselView.Height;

                var offset = carouselHeight * (centerItemIndex + 1) - e.VerticalOffset;
                var position = offset * ParallaxOffset / carouselHeight - ParallaxOffset;

                var lastItem = carouselItems[lastItemIndex];
                lastItem.ParallaxTranslation = position + ParallaxOffset;

                var currentItem = carouselItems[centerItemIndex];
                currentItem.ParallaxTranslation = position;

                break;
            }
            default:
                throw new ArgumentOutOfRangeException();
        }
    }
}

The ParallaxOffset bindable property was used to set the view speed when scrolling the CarouselView.


For use, I suggest you create some kind of BaseParallaxCarouselItem.

public abstract class BaseParallaxCarouselItem : INotifyPropertyChanged
{
    public double ParallaxTranslation { get; set; }

    public event PropertyChangedEventHandler PropertyChanged;
}

How to use:

<Label
    Text="{Binding Source={x:Reference Cell}, Path=Character.Name}"
    TextColor="White"
    FontFamily="{x:Static helpers:Constants+EmbeddedFonts.MKTitle}"
    FontSize="40"
    MaxLines="1"
    TranslationX="{Binding Source={x:Reference Cell},
                           Path=Character.ParallaxTranslation,
                           Converter={StaticResource DivisionByNumberConverter}, 
                           ConverterParameter=3}" />

<Image
    Source="{Binding Source={x:Reference Cell}, Path=Character.Image}"
    Aspect="AspectFit"
    TranslationX="{Binding Source={x:Reference Cell},
                           Path=Character.ParallaxTranslation}" />

The Character model implements BaseParallaxCarouselItem.

As you can see, you can use a different speed for all views.

In this part of the code, the Label will move 3 times slower than the Image.


Clip views



I want to suggest that you use the Clip property to create a nice and complex control without any NuGet packages or renderers. For example, you can see the use of the Clip property in the gif or image (Back and Select buttons). In this example, I am using SVG paths to create these views, and I found several online tools to create SVG paths (e.g. https://mavo.io/demos/svgpath/).


Back Button code

<Grid
    WidthRequest="115"
    HeightRequest="40">

	<BoxView
		Style="{StaticResource MKBorderBoxViewStyle}"
		WidthRequest="115"
		HeightRequest="40"
		HorizontalOptions="Start"
		VerticalOptions="Center">
		<BoxView.Clip>

			<PathGeometry
				Figures="m 15 0 h 100 a 35 35 0 0 1 -15 20 a 35 35 0 0 1 15 20 h -100 a 35 35 0 0 0 -15 -20 a 35 35 0 0 0 15 -20 z" />
		</BoxView.Clip>
	</BoxView>

	<Button
		Style="{StaticResource MKButtonStyle}"
		Command="{Binding GoBackCommand}"
		Text="{x:Static helpers:Constants+Texts.Back}"
		WidthRequest="115"
		HeightRequest="40"
		HorizontalOptions="Start"
		VerticalOptions="Center">
		<Button.Clip>

			<PathGeometry
				Figures="m 17 2 h 95 a 35 35 0 0 1 -15 18 a 35 35 0 0 1 15 18 h -95 a 35 35 0 0 0 -15 -18 a 35 35 0 0 0 15 -18 z" />
		</Button.Clip>
	</Button>
</Grid>

I hope you find something important in this article. Improve your development skills and create cool apps with Xamarin.Forms (MAUI).

735 views1 comment

1 Comment


Sarthak Chauhan
Sarthak Chauhan
May 22, 2021

Can you share repo code please

Like
bottom of page