Custom ScrollingFrame but the elements on a circle

I came across this video:

and I want to replicate this myself. but I ran into the problem that I don’t know how to implement such a thing so that the elements it scrolls are positioned on a circle, and that it is not exactly like ScrollingFrame but bound to each frame and when the player spins the wheel down, the whole canvas shifts exactly on one frame and not on another value.

In addition, I also need to consider that elements that are farther from the center (of the selected element) should be rotated to the right side and change their transparency.

Any advice would be welcomed. i’m not asking you to write a ready-made script, just help figure out how to do it, but would appreciate it if someone would go to such lengths :3

1 Like

I think you want to use the UI Page layout, that is what it seems to be, it has all of the options that are required in the video, try testing it out and see if its right!

Okay, I’ll try to look more closely at the parameters of this item.

ok, but that’s not exactly what I had in mind. it has similar parameters, but it can only move vertically and horizontally. and I need it to move along the border of a circle. and this circle itself has a 1/4th of a fraction, which is visible on the screen if you mentally draw a line, along the centers of the frames he moves with the mouse wheel.

I see, maybe this could be scripting, the animations seem pretty advanced for a UI, you probably will have to script it in some way, using tweening, and more.

I know it should be used, but I would like at least some tiny tips on how to implement positioning of UI elements on a circle, but not just positioning, but also their dynamic change of position on demand (event).

Sine and Cosine, those are going to be your best friend. Although I do not know a bunch about circle Gui’s I believe you have to use Sin and Cosine.

1 Like

Well, while I was looking for answers on the internet, the trigonometry option came up a lot. but I don’t know how it works myself. saddly :frowning:

local y = 30


print(math.sin(y)) -- that will give you the value for the y and just do the same for the x and you should be able to get the circle motion you wanted
1 Like

Hey, I remember seeing this post and was bored so I made it. You’d need to set up your own input controlling and such but I think you needed help with the math, it was surprisingly relatively simple.

local tweenService = game:GetService('TweenService')

local container = script.Parent

local slots = {}

local TOTAL_SPAN_DEGREES = 90
local DURATION = 0.25
local EASING_STYLE = Enum.EasingStyle.Sine
local EASING_DIRECTION = Enum.EasingDirection.InOut
local MIN_SIZE = UDim2.fromOffset(25, 25)
local MAX_SIZE = UDim2.fromOffset(50, 50)

local FRAME_COUNT = 8

local currentFocus = 0
local currentFocusIndex = 0

local totalSpanRads = math.rad(TOTAL_SPAN_DEGREES)

for i = 1, FRAME_COUNT do
	local frame = Instance.new('Frame')
	frame.Size = UDim2.new(0, 10, 0, 10)
	frame.AnchorPoint = Vector2.one * 0.5
	frame.Parent = script.Parent
	
	slots[i] = frame
end

local function lerp(a, b, t)
	return a + (b - a) * t
end

local function getValue(value, style, direction)
	return tweenService:GetValue(value, style, direction)
end

local last

local function focus(targetIndex: number) -- this does the actual interpolation and such, call this to actually scroll it to the targetIndex
	local fixedValue = math.clamp(targetIndex, 1, FRAME_COUNT) -- clamp it so we don't exceed the number of frames
	
	if currentFocusIndex == fixedValue then -- same value as before so we don't wanna do anything
		return
	end
	
	currentFocusIndex = fixedValue
	targetIndex = fixedValue
	
	local now = os.clock()
	last = now
	
	local valueNow = currentFocus
	
	repeat
		task.wait()
		
		local targetIndex = lerp(valueNow, targetIndex, getValue(math.min((os.clock() - now) / DURATION, 1), EASING_STYLE, EASING_DIRECTION))
		currentFocus = targetIndex
		
		for index, slot in slots do
			local diff = targetIndex - index -- difference between this index and the target index which is used to determine at what angle to position the frames
			
			local angle = diff / FRAME_COUNT * totalSpanRads - (math.pi * 0.75) -- angle of the position from the origin of the frame; subtract 0.75 * pi because we are using the top-left corner as the "target" corner
			
			slot.Transparency = math.clamp(math.abs(diff * 0.5), 0, 1) -- set the transparency to be 0 when this is the current focus, and to 0.5 when it's the next focus and 1 when it's not the next focus
			slot.Size = MAX_SIZE:Lerp(MIN_SIZE, getValue(math.clamp(math.abs(diff) + 0.5, 0, 1), EASING_STYLE, EASING_DIRECTION)) -- same as above but for size
			slot.ZIndex = -math.abs(diff) -- set the z index so the current focus is on top of the previous and next focus
			slot.Rotation = diff * 22.5
			
			
			local x = math.cos(angle) * 100 -- x position at the angle
			local y = math.sin(angle) * 100 -- y position at the angle
			
			slot.Position = UDim2.fromOffset(x + 0.5 * container.AbsoluteSize.X, y + 0.5 * container.AbsoluteSize.Y) -- finally set the position
		end
	until os.clock() - now > DURATION or last ~= now
end

script.Parent.InputChanged:Connect(function(input) -- basic input thing, you can implement your controls however you want
	if input.UserInputType ~= Enum.UserInputType.MouseWheel then
		return
	end
	
	focus(currentFocusIndex + math.sign(input.Position.Z))
end)

focus(1)
3 Likes

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