How to snap frames into each other or other GUI elements?

You can write your topic however you want, but you need to answer these questions:

  1. What do you want to achieve? Keep it simple and clear!
    I want to be able to snap frames into each other after dragging and releasing them. Like a drag and drop inventory system.
  2. What is the issue? Include screenshots / videos if possible!
    I can’t figure out how to determine which frame the draggable frame should snap to.
  3. What solutions have you tried so far? Did you look for solutions on the Developer Hub?
    I currently have a draggable frame. I have looked all over the internet and cannot find any help to describe the process of determing which frame to snap to.

Here is my current code for a draggable frame

local UIS = game:GetService("UserInputService")

local frame = script.Parent

local Dragtoggle = nil
local dragSpeed = 0.01
local dragstart = nil
local startPOS = nil

local function updateInput(input)
	
	local delta = input.Position - dragstart
	local pos = UDim2.new(startPOS.X.Scale, startPOS.X.Offset + delta.X,
		startPOS.Y.Scale, startPOS.Y.Offset + delta.Y)
	game:GetService("TweenService"):Create(frame, TweenInfo.new(dragSpeed), {Position = pos}):Play()
end

frame.InputBegan:Connect(function(input)
	
	if (input.UserInputType == Enum.UserInputType.MouseButton1) then
		
		Dragtoggle = true
		dragstart = input.Position
		input.Changed:Connect(function()
			startPOS = frame.Position
			if input.UserInputState == Enum.UserInputState.End then
				
				Dragtoggle = false
				
			end 
			
		end)
		
	end
	
end)

UIS.InputChanged:Connect(function(input)
	
	if input.UserInputType == Enum.UserInputType.MouseMovement then
		
		if Dragtoggle then
			updateInput(input)
			
			
		end
		
	end
	
end)



I want to be more clear on what I’m trying to achieve. In an inventory system with multiple frames, I want to be able to drag an item (frame) within the tiles and drop it with the item going to the nearest frame. I cannot figure out how to determine which frame the item is closest too. From there I have an idea on how to snap the two elements. However, all help is appreciated.

Edit: I just read your explanation of what you mean by “snapping”.
You could loop through all the item slots and find the distance between the dragged frame and the slot, and then pick the one with the minimum distance. (math.min would be useful to do this easily)

1 Like

I currently have this so far


local draggable = script.Parent.draggable

local Frame1 = script.Parent.slot1
local Frame2 = script.Parent.slot2
local Frame3 = script.Parent.slot3


function CalculateDistance1()

	local distance = draggable.AbsolutePosition - Frame1.AbsolutePosition

	return distance

end

print(CalculateDistance1())

function CalculateDistance2()

	local distance = draggable.AbsolutePosition - Frame2.AbsolutePosition

	return distance

end

print(CalculateDistance2())

function CalculateDistance3()

	local distance = draggable.AbsolutePosition - Frame3.AbsolutePosition

	return distance

end

print(CalculateDistance3())


math.min(CalculateDistance1(), CalculateDistance2(), CalculateDistance3())

however I’m wondering how to get a singular distance value from the absolutePosition. In order for the Math.min to work

To get a single number you can use the Pythagorean theorem: distance = math.sqrt(xDifference^2 + yDifference^2).

1 Like

Thanks so much! I don’t know why I didn’t think of that. I’m going to attach my code incase anyone could want it in the future.

local UIS = game:GetService("UserInputService")

local frame = script.Parent

local Dragtoggle = nil
local dragSpeed = 0
local dragstart = nil
local startPOS = nil

local function updateInput(input)
	
	local delta = input.Position - dragstart
	local pos = UDim2.new(startPOS.X.Scale, startPOS.X.Offset + delta.X,
		startPOS.Y.Scale, startPOS.Y.Offset + delta.Y)
	game:GetService("TweenService"):Create(frame, TweenInfo.new(dragSpeed), {Position = pos}):Play()
end
--------------------------------------------------------------------
frame.InputBegan:Connect(function(input)
	
	if (input.UserInputType == Enum.UserInputType.MouseButton1) then
		
		Dragtoggle = true
		dragstart = input.Position
		
		
	end
	
end)

frame.InputEnded:Connect(function(input)
	startPOS = frame.Position
	 -- input ended. calculates distance

		Dragtoggle = false


		

		local Frame1 = script.Parent.Parent.slot1
		local Frame2 = script.Parent.Parent.slot2
		local Frame3 = script.Parent.Parent.slot3


		local function CalculateDistance1()

			local xDifference = frame.AbsolutePosition.X - Frame1.AbsolutePosition.X
			local yDifference = frame.AbsolutePosition.Y - Frame1.AbsolutePosition.Y

			local distance = math.sqrt(xDifference^2 + yDifference^2)
			return distance

		end
		local function CalculateDistance2()

			local xDifference = frame.AbsolutePosition.X - Frame2.AbsolutePosition.X
			local yDifference = frame.AbsolutePosition.Y - Frame2.AbsolutePosition.Y

			local distance = math.sqrt(xDifference^2 + yDifference^2)
			return distance

		end
		local function CalculateDistance3()

			local xDifference = frame.AbsolutePosition.X - Frame3.AbsolutePosition.X
			local yDifference = frame.AbsolutePosition.Y - Frame3.AbsolutePosition.Y

			local distance = math.sqrt(xDifference^2 + yDifference^2)
			return distance

		end

		local lowestvalue = math.min(CalculateDistance1(), CalculateDistance2(), CalculateDistance3())

		if lowestvalue == CalculateDistance1() then
			frame.Position = Frame1.Position
		end
		if lowestvalue == CalculateDistance2() then
			frame.Position = Frame2.Position
		end
		if lowestvalue == CalculateDistance3() then
			frame.Position = Frame3.Position
		

	end 

end)


UIS.InputChanged:Connect(function(input)
	
	if input.UserInputType == Enum.UserInputType.MouseMovement then
		
		if Dragtoggle then
			updateInput(input)
			
			
		end
		
	end
	
end)

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.