Buttons not expanding and positioning correctly when moving mouse to a new button or mouse moves over all the buttons

So i just wanna make it so that when you hover over a button it expands and pushes the other buttons away using tweens making it look smooth and clean. so far its alright but then i get the issue where if you hover over too many too quickly or you try to hover over a different button it just completely dies on me, i think my calculations on the positioning is bad plus to check the frames with the checkFrame function is literally a workaround on something i don’t actually know how to do properly.

Medal_OHd4szR9Zs

I’ve tired different ways to expand it like changing the way it checks for the size and keeping the original data of the buttons but that doesn’t seem to work. Really need help with this as i’ve run out of ideas on what to do. Any way to make it better or change something that might fix it?

Here’s my script for this:

------------// (V) Variables (V) \\------------

local mainFrame = script.Parent
local buttons = mainFrame.Buttons
local userInput = game:GetService("UserInputService")
local debounce = false -- idk if its needed yet, Added for now

------------// (T) Tweens (T) \\------------

local tweenService = game:GetService("TweenService")

-- Making a quick and easy tween function
local function Tween(obj, time, style, dir, goal)
	local tween = tweenService:Create(obj, TweenInfo.new(time, style, dir), goal)
	tween:Play()
	return tween
end

------------// (S) Script (S) \\------------

-- Stores the original size and position of each button
local buttonData = {}

for _, button in pairs(buttons:GetChildren()) do
	if button:IsA("Frame") then
		local icon = button:FindFirstChildOfClass("ImageButton")
		local textLabel = button:FindFirstChildOfClass("TextLabel")
		buttonData[button] = {
			isExpanded = false,
			Size = button.Size,
			Position = button.Position,
			IconPos = icon.Position,
			TextPos = textLabel.Position,
			TextSize = textLabel.Size
		}
	end
end

local function isTouching(b, oth)
	local pos1 = b.AbsolutePosition
	local size1 = b.AbsoluteSize
	local pos2 = oth.AbsolutePosition
	local size2 = oth.AbsoluteSize

	local left1, right1 = pos1.X, pos1.X + size1.X
	local top1, bottom1 = pos1.Y, pos1.Y + size1.Y
	local left2, right2 = pos2.X, pos2.X + size2.X
	local top2, bottom2 = pos2.Y, pos2.Y + size2.Y

	local touching = right1 > left2 and left1 < right2 and bottom1 > top2 and top1 < bottom2
	return touching
end

local function reverseTable(t)
	local rt = {}
	local n = #t
	for i = n, 1, -1 do
		table.insert(rt, t[i])
	end
	return rt
end

local function handleButtonHover(button, isExpanding)
	local data = buttonData[button]
	if not data then return end

	local otherButtons = {}
	for _,v in ipairs(buttons:GetChildren()) do
		if v:IsA("Frame") and v ~= button then
			table.insert(otherButtons, v)
		end
	end

	local textLabel = button:FindFirstChildOfClass("TextLabel")
	local imageButton = button:FindFirstChildOfClass("ImageButton")

	local originSize = data.Size
	local originPos = data.Position
	local originIconPos = data.IconPos
	local originTextPos = data.TextPos
	local absoluteSizeX = button.AbsoluteSize.X

	local textBounds = game:GetService("TextService"):GetTextSize(textLabel.Text, textLabel.TextSize, textLabel.Font, Vector2.new(originSize.X.Offset + 100, math.huge))

	local expandSize = UDim2.new(originSize.X.Scale, textBounds.X - 2, originSize.Y.Scale, originSize.Y.Offset)
	local expandPos = UDim2.new(originPos.X.Scale, originPos.X.Offset + (textBounds.X/2), originPos.Y.Scale, originPos.Y.Offset)
	local edgeDetermainRight = absoluteSizeX + expandSize.X.Offset -- Finds edge on the right
	local edgeDetermainLeft = absoluteSizeX / 2 -- Finds edge on the left
	local expandIconPos = UDim2.new(0, edgeDetermainLeft + 2.9, 0.5, imageButton.Size.Y.Offset / 2)
	local expandTextPos = UDim2.new(0, edgeDetermainRight / 1.65, 0.5, textLabel.Position.Y.Offset)

	local buttonPush = {}
	for _,touched in ipairs(otherButtons) do
		--print("[SYSTEM]: Checking for overlap with ["..touched.Name.."]")
		local checkFrame = Instance.new("Frame")
		checkFrame.BackgroundTransparency = 1
		checkFrame.Parent = button.Parent
		checkFrame.ZIndex = 16
		checkFrame.AnchorPoint = Vector2.new(0.5, 0.5)
		local size = UDim2.new(expandSize.X.Scale, expandSize.X.Offset * 1.7, expandSize.Y.Scale, expandSize.Y.Offset)
		local pos = UDim2.new(expandPos.X.Scale, expandTextPos.X.Offset * 1.4, expandPos.Y.Scale, expandPos.Y.Offset)
		checkFrame.Size = size
		checkFrame.Position = pos
		if isTouching(checkFrame, touched) then
			warn("[SYSTEM]: Button is overlapping with ["..touched.Name.."]! Button set for 'PUSH'")
			table.insert(buttonPush, touched)
		end
		checkFrame:Destroy()
	end

	local totalOffset = expandSize.X.Offset
	local systemmsg = false

	if isExpanding then
		print("[SYSTEM]: Starting Tweens")
		local reverse = reverseTable(buttonPush)
		for _, v in ipairs(reverse) do
			if systemmsg == false then
				systemmsg = true
				print("[SYSTEM]: Pushing Buttons...")
			end
			local pushPos = UDim2.new(v.Position.X.Scale, v.Position.X.Offset + totalOffset, v.Position.Y.Scale, v.Position.Y.Offset)
			wait()
			Tween(v, 0.2, Enum.EasingStyle.Sine, Enum.EasingDirection.InOut, {Position = pushPos})
		end
		wait(.02)
		print("[SYSTEM]: Tweening...")
		button.UIStroke.UIGradient.stroke.Enabled = true
		Tween(button.UIStroke, 0.2, Enum.EasingStyle.Sine, Enum.EasingDirection.InOut, {Transparency = 0})
		Tween(button, 0.2, Enum.EasingStyle.Sine, Enum.EasingDirection.InOut, {Size = expandSize, Position = expandPos})
		Tween(imageButton, 0.2, Enum.EasingStyle.Sine, Enum.EasingDirection.InOut, {Position = expandIconPos})
		Tween(textLabel, 0.2, Enum.EasingStyle.Sine, Enum.EasingDirection.InOut, {Position = expandTextPos, TextTransparency = 0})
		print("[SYSTEM]: Complete!")
	else
		print("[SYSTEM]: Starting Tweens")
		print("[SYSTEM]: Tweening...")
		button.UIStroke.UIGradient.stroke.Enabled = false
		Tween(button.UIStroke, 0.1, Enum.EasingStyle.Sine, Enum.EasingDirection.InOut, {Transparency = 1})
		Tween(button, 0.2, Enum.EasingStyle.Sine, Enum.EasingDirection.InOut, {Size = originSize, Position = originPos})
		Tween(imageButton, 0.2, Enum.EasingStyle.Sine, Enum.EasingDirection.InOut, {Position = originIconPos})
		Tween(textLabel, 0.1, Enum.EasingStyle.Sine, Enum.EasingDirection.InOut, {Position = originTextPos, TextTransparency = 1})
		wait(.02)
		for _, v in ipairs(buttonPush) do
			if systemmsg == false then
				systemmsg = true
				print("[SYSTEM]: Returning Buttons...")
			end
			local pushPos = UDim2.new(v.Position.X.Scale, v.Position.X.Offset - totalOffset, v.Position.Y.Scale, v.Position.Y.Offset)
			wait()
			Tween(v, 0.2, Enum.EasingStyle.Sine, Enum.EasingDirection.InOut, {Position = pushPos})
		end
		wait(.2)
		print("[SYSTEM]: Complete!")
	end
end

for _, button in pairs(buttons:GetChildren()) do
	if button:IsA("Frame") then
		button.MouseEnter:Connect(function()
			handleButtonHover(button, true)
		end)
		button.MouseLeave:Connect(function()
			handleButtonHover(button, false)
		end)
	end
end

you should be able to accomplish this somewhat easily using a ui list layout, list layouts automatically position elements in the list in a way that they don’t overlap and go one after another so changing the size of one of them will automatically position the buttons that come after it. here’s an example place that i made to show how you could use this.
ExpandingUIButtons.rbxl (66.2 KB)
i didn’t bother optimizing the code because its just for demonstration.

1 Like

This works, but i had a question about moving it, if i do it this way can i still achieve the same effect as i did before? you can see when i hover over a button that each button has a slight delay before moving. I wanna try and keep that animation if possible with the list layout.

I’m unaware if you can use tween survive on parts in a ui list layout however the delay effect is certainly possibly

I’m currently trying to figure out how to do it but it seems like uilistlayout doesn’t let me move them? If you could provide an example please do, just wanna try figure this out.

because of the way UI list layouts work there probably isnt a way to have the movement of the next buttons delayed the way you have it in the video. the closest you could probably get is by messing with the padding but that’s not really a great solution, thats all i could think of.
another messy kind of solution would be to make the actual buttons transparent and give each one a frame that is the same size as itself parented under the button. then whenever you hover over a button go to all the following buttons inner frames and slightly position them to the left for a moment before readjusting them to give the delayed effect.

1 Like

ahh i see, well i guess im just going to have to live with it without that delayed animation bit. If there are any other solutions please let me know. Thank you though!

1 Like

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