Blazor入門100天 : 自做一個支援長按事件的按鈕元件

AlexChow發表於2023-12-20

好長時間沒繼續寫這個系列部落格了, 不知道大家還記得我嗎? 話不多說,直接開擼.

配套原始碼

demo https://blazor.app1.es/b19LongPressButton

1. 新建 net8 blazor 工程 b19LongPressButton

至於用什麼模式大家各取所需, 我建立的是ssr單工程, 如果大家不小心建立錯了按頁面渲染模式,可以在 App.razor 裡面改一下, 加入 @rendermode="RenderMode.InteractiveServer" 這句話, 預設使用ssr模式渲染.

<Routes @rendermode="RenderMode.InteractiveServer" />

2. Components\Pages 下新建元件 LongPressButton.razor

@inherits ComponentBase

<div @onclick="onClick" @oncontextmenu="onContextMenu" @ontouchstart="OnTouchStart" @ontouchend="OnTouchEnd">
    @ChildContent
</div>


@code {


    [Inject]
    private IJSRuntime? JS { get; set; }

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

    /// <summary>
    /// 獲得/設定 啟用長按
    /// </summary>
    [Parameter]
    public bool LongPress { get; set; } = true;

    /// <summary>
    /// 獲得/設定 ContextMenu 選單項回撥委託
    /// </summary>
    [Parameter]
    public Func<MouseEventArgs, Task>? OnContextMenu { get; set; }

    /// <summary>
    /// 獲得/設定 長按回撥委託, 如果啟用長按並且不是觸控裝置,則回落到 Click 點選時觸發
    /// </summary>
    [Parameter]
    public Func<MouseEventArgs, Task>? OnLongPress { get; set; }

    /// <summary>
    /// 獲得/設定 Click 回撥委託
    /// </summary>
    [Parameter]
    public Func<MouseEventArgs, Task>? OnClick { get; set; }

    /// <summary>
    /// 獲得/設定 長按延時
    /// </summary>
    [Parameter]
    public int OnTouchTime { get; set; } = 500;

    /// <summary>
    /// ContextMenu 選單項點選時觸發
    /// </summary>
    /// <returns></returns>
    Task onContextMenu(MouseEventArgs args)
    {
        if (OnContextMenu != null)
        {
            return OnContextMenu.Invoke(args);
        }
        else
        {
            return Task.CompletedTask;
        }
    }

    /// <summary>
    /// 點選時觸發
    /// </summary>
    /// <returns></returns>
    Task onClick(MouseEventArgs args)
    {
        if (OnClick != null)
        {
            return OnClick.Invoke(args);
        }
        else if (OnLongPress != null && !IsTouchDevice)
        {
            return OnLongPress.Invoke(args);
        }
        else
        {
            return Task.CompletedTask;
        }
    }

    /// <summary>
    /// 是否觸控裝置
    /// </summary>
    private bool IsTouchDevice { get; set; }

    /// <summary>
    /// 是否觸控
    /// </summary>
    private bool TouchStart { get; set; }

    /// <summary>
    /// 觸控定時器工作指示
    /// </summary>
    private bool IsBusy { get; set; }

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            IsTouchDevice = await JS!.InvokeAsync<bool>("eval", $"'ontouchstart' in document.documentElement");
        }
    }

    private async Task OnTouchStart(TouchEventArgs e)
    {
        if (!IsBusy)
        {
            IsBusy = true;
            TouchStart = true;

            // 延時保持 TouchStart 狀態
            await Task.Delay(OnTouchTime);
            if (TouchStart)
            {
                var args = new MouseEventArgs()
                {
                    ClientX = e.Touches[0].ClientX,
                    ClientY = e.Touches[0].ClientY,
                    ScreenX = e.Touches[0].ScreenX,
                    ScreenY = e.Touches[0].ScreenY,
                    Type = "LongPress"
                };

                // 彈出關聯選單
                if (OnContextMenu != null)
                    await OnContextMenu(args);

                if (OnLongPress != null)
                    await OnLongPress(args);

                //延時防止重複啟用選單功能
                await Task.Delay(OnTouchTime);
            }
            IsBusy = false;
        }
    }

    private void OnTouchEnd()
    {
        TouchStart = false;
    }

}

3. 回到首頁 Home.razor 新增元件測試

@page "/"

<PageTitle>Home</PageTitle>

 <LongPressButton OnLongPress="TaskOnLongPress" > 
        <div style="width:200px;height:100px;background-color:gold;">
            <p>LongPressButton</p>
        </div> 
</LongPressButton>

<p>@message</p>

@code {
    string message = "No long press";

    private Task TaskOnLongPress(MouseEventArgs e)
    {
        message = e.Type;
        StateHasChanged();
        return Task.CompletedTask;
    }
}

4. 測試

執行程式

普通瀏覽器模式, 不支援觸控,會自動會落到點選事件, 點選顯示為 Click

F12開啟開發者工具, 點選模擬手機/平板, 需要F5重新整理頁面重新讀取是否為觸控裝置, 點選無反應, 長按顯示為

相關文章