How to keep the original position when changing the anchor point?

When I change the anchor point of the frame, it moves. However, the position remains the same except for the absolute position. The problem is that I cannot set the absolute position as it’s read only. How then I can keep the original position of my GUI when changing the anchor point?

I already tried the solution from this post but did not work and instead it just moved my GUI out of the screen.


So far I managed to write this:

function Util.SetAnchorPointWithoutMoving(gui: GuiObject, desiredAnchorPoint: Vector2)
	-- Save previous position
	local oldAP = gui.AbsolutePosition
	
	-- Set the anchor point
	gui.AnchorPoint = desiredAnchorPoint
	
	-- Correct the position
	local newAP = gui.AbsolutePosition
	local relative = oldAP - newAP
	gui.Position += UDim2.fromOffset(relative.X, relative.Y)
end

However, for some reason the GUI still moves by like 0.5 pixels.

Are you sure the position is changing in the video? It seems to stay the same, it’s just that the border moves.

It’s actually moving by roughly ~1 pixel, I know this because I was observing the absolute position in the explorer.

I am unsure to why it is important for your Ui to maintain the same position when adjusting the anchor point, but I am pretty sure that this is something you cannot change.

AnchorPoint changes the origin point of the Frame

0.5,0.5 means that the position you previously set it to is now the exact Center of your Frame.

By default (0,0) the position is TopLeft, so the Frame is actually “sticking out” 90 pixels to the right and 80 pixels below its position.

So if you want to change it to 0.5,0.5 and maintain your visual position, you need to add half the size of your Frame to the position. So position would be along the lines of:

UDim2.new(OldScaleX, OldOffsetX + 45, OldScaleY, OldOffsetY + 40)

Or since this is a script, here’s a calculated version of it that should work for your function there.

gui.Position = UDim2.fromOffset(oldAP.X + (gui.AbsoluteSize.X * desiredAnchorPoint.X), oldAP.Y + (gui.AbsoluteSize.Y * desiredAnchorPoint.Y))

Edited the function to make it so that it will work with any AnchorPoint now and now just /2 for 0.5,0.5

Edit: to clarify why your original function doesn’t work, AnchorPoint does NOT change the position of a GuiObject. It just changes where it’s rendered from. By default it renders the pixels of your GuiObject from the Top Left. In this case, the center is where it’s rendered from.

2 Likes

Your script seems to be pushing the gui left and up, I am not sure why.

I made one mistake of leaving desiredAnchorPoint.Y in the X side of the offset calculation, but besides from that, there should be no reason it’s doing that. What anchorpoint are you setting? 0.5,0.5?

Edit: Pushing it left should be happening, but pushing it up…? You make it sound like the Y is going down. Or is the frame itself moving down?

1 Like

Since you hearted this it brought it back to my attention so I tested it.

eaVuWE0taU

The green part is where it was first.

It tweens to AnchorPoint 0.5,0.5 first,
then it tweens to the solution I gave you.

-- Same code for position, just changed it from TweenPosition
gui.Position = UDim2.fromOffset(oldAP.X + (gui.AbsoluteSize.X * desiredAnchorPoint.X), oldAP.Y + (gui.AbsoluteSize.Y * desiredAnchorPoint.Y))

For some reason when I change 0.5, 0.5 to 0.5, 0, it moves the GUI to the left.

RobloxStudioBeta_V9DNOSmlpv

That GIF is a bit confusing since it’s too fast, but if you’re saying what I think you’re saying, you’re using the code I messed up a bit.

If I had to guess, the first parameter in UDim2.fromOffset is:

oldAP.X + (gui.AbsoluteSize.X * desiredAnchorPoint.Y)

and not

oldAP.X + (gui.AbsoluteSize.X * desiredAnchorPoint.X)

Like it should be.
Since with the proper parameters it works fine for me

RobloxStudioBeta_DbySP4ZTgl

I did use your function correctly, but it doesn’t work as intended. In the meantime someone figured out slightly different way to approach this problem and their solution works almost perfectly but there are few cases where it doesn’t work as well.

local originalPosition = frame.AbsolutePosition

local function setAnchorPoint(gui, desiredAnchor)
    local anchorInfluence = desiredAnchor * gui.AbsoluteSize
    gui.AnchorPoint = desiredAnchor
    gui.Position = UDim2.fromOffset(anchorInfluence.X + originalPosition.X, anchorInfluence.Y + originalPosition.Y)
end

It involves saving the original position.

This function fails in these cases:

RobloxStudioBeta_2DnTgl1UEZ

While it works in the following cases:

RobloxStudioBeta_KdAWN5tgxR

So you want there to be no minute “shift” in the UI as you move it around?

Yes, I want to have an option in my plugin that will keep the position of the anchor point as sometimes I have an GUI element perfectly positioned but with incorrect anchor point, and when I change the anchor point it’s really annoying to have to reposition the GUI element back to where it was.

Hmm… Alright let me see what I can do.

Are you okay with using Scale for position instead of offset?

Yes, the scale can be always converted back to offset if possible.

Here’s what I’ve whipped up, including my testing code. I found no issues with anything, nor any slight stammers in the Frame.

Edit: whoops?

local function SetAnchorPoint(Gui, DesiredAnchor)
	local Canvas = Gui.Parent -- Object's Parent GUI (AbsoluteSize needed for Scaling.)
	local ParentSize = Canvas.AbsoluteSize
	local ChildSize = Gui.AbsoluteSize
	local ChildPosition = Gui.AbsolutePosition
	
	local CorrectionOffsetX = ChildSize.X * DesiredAnchor.X
	local CorrectionOffsetY = ChildSize.Y * DesiredAnchor.Y
	
	local CorrectedUDim2 = UDim2.fromScale((ChildPosition.X + CorrectionOffsetX) / ParentSize.X, (ChildPosition.Y + CorrectionOffsetY) / ParentSize.Y)
	Gui.AnchorPoint = DesiredAnchor
	Gui.Position = CorrectedUDim2
end

task.wait(5)

local Anchors = {
	Vector2.new(0.5,0.5),
	Vector2.new(1,1),
	
	Vector2.new(0.5,0),
	Vector2.new(0,0.5),
	
	Vector2.new(1,0),
	Vector2.new(0,1),
	
	Vector2.new(1,0.5),
	Vector2.new(0.5,1),
}

for _,V in pairs(Anchors) do
	SetAnchorPoint(script.Parent.Frame, V)
	task.wait(0.25)
end

It works perfectly, but it doesn’t seem to work when parented to another GUI element because it moves out far away.

RobloxStudioBeta_iW2Mv2pyfn

Haahhh I’ll work on a fix really quick.

1 Like

I’m such an idiot, must still be tired, the fix is so simple and might as well be done anyways when converting offsets to scaled values…

local function SetAnchorPoint(Gui, DesiredAnchor)
	local Canvas = Gui.Parent -- Object's Parent GUI (AbsoluteSize needed for Scaling.)
	local ParentSize = Canvas.AbsoluteSize
	-- tada:
	local ParentPosition = Canvas.AbsolutePosition
	local ChildSize = Gui.AbsoluteSize
	local ChildPosition = Gui.AbsolutePosition
	-- tada:
	ChildPosition = ChildPosition - ParentPosition
	
	local CorrectionOffsetX = ChildSize.X * DesiredAnchor.X
	local CorrectionOffsetY = ChildSize.Y * DesiredAnchor.Y
	
	local CorrectedUDim2 = UDim2.fromScale((ChildPosition.X + CorrectionOffsetX) / ParentSize.X, (ChildPosition.Y + CorrectionOffsetY) / ParentSize.Y)
	Gui.AnchorPoint = DesiredAnchor
	Gui.Position = CorrectedUDim2
end
4 Likes

Here is my complete solution (which includes support for viewport when parent is not available).

--!strict

local Workspace = game:GetService("Workspace")

local Util = {}

-- PS stands for position and size
function Util.GetParentPS(gui: GuiObject): (Vector2, Vector2)
	local parent = gui.Parent
	if parent then
		if parent:IsA("GuiObject") then
			return parent.AbsolutePosition, parent.AbsoluteSize
		end
	end
	return Vector2.zero, Workspace.Camera.ViewportSize
end

function Util.SetAnchorPointWithoutMoving(gui: GuiObject, desiredPoint: Vector2)
	local parentPosition, parentSize = Util.GetParentPS(gui)
	
	local childSize = gui.AbsoluteSize
	local childPosition = gui.AbsolutePosition
	
	-- Calculate the relative position
	childPosition = childPosition - parentPosition

	local correctionOffsetX = childSize.X * desiredPoint.X
	local correctionOffsetY = childSize.Y * desiredPoint.Y

	local correctedPosition = UDim2.fromScale(
		(childPosition.X + correctionOffsetX) / parentSize.X,
		(childPosition.Y + correctionOffsetY) / parentSize.Y
	)
	
	gui.AnchorPoint = desiredPoint
	gui.Position = correctedPosition
end

return Util