Start typing to search packages!
greatuidrag
By @coffilhg
Roblox
Mirrored from WallyGreatUIDrag
Draggable UIs made easy! Supports desktop and mobile, GUI insets, anchor points, viewport resizing, and CSS-like padding limits. Console support is untested but should work for cursor-based input.
Available Here!
-
This repository ~ src/init.luau
-
GreatUIDrag = "coffilhg/greatuidrag@2.0.0" -
GreatUIDrag = "github.com/Coffilhg/Useful-Modules@GreatUIDrag/2.0.0"
Features
- Smooth spring interpolation (no Tween spam, thx to @crusherfire)
- CSS-like padding
- Optionally multiple drag handles
- Sensibility, speed and damping control
- Automatically handles
- IgnoreGuiInset changes
- AnchorPoint
- Viewport resizing (Non-fullscreen players and mobile players rotating their screen)
- Events for contact, start, release, completion and position changes (Click vs drag detection)
Usage
Place the module and its dependencies somewhere accessible (e.g. ReplicatedStorage):
Here's how it looks when you run
rojo serve test.project.jsonof this repository(you must have rojo, wally, wally-package-types and previously download dependencies (e.g. with
wally install && rojo sourcemap test.project.json --output sourcemap.json && wally-package-types -s sourcemap.json Packages/))
Packages
├── _Index
├── CoffeeObjects
├── GUICompatibility
├── GreatUIDrag
│ └── _Spring
└── LemonSignal
Then require it:
local GreatUIDrag = require(path.To.GreatUIDrag)
Quick Start
GreatUIDrag(
Frame, -- GuiObject to move
Frame.Header -- Drag handle (TextButton / ImageButton) -- Optional for V2.0.0+
)
or more explicit
local drag = GreatUIDrag(
Frame, -- GuiObject to move
Frame.Header, -- Drag handle (TextButton / ImageButton)
1, -- Damping (Less = more like jelly)
27, -- Speed (More = faster interpolation)
{UDim.new(0.04, 8)}, -- Padding (Top, Right, Bottom, Left)
1, -- Sensibility (Useful for UI bigger than screen)
0.24 -- Drag Start Delay (Useful to ignore simple clicks)
)
API
GreatUIDrag(
dragElement: GuiObject,
dragHandle: ( DragHandle | {DragHandle} )?,
damping: number?,
speed: number?,
paddingOptions: ( number | UDim | {number | UDim} )?,
sensibilityMultiplier: number?,
dragStartDelay: number?
): DragElement
Parameters
dragElement
The UI object that will move.
dragHandle (optional for V2.0.0+)
A
TextButtonorImageButtonOR an array (table) of them
More can be added using
:AddHandlefor V2.0.0+
damping (optional)
Spring damping factor Default =
1Lower = bouncier Higher = stiffer
speed (optional)
Spring speed
Default =
27 / sensibilityMultiplier
paddingOptions (optional)
CSS-like syntax:
| Input | Result |
|---|---|
Empty (nil) or {} | Defaults to 30px padding on all sides |
x or {x} | All sides have x padding |
{y, x} | Horizontal (top+bottom) padding is y; Vertical (right+left) padding is x |
{y1, x, y2} | Top padding = y1, sides (right+left) padding = x, Bottom padding = y2 |
top, right, bottom, left | full control |
Each value can be an UDim or a number, which will be converted into UDim.new(0, number) (pixels)
sensibilityMultiplier (optional)
Drag sensitivity
Default =
1
dragStartDelay (optional)
Minimal Press & Hold time (Delay) for Drag to start
OnDragStartedFires once this time has passed, after theOnDragContact, which fires the instant the DragElement is PressedDefault =
0.24
Returned Object (DragElement) Methods
local MyDragElement = GreatUIDrag(...)
MyDragElement:AddHandle(dragHandle: ( DragHandle | {DragHandle} )?)
"
type DragHandle = (TextButton | ImageButton | GuiButton)"Adds a
TextButton/ImageButton/GuiButton, or an array (table) of them, as additional drag handles (or the main one if none were added previously at creation).
MyDragElement:Destroy()
Cancels active drag on this
DragElementand disconnects all signals involved (including all Event signals)It is silent, without
OnDragReleased/OnDragCompletedCalled automatically when the
dragElementis destroyed (anything that triggersdragElement.Destroying)Does not destroy the
dragElementor the handles themselves.(no arguments)
MyDragElement:InitDrag(triggerHandle: DragHandle)
Activates dragging (as if client started using one of the drag handles), if this
DragElementisn't active already
triggerHandleis passed to the events
MyDragElement:IsDestroyed(): boolean
Returns a boolean on whether the
DragElementis destroyed. If it is, it shall not be used and the reference shall benillified or overwritten(no arguments)
MyDragElement:Reposition()
Immediately repositions the element to the current spring state
(no arguments)
MyDragElement:SetPosition(position: UDim2?)
Places the element immediately and resets the spring, without animating
If
typeof(position) ~= "UDim2"(e.g. not given), it uses the mouse location
MyDragElement:Stop()
Stops active dragging (client input signals) but allows spring to reach the latest goal
(no arguments)
Returned Object (DragElement) Events (V2.0.0+)
All of the events are Signal Objects instantiated via LemonSignal
(a new dependency introduced in v2.0.0)
| Event | Arguments | Fires |
|---|---|---|
OnDragContact | triggerHandle | A handle was pressed; First contact with the dragElement made |
OnDragStarted | triggerHandle | The pointer moved after dragStartDelay (a real drag); Fires after OnDragContact and dragStartDelay if not OnDragReleased |
OnDragReleased | finalGoal, wasDragged, triggerHandle | Released, or another element started dragging; the spring keeps going; Fires after dragElement gets released or another dragElement is starting, continues moving towards the final goal |
OnDragCompleted | finalGoal, wasDragged, triggerHandle | The spring settled at the final goal; Fires after the final goal was reached |
OnPositionChanged | currentAbsoluteX, currentAbsoluteY | The element was repositioned (the offsets written to Position); Fires on Reposition / UIS.InputChanged while used |
wasDragged is true if OnDragStarted fired, otherwise nil, so you can tell a click from a drag:
wasDraggedis an alias forIsDragStartFiredBeforeReleased(how it is commented in the source)
Example usage:
MyDragElement.OnDragReleased:Connect(function(finalGoal, wasDragged, handle)
if not wasDragged then
print("just a click on", handle)
end
end)
Notes
- Only one active drag is allowed at a time (intentional)
- Pressing another element's handle stops this one (its
OnDragReleasedfires)
- Pressing another element's handle stops this one (its
- Uses
RunService.Heartbeatonly while active or on-the-way to complete the latest goal - Automatically updates bounds based on:
- Viewport resize (
game.Workspace.CurrentCamera:GetPropertyChangedSignal("ViewportSize")) - AnchorPoint change (
dragElement:GetPropertyChangedSignal("AnchorPoint")) - GuiInset change (
GUICompatibility:GetGuiInsetSize().Value)
- Viewport resize (
- The element won't move unless it's inside a
ScreenGui. This is re-checked when its parent changes - To react to a
DragElementbeing destroyed, connect to thedragElement's ownDestroyingevent. Check:IsDestroyed()if you need to know later - Setting
dragElement.Parent = nilis notdragElement:Destroy(), therefore it does not automatically callDragElement:Destroy()
Dependencies
- GUICompatibility
- CoffeeObjects
- _Spring (by @crusherfire | https://www.roblox.com/users/80102935/profile)
- LemonSignal
License & Attribution
This module is licensed under the Mozilla Public License 2.0 (MPL-2.0).
What this means for Roblox Developers:
- Use & Modify: You can freely use this module in any public, private, or commercial Roblox game.
- File-Level Copyleft: If you modify the source code of this module itself, you must make your modified version of the module publicly available under the MPL 2.0.
- No Viral Code Leakage: Including this module in your game does not force you to open-source your other game scripts, UI layouts, or proprietary codebase.
See the full terms in the LICENSE file.
Attribution to all dependencies is included in Notice
Copyright © 2026 @Coffilhg (Roblox UserId 517222346)
Package Details
Install command (Click to copy)
Version
2.0.0
License
MPL-2.0
Usable with conditions
File-level copyleft: if you modify this package's own source files, those modified files must be made available under MPL-2.0. Using it unmodified in a closed-source game is fine.
Automated license review — not legal advice.
