Skip to content
# SwipeTrap ## Description A SwipeTrap traps swipe gestures (touch, mouse or pen) on its container and raises start, move, end and trigger events carrying the swipe distance, direction, velocity, duration and pointer type. It supports axis locking (fixed or auto-detected), distance-based and velocity-based (flick) triggering, movement thresholds, event throttling and input filtering, which makes it the building block for swipeable panels, dismissible list items and other gesture-driven UIs. ## Notes The trap declares the axes it takes to the browser as a CSS touch-action, so a native scroll can never win the race against it: with no OrientationLock both axes belong to the trap and the page will not scroll over it, while a Horizontal or Vertical lock leaves the free axis to the browser. Pinch-zoom is always left to the browser. While a swipe is actually being trapped the root carries a bit-stp-swp class, which turns text selection off and is there to style the swiping state. A swipe is a path-based gesture, so pair it with a single-pointer alternative (a button, a menu item) to keep the feature operable without dragging. ## Parameters | Name | Type | Default Value | Description | | :--- | :--- | :------------ | :---------- | | ChildContent | `RenderFragment?` | null | The content of the swipe trap. | | OnStart | `EventCallback<BitSwipeTrapEventArgs>` | | The event callback for when the swipe action starts on the container of the swipe trap. | | OnMove | `EventCallback<BitSwipeTrapEventArgs>` | | The event callback for when the swipe action moves on the container of the swipe trap. | | OnEnd | `EventCallback<BitSwipeTrapEventArgs>` | | The event callback for when the swipe action ends on the container of the swipe trap. | | OnTrigger | `EventCallback<BitSwipeTrapTriggerArgs>` | | The event callback for when the swipe action triggers based on the Trigger or TriggerVelocity constraints. | | OrientationLock | `BitSwipeOrientation?` | null | Specifies the orientation lock in which the swipe trap allows to trap the swipe actions. A Horizontal or Vertical lock is fixed for the whole gesture, whichever direction it starts in: the locked axis is the only one trapped and the only one reported, while the other axis keeps its default browser behavior (via a matching touch-action) and always reports zero. Auto instead locks to the first axis the gesture moves along. | | SkipSelector | `string?` | null | A CSS selector of descendant elements on which starting a swipe is ignored (e.g. inputs or nested interactive elements). | | Threshold | `decimal?` | null | The distance in pixels a gesture must cover before the swipe trap takes it over and stops the default behavior. It is also what resolves the axis a diagonal gesture is moving along (default is 0). | | Throttle | `int?` | null | The throttle time in milliseconds to apply a delay between periodic calls to raise the OnMove event (default is 0, meaning no throttling). | | TouchOnly | `bool` | false | Ignores mouse swipes, trapping only touch (and pen) gestures. | | Trigger | `decimal?` | null | The swiping point to trigger and call the OnTrigger event: either a fraction of the element's width/height (values less than 1) or an absolute value in pixels (default is 0.25m). | | TriggerVelocity | `decimal?` | null | The swiping velocity in pixels per millisecond that triggers and calls the OnTrigger event on release (a flick), even if the swiping distance has not reached the Trigger point (default is 0, meaning disabled). | | AriaLabel | `string?` | null | Gets or sets the accessible label for the component, used by assistive technologies. | | Class | `string?` | null | Gets or sets the CSS class name(s) to apply to the rendered element. | | Dir | `BitDir?` | null | Gets or sets the text directionality for the component's content. | | ForceAnimation | `bool` | false | Gets or sets a value indicating whether the component's animations play at their full duration even when reduced motion is requested. | | HtmlAttributes | `Dictionary<string, object>` | new Dictionary<string, object>() | Captures additional HTML attributes to be applied to the rendered element, in addition to the component's parameters. | | Id | `string?` | null | Gets or sets the unique identifier for the component's root element. | | IsEnabled | `bool` | true | Gets or sets a value indicating whether the component is enabled and can respond to user interaction. | | Style | `string?` | null | Gets or sets the CSS style string to apply to the rendered element. | | TabIndex | `string?` | null | Gets or sets the tab order index for the component when navigating with the keyboard. | | Visibility | `BitVisibility` | BitVisibility.Visible | Gets or sets the visibility state (visible, hidden, or collapsed) of the component. | ## Public Members | Name | Type | Default Value | Description | | :--- | :--- | :------------ | :---------- | | UniqueId | `Guid` | Guid.NewGuid() | Gets the readonly unique identifier for the component's root element, assigned when the component instance is constructed. | | RootElement | `ElementReference` | | Gets the reference to the root HTML element associated with this component. | ## Enums ### BitSwipeOrientation Enum | Name | Value | Description | | :--- | :--- | :---------- | | None | 0 | No orientation lock for the swipe trap. | | Horizontal | 1 | Horizontal orientation lock of trapping the swipe action. | | Vertical | 2 | Vertical orientation lock of trapping the swipe action. | | Auto | 3 | Locks the trap to the first orientation the gesture moves along, trapping that axis and zeroing the other. | ### BitSwipeDirection Enum | Name | Value | Description | | :--- | :--- | :---------- | | Right | 0 | Swipe to right direction. | | Left | 1 | Swipe to left direction. | | Top | 2 | Swipe to top direction. | | Bottom | 3 | Swipe to bottom direction. | ### BitVisibility Enum | Name | Value | Description | | :--- | :--- | :---------- | | Visible | 0 | The content of the component is visible. | | Hidden | 1 | The content of the component is hidden, but the space it takes on the page remains (visibility:hidden). | | Collapsed | 2 | The component is hidden (display:none). | ### BitDir Enum | Name | Value | Description | | :--- | :--- | :---------- | | Ltr | 0 | Ltr (left to right) is to be used for languages that are written from the left to the right (like English). | | Rtl | 1 | Rtl (right to left) is to be used for languages that are written from the right to the left (like Arabic). | | Auto | 2 | Auto lets the user agent decide. It uses a basic algorithm as it parses the characters inside the element until it finds a character with a strong directionality, then applies that directionality to the whole element. | ## Sub Classes ### BitSwipeTrapEventArgs Properties The event arguments of the SwipeTrap events. | Name | Type | Default Value | Description | | :--- | :--- | :------------ | :---------- | | StartX | `decimal` | 0 | The horizontal start point of the swipe action in pixels, relative to the viewport. | | StartY | `decimal` | 0 | The vertical start point of the swipe action in pixels, relative to the viewport. | | DiffX | `decimal` | 0 | The horizontal difference of swipe action in pixels. | | DiffY | `decimal` | 0 | The vertical difference of swipe action in pixels. | | VelocityX | `decimal` | 0 | The horizontal velocity of the swipe action in pixels per millisecond. | | VelocityY | `decimal` | 0 | The vertical velocity of the swipe action in pixels per millisecond. | | PointerType | `string?` | null | The type of the pointer that performed the swipe action: "mouse", "touch" or "pen". | | IsCanceled | `bool` | false | Whether the swipe action ended by being canceled (e.g. the browser took the gesture over) instead of a normal release. Only meaningful in the OnEnd event. | | Duration | `decimal` | 0 | The elapsed time of the swipe action in milliseconds, measured from the moment it started. | ### BitSwipeTrapTriggerArgs Properties The event arguments of the SwipeTrap trigger event. | Name | Type | Default Value | Description | | :--- | :--- | :------------ | :---------- | | Direction | `BitSwipeDirection` | | The swipe direction in which the action triggered. | | DiffX | `decimal` | 0 | The horizontal difference of swipe action in pixels. | | DiffY | `decimal` | 0 | The vertical difference of swipe action in pixels. | | VelocityX | `decimal` | 0 | The horizontal velocity of the swipe action in pixels per millisecond. | | VelocityY | `decimal` | 0 | The vertical velocity of the swipe action in pixels per millisecond. | | PointerType | `string?` | null | The type of the pointer that performed the swipe action: "mouse", "touch" or "pen". | | Duration | `decimal` | 0 | The elapsed time of the swipe action in milliseconds, measured from the moment it started. | ## Examples \n**Basic**: ```razor
StartX: @swipeTrapEventArgsBasic?.StartX
StartY: @swipeTrapEventArgsBasic?.StartY
DiffX: @swipeTrapEventArgsBasic?.DiffX
DiffY: @swipeTrapEventArgsBasic?.DiffY
VelocityX: @swipeTrapEventArgsBasic?.VelocityX.ToString("0.00")
VelocityY: @swipeTrapEventArgsBasic?.VelocityY.ToString("0.00")
Duration: @swipeTrapEventArgsBasic?.Duration.ToString("0") ms
PointerType: @swipeTrapEventArgsBasic?.PointerType
IsCanceled: @swipeTrapEventArgsBasic?.IsCanceled
---
Triggered? @isTriggeredBasic
Trigger direction: @swipeTrapTriggerArgsBasic?.Direction
Trigger diffX: @swipeTrapTriggerArgsBasic?.DiffX
Trigger diffY: @swipeTrapTriggerArgsBasic?.DiffY
``` ```csharp private bool isTriggeredBasic; BitSwipeTrapEventArgs? swipeTrapEventArgsBasic; BitSwipeTrapTriggerArgs? swipeTrapTriggerArgsBasic; private void HandleOnStartBasic(BitSwipeTrapEventArgs args) { swipeTrapEventArgsBasic = args; } private void HandleOnMoveBasic(BitSwipeTrapEventArgs args) { swipeTrapEventArgsBasic = args; } private void HandleOnEndBasic(BitSwipeTrapEventArgs args) { swipeTrapEventArgsBasic = args; } private void HandleOnTriggerBasic(BitSwipeTrapTriggerArgs args) { isTriggeredBasic = true; swipeTrapTriggerArgsBasic = args; _ = Task.Delay(3000).ContinueWith(async _ => { isTriggeredBasic = false; swipeTrapEventArgsBasic = null; swipeTrapTriggerArgsBasic = null; await InvokeAsync(StateHasChanged); }); } ``` \n**Trigger**: ```razor
Trigger="0.5m"
(half of the container)
Direction: @triggerArgsFractional?.Direction
DiffX: @triggerArgsFractional?.DiffX
DiffY: @triggerArgsFractional?.DiffY
Trigger="80m"
(80 pixels)
Direction: @triggerArgsAbsolute?.Direction
DiffX: @triggerArgsAbsolute?.DiffX
DiffY: @triggerArgsAbsolute?.DiffY
``` ```csharp private BitSwipeTrapTriggerArgs? triggerArgsFractional; private BitSwipeTrapTriggerArgs? triggerArgsAbsolute; private void HandleOnTriggerFractional(BitSwipeTrapTriggerArgs args) { triggerArgsFractional = args; } private void HandleOnTriggerAbsolute(BitSwipeTrapTriggerArgs args) { triggerArgsAbsolute = args; } ``` \n**Flick**: ```razor
Flicked? @isFlicked
Flick direction: @swipeTrapTriggerArgsFlick?.Direction
Flick velocityX: @swipeTrapTriggerArgsFlick?.VelocityX.ToString("0.00")
Flick velocityY: @swipeTrapTriggerArgsFlick?.VelocityY.ToString("0.00")
Flick duration: @swipeTrapTriggerArgsFlick?.Duration.ToString("0") ms
``` ```csharp private bool isFlicked; private BitSwipeTrapTriggerArgs? swipeTrapTriggerArgsFlick; private void HandleOnTriggerFlick(BitSwipeTrapTriggerArgs args) { isFlicked = true; swipeTrapTriggerArgsFlick = args; _ = Task.Delay(3000).ContinueWith(async _ => { isFlicked = false; swipeTrapTriggerArgsFlick = null; await InvokeAsync(StateHasChanged); }); } ``` \n**OrientationLock**: ```razor
Horizontal lock
DiffX: @diffXHorizontalLock
DiffY: @diffYHorizontalLock
Vertical lock
DiffX: @diffXVerticalLock
DiffY: @diffYVerticalLock
Auto lock
DiffX: @diffXAutoLock
DiffY: @diffYAutoLock
``` ```csharp private decimal diffXHorizontalLock; private decimal diffYHorizontalLock; private decimal diffXVerticalLock; private decimal diffYVerticalLock; private decimal diffXAutoLock; private decimal diffYAutoLock; private void HandleOnMoveHorizontalLock(BitSwipeTrapEventArgs args) { diffXHorizontalLock = args.DiffX; diffYHorizontalLock = args.DiffY; } private void HandleOnEndHorizontalLock(BitSwipeTrapEventArgs args) { diffXHorizontalLock = 0; diffYHorizontalLock = 0; } private void HandleOnMoveVerticalLock(BitSwipeTrapEventArgs args) { diffXVerticalLock = args.DiffX; diffYVerticalLock = args.DiffY; } private void HandleOnEndVerticalLock(BitSwipeTrapEventArgs args) { diffXVerticalLock = 0; diffYVerticalLock = 0; } private void HandleOnMoveAutoLock(BitSwipeTrapEventArgs args) { diffXAutoLock = args.DiffX; diffYAutoLock = args.DiffY; } private void HandleOnEndAutoLock(BitSwipeTrapEventArgs args) { diffXAutoLock = 0; diffYAutoLock = 0; } ``` \n**Threshold & Throttle**: ```razor
Threshold="30"
(the first 30px are free)
DiffX: @diffXThreshold
DiffY: @diffYThreshold
Throttle="200"
(at most one move per 200ms)
Moves: @moveCountThrottle
DiffX: @diffXThrottle
DiffY: @diffYThrottle
``` ```csharp private decimal diffXThreshold; private decimal diffYThreshold; private int moveCountThrottle; private decimal diffXThrottle; private decimal diffYThrottle; private void HandleOnMoveThreshold(BitSwipeTrapEventArgs args) { diffXThreshold = args.DiffX; diffYThreshold = args.DiffY; } private void HandleOnEndThreshold(BitSwipeTrapEventArgs args) { diffXThreshold = 0; diffYThreshold = 0; } private void HandleOnMoveThrottle(BitSwipeTrapEventArgs args) { moveCountThrottle++; diffXThrottle = args.DiffX; diffYThrottle = args.DiffY; } private void HandleOnEndThrottle(BitSwipeTrapEventArgs args) { moveCountThrottle = 0; diffXThrottle = 0; diffYThrottle = 0; } ``` \n**Filtering**: ```razor
TouchOnly
(mouse drags are ignored)
DiffX: @diffXTouchOnly
DiffY: @diffYTouchOnly
SkipSelector
DiffX: @diffXSkip
DiffY: @diffYSkip
``` ```csharp private decimal diffXTouchOnly; private decimal diffYTouchOnly; private decimal diffXSkip; private decimal diffYSkip; private void HandleOnMoveTouchOnly(BitSwipeTrapEventArgs args) { diffXTouchOnly = args.DiffX; diffYTouchOnly = args.DiffY; } private void HandleOnEndTouchOnly(BitSwipeTrapEventArgs args) { diffXTouchOnly = 0; diffYTouchOnly = 0; } private void HandleOnMoveSkip(BitSwipeTrapEventArgs args) { diffXSkip = args.DiffX; diffYSkip = args.DiffY; } private void HandleOnEndSkip(BitSwipeTrapEventArgs args) { diffXSkip = 0; diffYSkip = 0; } ``` \n**Panel**: ```razor

Title

Item1
Item2
Item3
``` ```csharp private decimal diffXPanel; private bool isPanelOpen; private void OpenPanel() { isPanelOpen = true; } private void ClosePanel() { isPanelOpen = false; } private void HandleOnMovePanel(BitSwipeTrapEventArgs args) { diffXPanel = args.DiffX; } private void HandleOnEndPanel(BitSwipeTrapEventArgs args) { diffXPanel = 0; } private void HandleOnTriggerPanel(BitSwipeTrapTriggerArgs args) { if (args.Direction == BitSwipeDirection.Left) { diffXPanel = 0; ClosePanel(); } } private string GetPanelStyle() { return diffXPanel < 0 ? $"transform: translateX({diffXPanel}px)" : ""; } ``` \n**List**: ```razor
@foreach (int idx in itemsList) { var i = idx;
Delete
Item@(i + 1)
}
Reset ``` ```csharp private int deletingIndex = -1; private bool isListDialogOpen; private TaskCompletionSource? listTcs; private List itemsList = Enumerable.Range(0, 10).ToList(); private decimal[] diffXList = Enumerable.Repeat(0m, 10).ToArray(); private void HandleOnMoveList(BitSwipeTrapEventArgs args, int index) { diffXList[index] = args.DiffX; } private void HandleOnEndList(BitSwipeTrapEventArgs args, int index) { if (diffXList[index] < 60) { diffXList[index] = 0; } } private async Task HandleOnTriggerList(BitSwipeTrapTriggerArgs args, int index) { if (args.Direction == BitSwipeDirection.Right) { deletingIndex = index; listTcs = new(); isListDialogOpen = true; await listTcs.Task; isListDialogOpen = false; diffXList[index] = 0; deletingIndex = -1; } } private string GetRowStyle(int index) { var x = Math.Min(diffXList[index], 60); return x > 0 ? $"transform: translateX({x}px)" : ""; } private void HandleOnOkList() { if (deletingIndex != -1) { itemsList.Remove(deletingIndex); } listTcs?.SetResult(); } private void HandleOnCancelList() { listTcs?.SetResult(); } private void ResetList() { itemsList = Enumerable.Range(0, 10).ToList(); } ``` \n**Advanced**: ```razor
bit BlazorUI
Swipe left or right

Left Menu

Item1
Item2
Item3

Right Menu

Item1
Item2
Item3
``` ```csharp private decimal? diffXPanelAdvanced; private BitSwipeDirection? direction; private BitSwipeDirection? panelOpen; private void OpenPanelAdvanced(BitSwipeDirection swipeDirection) { if (panelOpen == swipeDirection) return; direction = null; panelOpen = swipeDirection; diffXPanelAdvanced = 0; } private void ClosePanelAdvanced() { panelOpen = null; diffXPanelAdvanced = null; } private void HandleOnMovePanelAdvanced(BitSwipeTrapEventArgs args) { diffXPanelAdvanced = args.DiffX; if (Math.Abs(args.DiffX) > 2 || Math.Abs(args.DiffY) > 2) { direction = Math.Abs(args.DiffX) >= Math.Abs(args.DiffY) ? args.DiffX > 0 ? BitSwipeDirection.Right : BitSwipeDirection.Left : args.DiffY > 0 ? BitSwipeDirection.Bottom : BitSwipeDirection.Top; } else { direction = null; } } private void HandleOnEndPanelAdvanced(BitSwipeTrapEventArgs args) { if (panelOpen.HasValue) { diffXPanelAdvanced = 0; } else { diffXPanelAdvanced = null; } } private void HandleOnTriggerPanelAdvanced(BitSwipeTrapTriggerArgs args) { if (args.Direction == BitSwipeDirection.Left) { if (panelOpen.HasValue is false || panelOpen == BitSwipeDirection.Right) { OpenPanelAdvanced(BitSwipeDirection.Right); } else if (panelOpen == BitSwipeDirection.Left) { ClosePanelAdvanced(); } } else if (args.Direction == BitSwipeDirection.Right) { if (panelOpen.HasValue is false || panelOpen == BitSwipeDirection.Left) { OpenPanelAdvanced(BitSwipeDirection.Left); } else if (panelOpen == BitSwipeDirection.Right) { ClosePanelAdvanced(); } } } private string GetLeftPanelAdvancedStyle() { if (panelOpen == BitSwipeDirection.Left && direction != BitSwipeDirection.Left) { return "transform: translateX(0px)"; } else if((panelOpen.HasValue is false && direction == BitSwipeDirection.Right) || (panelOpen == BitSwipeDirection.Left && direction == BitSwipeDirection.Left)) { return diffXPanelAdvanced switch { 0 or > 200 => "transform: translateX(0px)", < 0 and < 200 => $"transform: translateX({diffXPanelAdvanced}px)", > 0 => $"transform: translateX(calc(-100% + {diffXPanelAdvanced}px))", _ => string.Empty }; } return string.Empty; } private string GetRightPanelAdvancedStyle() { if (panelOpen == BitSwipeDirection.Right && direction != BitSwipeDirection.Right) { return "transform: translateX(0px)"; } else if ((panelOpen.HasValue is false && direction == BitSwipeDirection.Left) || (panelOpen == BitSwipeDirection.Right && direction == BitSwipeDirection.Right)) { return diffXPanelAdvanced switch { 0 or < -200 => "transform: translateX(0px)", > 0 => $"transform: translateX({diffXPanelAdvanced}px)", < 0 => $"transform: translateX(calc(100% - {(-1 * diffXPanelAdvanced)}px))", _ => string.Empty }; } return string.Empty; } ```