How does Blazor implement Tab switching similar to WeChat?

Does anyone want to swipe to switch tabs when using tabs?

And there is the effect of sliding left out, left in, right out of right in, this article will explain how Blazorto switch Tab by sliding in the middle

The UI components in this article are used MASA Blazor, you can also use other UI frameworks, this does not affect the actual running effect, the case in this article is compatible with PC and Android, the demonstration effect is executed in android, and the execution effect in PC is still the same Effective (pro-test)

Install first Install MASA Blazoraccording to MASA BlazorMASA Blazor

Preparation

  1. create AppBar.razorfile

  2. modify MainLayout.razorfile code

@inherits LayoutComponentBase

<MApp>
    <AppBar>
        <div class="body">
            @Body
        </div>
    </AppBar>
</MApp>

<style>
.body {
      
      
    /*设置内容高度 需要减去导航栏的高度*/
    height: calc(100vh - 48px);
    max-height: calc(100vh - 48px);
}
</style>
  1. Create AppBar.razor.cssa file and add relevant codes. The following codes are used to achieve an in-and-out effect when switching. The specific code examples are from Animista - On-Demand CSS Animations Library

    
    /*左边滑动出*/
    .slide-out-left {
        -webkit-animation: slide-out-left 0.5s;
        animation: slide-out-left 0.5s;
    }
    
    /*右边滑动出*/
    .slide-out-right {
        -webkit-animation: slide-out-right 0.5s;
        animation: slide-out-right 0.5s;
    }
    
    /*右边滑动进*/
    .slide-in-right {
        -webkit-animation: slide-in-right 0.5s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
        animation: slide-in-right 0.5s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
    }
    
    /*左边滑动进*/
    .slide-in-left {
        -webkit-animation: slide-in-left 0.5s;
        animation: slide-in-left 0.5s;
    }
    
    @-webkit-keyframes slide-out-left {
        0% {
            -webkit-transform: translateX(0);
            transform: translateX(0);
            opacity: 1;
        }
    
        100% {
            -webkit-transform: translateX(-1000px);
            transform: translateX(-1000px);
            opacity: 0;
        }
    }
    
    @keyframes slide-out-left {
        0% {
            -webkit-transform: translateX(0);
            transform: translateX(0);
            opacity: 1;
        }
    
        100% {
            -webkit-transform: translateX(-1000px);
            transform: translateX(-1000px);
            opacity: 0;
        }
    }
    
    
    @-webkit-keyframes slide-out-right {
        0% {
            -webkit-transform: translateX(0);
            transform: translateX(0);
            opacity: 1;
        }
    
        100% {
            -webkit-transform: translateX(1000px);
            transform: translateX(1000px);
            opacity: 0;
        }
    }
    
    @keyframes slide-out-right {
        0% {
            -webkit-transform: translateX(0);
            transform: translateX(0);
            opacity: 1;
        }
    
        100% {
            -webkit-transform: translateX(1000px);
            transform: translateX(1000px);
            opacity: 0;
        }
    }
    
    
    @-webkit-keyframes slide-in-left {
        0% {
            -webkit-transform: translateX(-1000px);
            transform: translateX(-1000px);
            opacity: 0;
        }
    
        100% {
            -webkit-transform: translateX(0);
            transform: translateX(0);
            opacity: 1;
        }
    }
    
    @keyframes slide-in-left {
        0% {
            -webkit-transform: translateX(-1000px);
            transform: translateX(-1000px);
            opacity: 0;
        }
    
        100% {
            -webkit-transform: translateX(0);
            transform: translateX(0);
            opacity: 1;
        }
    }
    @-webkit-keyframes slide-in-right {
        0% {
            -webkit-transform: translateX(1000px);
            transform: translateX(1000px);
            opacity: 0;
        }
    
        100% {
            -webkit-transform: translateX(0);
            transform: translateX(0);
            opacity: 1;
        }
    }
    
    @keyframes slide-in-right {
        0% {
            -webkit-transform: translateX(1000px);
            transform: translateX(1000px);
            opacity: 0;
        }
    
        100% {
            -webkit-transform: translateX(0);
            transform: translateX(0);
            opacity: 1;
        }
    }
    
    
  2. The created AppBarmodel is used to dynamically add the navigation bar, create AppBarDto.csfiles and add related codes

public class AppBarDto
{
    public string Key { get; set; }

    /// <summary>
    /// 标题
    /// </summary>
    public string Title { get; init; }

    /// <summary>
    /// 图标
    /// </summary>
    public string? Icon { get; set; }

    /// <summary>
    /// 路由
    /// </summary>
    public string Href { get; init; }

    public AppBarDto(string title, string href, string? icon = null)
    {
        Title = title;
        Icon = icon;
        Href = href;
        Key = Guid.NewGuid().ToString("N");
    }
}
  1. Add related pages, Pagesunder the folder, respectively create Index.razor, Feature.razor, Friend.razor,PersonalCenter.razor

File related code:
Index.razor

@page "/"
<h3>Index</h3>

Feature.razor

@page "/feature"
<h3>Feature</h3>

Friend.razor

@page "/friend"
<h3>Friend</h3>

PersonalCenter.razor

@page "/personal-center"
<h3>PersonalCenter</h3>
  1. modify AppBar.razorthe code


<div class="@Class" @ontouchstart="TouchStart" @ontouchend="TouchEnd" @onmousedown="Mousedown" @onmouseup="Mouseup" style="height: 100%">
    @ChildContent
</div>

 @*这里也可以是其他组件的Tab,其实只是记录当前的导航的数据*@
<MTabs Centered
       BackgroundColor="indigo"
       ShowArrows="false"
       Value="selectModel.Key"
       Dark>
    @foreach (var i in AppBars)
    {
        <MTab Value="i.Key" OnClick="()=>GoHref(i)">
            @if (!string.IsNullOrEmpty(i.Icon))
            {
                <MIcon Dark>@i.Icon</MIcon>
            }
            @i.Title
        </MTab>
    }
</MTabs>

  1. Create AppBar.razor.csadd the following code

public partial class AppBar
{
    #region Inject

    [Inject]
    public required NavigationManager NavigationManager { get; set; }

    #endregion

    private readonly List<AppBarDto> AppBars = new();

    [Parameter]
    public RenderFragment ChildContent { get; set; }

    private AppBarDto selectModel;

    private string Class { get; set; }

    protected override void OnInitialized()
    {
        AppBars.Add(new AppBarDto("首页", "/", "home"));
        AppBars.Add(new AppBarDto("好友", "/personal-center", "mdi-account-group-outline"));
        AppBars.Add(new AppBarDto("功能", "/feature", "mdi-wrench"));
        AppBars.Add(new AppBarDto("个人中心", "/personal-center", "mdi-badge-account-alert"));

        // 默认选择的导航标签
        selectModel = AppBars[0];

        base.OnInitialized();
    }

    /// <summary>
    /// 导航栏跳转
    /// </summary>
    /// <param name="appBar"></param>
    private void GoHref(AppBarDto appBar)
    {
        // 防止重复点击
        if(appBar == selectModel)
        {
            return;
        }

        // 当点击导航的索引大于现在导航时启动滑动效果
        if(AppBars.IndexOf(appBar) > AppBars.IndexOf(selectModel))
        {
            Class = "slide-out-left";
            Task.Run(async () =>
            {
                // 由于特效时间为0.5s 这里是等待特效完成
                await Task.Delay(450);
                NavigationManager.NavigateTo(selectModel.Href);
                Class = "slide-in-right";
                _ = InvokeAsync(StateHasChanged);
            });
        }
        // 当点击导航的索引小于现在导航时启动滑动效果
        else if (AppBars.IndexOf(appBar) < AppBars.IndexOf(selectModel))
        {
            Class = "slide-out-right";
            Task.Run(async () =>
            {
                // 由于特效时间为0.5s 这里是等待特效完成
                await Task.Delay(450);
                NavigationManager.NavigateTo(selectModel.Href);
                Class = "slide-in-left";
                _ = InvokeAsync(StateHasChanged);
            });
        }
        selectModel = appBar;
        NavigationManager.NavigateTo(appBar.Href);
    }


    /// <summary>
    /// 开始X坐标
    /// </summary>
    private double _startX;


    #region 移动端滑动处理

    /// <summary>
    /// 记录开始坐标
    /// </summary>
    /// <param name="args"></param>
    private void TouchStart(TouchEventArgs args)
    {
        var touch = args.ChangedTouches[0];
        _startX = touch.ScreenX;
    }

    private void TouchEnd(TouchEventArgs args)
    {
        var touch = args.ChangedTouches[0];
        Switchover((decimal)touch.ScreenX);
    }

    #endregion

    #region PC滑动处理

    /// <summary>
    /// 记录开始坐标
    /// </summary>
    /// <param name="args"></param>
    private void Mousedown(MouseEventArgs args)
    {
        _startX = args.ScreenX;
    }

    private void Mouseup(MouseEventArgs args)
    {
        Switchover((decimal)args.ScreenX);
    }

    #endregion

    private void Switchover(decimal screenX)
    {
        var index = AppBars.IndexOf(selectModel);
        // 限制过度滑动
        if (index == AppBars.Count || index > AppBars.Count)
        {
            return;
        }

        // 设置滑动最大位限制,达到这个限制才滑动生效
        var size = 200;

        // 需要滑动200才切换 如果开始坐标x大于 当前结束的x坐标往右边切换tab
        if ((decimal)_startX - size > screenX)
        {
            // 如果右边往左边滑动 当前索引是当前最大数量的话不需要切换
            if (index == AppBars.Count - 1)
            {
                return;
            }
            selectModel = AppBars[index + 1];
            Class = "slide-out-left";

            Task.Run(async () =>
            {
                // 由于特效时间为0.5s 这里是等待特效完成
                await Task.Delay(450);
                NavigationManager.NavigateTo(selectModel.Href);
                Class = "slide-in-right";
                _ = InvokeAsync(StateHasChanged);
            });
        }
        else if ((decimal)_startX + size < screenX)
        {
            // 如果左边往右边滑动 当前索引是0的话不需要切换
            if (index == 0)
            {
                return;
            }
            selectModel = AppBars[index - 1];
            Class = "slide-out-right";
            Task.Run(async () =>
            {
                // 由于特效时间为0.5s 这里是等待特效完成
                await Task.Delay(450);
                NavigationManager.NavigateTo(selectModel.Href);
                Class = "slide-in-left";
                _ = InvokeAsync(StateHasChanged);
            });
        }
    }
}

running result:

insert image description here

A token that loves to learn

Qq exchange group: 737776595
WeChat exchange group:

Guess you like

Origin blog.csdn.net/xiaohucxy/article/details/128751323