In Cased You Missed the Previous Tutorial:
Hello developers!
Majority of you voted for more tutorials on the previous one, so here it is! If you do not know already, this tutorial is dedicated to teaching you how to create certain aspects of websites. They were not wrong when they said anything is possible in Roblox (unless it is unsupported by the engine, but we leave this part out when talking to non-Robloxians ).
Disclaimers / Knowledge Required
1
This technique only works without the ScrollBar. So please set
ScrollBarThickness
to 0.
2
Tween Service knowledge is required.
Overview
Today, we’ll be incorporating useful features to Scrolling Frames. In total there are exactly 3 of these custom features:
- Topbar collapsing when scrolling up
- Simple smooth scroll
- Outline (click to scroll to sections and more!)
As you can see, the blue “Shop” topbar on the top tweens out when scrolled down, but appears when scrolled up. Along with the smooth scrolling, notice the outline at the right showing the sections of the Shop. When I scroll to that section, the blue select moves to the name of that section in the outline. When I click on the button in the outline, the frame scrolls to that section.
This can be very useful especially when you have a lot of content in your Scrolling Frame. Players can easily skip to a section and the overall UX (user experience) improves when these additions.
Setup
OK, so the overall layout is a bit complex but hopefully this image gives you an idea of where things should go. Of course, you do not need the exact sizes and positions, only the layout is important.
(The color of the box next to objects in the Explorer is represented in the Viewport by the same colored border).
This is how I created the basic layout. You do not have to get the exact sizes expect for things that have to cover the whole screen like #1.
Step-By-Step Setup
- Add a Frame to the ScreenGui; properties BackgroundTransparency and Size to 1 and {1, 0, 1, 0}, respectively.
- Add a Frame to the “Frame” from #1; rename it to “Topbar”, resize it to {1, 0, 0, 75}, and color it to anything you want.
- Add a text label inside the Topbar, format it as you desire.
- Add a Frame to the Frame (#1); rename it to “Outline”, resize it to {0.15, 0, 1, -75} (-75 because of the Topbar size), and set AnchorPoint to (1,1)
- Add TextButtons inside Outline; renaming them and formatting them as you wish (mine are Gear, Pets, and Perks).
- Add a Frame to the Outline; rename it to “Selected” and resize it to {0, 5, 0, 50}.
- Add a ScrollingFrame to ScreenGui; rename it to “Scrolling”, resize it to {0.85, 0},{1, -75}, set CanvasSize to {0, 0, 0, 2500}, AnchorPoint to (1,1), and ScrollingEnabled to false.
- Add a TextButton to the ScreenGui; rename it to “ScrollDetector”, BackgroundTransparency to 1, same exact properties as Scrolling.
- Add TextLabels inside of “Scrolling”; rename them to the exact names of the buttons in the oulintes (this is essential).
- Add a Bool Value inside of “Scrolling”; rename it to “CollapseExpand”.
I apologize if that sounds confusing, but the above it a must for this to work. I cannot cover everything in that UI, it would take extremely long. But this is the overall structure.
Add Local Scripts
- Add three Local Scripts inside of the main Scrolling Frame, renaming them to “PlatformSetter”, “ScrollingOutline”, and “ScrollingScript”.
- Add a Local Script in “PlatformSetter” and rename it to “Keyboard”.
- Add Local Scripts (one in each of the TextButtons in the Outline), and rename them to “ClickToScroll”. Make sure to right click on it, convert to package, and select “AutoUpdate” in the package link parented to the script.
Scripting
Smooth Scrolling
Open the “Keyboard” script. Let us start by defining variables:
local sFrame = script.Parent.Parent --main scrolling frame
local frame = sFrame.Parent
local ce = sFrame:WaitForChild("CollapseExpand") --bool value
local sDetec = frame:WaitForChild("ScrollDetector") --text button (used to detect mouse wheel actions)
local ts = game:GetService("TweenService")
local ti = TweenInfo.new(0.5, Enum.EasingStyle.Quint, Enum.EasingDirection.Out)
local player = game.Players.LocalPlayer
local ui = player:WaitForChild("PlayerGui")
local sd = ui:WaitForChild("ScrollDistance") --this is a Number Value I added to StarterGui, but this is basically a number representing how far to scroll each time
local y = sFrame.CanvasPosition.Y
Next, create this function that will set the value of “CollapseExpand”. This will prove useful later on:
local function setVal(bool)
ce.Value = bool
end
Then, the main part of the script:
sDetec.MouseWheelBackward:Connect(function()
setVal(false) --basically, to temporarily stop the topbar from collapse WHILE the scrolling tween is executed
if sFrame.CanvasPosition.Y <= (sFrame.CanvasSize.Y.Offset - sd.Value) then --if the scrolling frame is not close to the end
print("not close to end!")
y = y + sd.Value
--setting the tween
local sDown = {CanvasPosition = Vector2.new(0, y)}
local downCreate = ts:Create(sFrame, ti, sDown)
downCreate:Play()
downCreate.Completed:Connect(function() --when completed, the topbar collapses
setVal(true)
end)
elseif sFrame.CanvasPosition.Y > (sFrame.CanvasSize.Y.Offset - sd.Value) then --if the scrolling frame is closer to the end than (the size of the canvas size minus the scroll distance)
print("pretty close to end!")
y = y + (sFrame.CanvasSize.Y.Offset - sFrame.CanvasPosition.Y) --add to y so that the scroll reaches the end
local sDown = {CanvasPosition = Vector2.new(0, y)}
local downCreate = ts:Create(sFrame, ti, sDown)
downCreate:Play()
downCreate.Completed:Connect(function()
setVal(true) --topbar can now collapse
end)
end
end)
sDetec.MouseWheelForward:Connect(function()
setVal(false)
if sFrame.CanvasPosition.Y >= sd.Value then --far from the beginning
print("not close to beg!")
y = y - sd.Value
local sUp = {CanvasPosition = Vector2.new(0, y)} --scroll down
local upCreate = ts:Create(sFrame, ti, sUp)
upCreate:Play()
upCreate.Completed:Connect(function()
setVal(true) --topbar can now expand
end)
elseif sFrame.AbsolutePosition.Y < sd.Value then --pretty close to the beginning
print("pretty close to beg!")
y = y - sFrame.CanvasPosition.Y
local sUp = {CanvasPosition = Vector2.new(0, y)}
local upCreate = ts:Create(sFrame, ti, sUp)
upCreate:Play()
upCreate.Completed:Connect(function()
setVal(true)
end)
end
end)
So this is a simple version of Smooth Scrolling. We have created a custom scrolling script!
Device Detection
Open the “PlatformSetter” script and simply paste this in:
local uis = game:GetService("UserInputService")
local keyboard = script.Keyboard
local isTouch = uis.TouchEnabled
local isKeyboard = uis.KeyboardEnabled
keyboard.Disabled = true
if isKeyboard == true then
keyboard.Disabled = false
end
script.Disabled = true
Expanding / Collapsing Topbar
Open the “ScrollingScript” and define these following variables:
local sFrame = script.Parent
local ce = sFrame:WaitForChild("CollapseExpand")
local frame = sFrame.Parent
local sd = frame:WaitForChild("ScrollDetector")
local tb = frame:WaitForChild("Topbar")
local outline = frame:WaitForChild("Outline")
local player = game.Players.LocalPlayer
local ui = player:WaitForChild("PlayerGui")
local scDis = ui:WaitForChild("ScrollDistance")
local cp = sFrame.CanvasPosition.Y
local dur = 0.25
local style = "Linear"
local dir = "Out"
local isOut = false --if topbar is expanded or not
Below, we detect if the Collapse Expand value changes, that’s our cue to tween the topbar!
ce.Changed:Connect(function()
if ce.Value == true then --this will only run when the smooth scroll is done
local cp2 = sFrame.CanvasPosition.Y --new position of canvas
if cp2 > cp and isOut == false then --if they scrolled down, then expand the topbar
tb:TweenPosition(UDim2.new(tb.Position.X.Scale, 0, tb.Position.Y.Scale, -(tb.Size.Y.Offset)), dir, style, dur, true)
outline:TweenSize(UDim2.new(outline.Size.X.Scale, 0, outline.Size.Y.Scale, 0), dir, style, dur, true)
sFrame:TweenSize(UDim2.new(sFrame.Size.X.Scale, 0, sFrame.Size.Y.Scale, 0), dir, style, dur, true)
sd:TweenSize(UDim2.new(sd.Size.X.Scale, 0, sd.Size.Y.Scale, 0), dir, style, dur, true) --we also need to scale the scroll detector so that it is the same size as the scrolling frame
isOut = true
elseif cp2 < cp and isOut == true then --collapse it
tb:TweenPosition(UDim2.new(0, 0, 0, 0), dir, style, dur, true)
outline:TweenSize(UDim2.new(outline.Size.X.Scale, 0, outline.Size.Y.Scale, -tb.Size.Y.Offset), dir, style, dur, true)
sFrame:TweenSize(UDim2.new(sFrame.Size.X.Scale, 0, sFrame.Size.Y.Scale, -tb.Size.Y.Offset), dir, style, dur, true)
sd:TweenSize(UDim2.new(sd.Size.X.Scale, 0, sd.Size.Y.Scale, -tb.Size.Y.Offset), dir, style, dur, true)
isOut = false
end
cp = cp2
end
end)
Now, the Topbar tweens out when scrolling down and tweens in when scrolling up.
Select Outline Buttons Based on the Canvas Position
This is almost the last part of scripting in this tutorial. This is where we detect if the player has scrolled to a certain area and then we highlight what section they are in in the Outline. Open “ScrollOutline” and define:
local sFrame = script.Parent
local frame = sFrame.Parent
local outline = frame:WaitForChild("Outline")
local sc = outline:WaitForChild("Scrolling")
local selected = sc:WaitForChild("Selected")
local sections = {"Gear", "Pets", "Perks"} --make sure these are in order from top to bottom!
Second, we check if the player has scrolled to a certain region:
sFrame:GetPropertyChangedSignal("CanvasPosition"):Connect(function()
local sOne = sFrame[sections[1]] --get the Text object inside of the scrolling frame
local sTwo = sFrame[sections[2]]
local sThree = sFrame[sections[3]]
if sFrame.CanvasPosition.Y >= sOne.Position.Y.Offset and sFrame.CanvasPosition.Y < sTwo.Position.Y.Offset then --within first section
print("one")
selected.Position = UDim2.new(selected.Position.X.Scale, 0, 0, sc[sOne.Name].Position.Y.Offset)
elseif sFrame.CanvasPosition.Y >= sTwo.Position.Y.Offset and sFrame.CanvasPosition.Y < sThree.Position.Y.Offset then --within second section
print("two")
selected.Position = UDim2.new(selected.Position.X.Scale, 0, 0, sc[sTwo.Name].Position.Y.Offset)
elseif sFrame.CanvasPosition.Y >= sThree.Position.Y.Offset then
print("three")
selected.Position = UDim2.new(selected.Position.X.Scale, 0, 0, sc[sThree.Name].Position.Y.Offset)
end
end)
Click to Scroll
Last part of the tutorial! Thank you for reading this far (or you just skipped here). Anyways, click on the local script that we coverted to package (it is called “ClickToScroll”).
local button = script.Parent --button in the outline
local scrolling = button.Parent --ignore this scrolling frame inside of the Outline
local outline = scrolling.Parent
local frame = outline.Parent
local sFrame = frame:WaitForChild("Scrolling")
local selected = scrolling:WaitForChild("Selected")
local pfSetter = sFrame:WaitForChild("PlatformSetter")
local ts = game:GetService("TweenService")
local ti = TweenInfo.new(0.5, Enum.EasingStyle.Linear, Enum.EasingDirection.Out)
local keyboard = pfSetter:WaitForChild("Keyboard")
local disabled = keyboard.Disabled --if the player is on a computer
Then, simply:
button.MouseButton1Click:Connect(function()
if disabled == false then
keyboard.Disabled = true --just so that while the script scrolls the frame, the player cannot
local asocText = sFrame[button.Name] --the text label associated with the name of the button
local tween = {CanvasPosition = Vector2.new(0, asocText.Position.Y.Offset)}
local create = ts:Create(sFrame, ti, tween)
create:Play()
create.Completed:Connect(function()
keyboard.Disabled = false --enable the script after scrolling for the player
end)
end
end)
Lastly, just duplicate this script as many times as there are buttons in the Outline (since I have three buttons (gear, pets, perks), I would duplicate the scripts 3 times and put one inside of each text button) like this:
And we are done! We have added these features to the Scrolling Frame!
Full Scripts
“Keyboard” (Smooth Scrolling):
local sFrame = script.Parent.Parent
local frame = sFrame.Parent
local ce = sFrame:WaitForChild("CollapseExpand")
local sDetec = frame:WaitForChild("ScrollDetector")
local ts = game:GetService("TweenService")
local ti = TweenInfo.new(0.5, Enum.EasingStyle.Quint, Enum.EasingDirection.Out)
local player = game.Players.LocalPlayer
local ui = player:WaitForChild("PlayerGui")
local sd = ui:WaitForChild("ScrollDistance")
local y = sFrame.CanvasPosition.Y
local function setVal(bool)
ce.Value = bool
end
sDetec.MouseWheelBackward:Connect(function()
setVal(false)
if sFrame.CanvasPosition.Y <= (sFrame.CanvasSize.Y.Offset - sd.Value) then
print("not close to end!")
y = y + sd.Value
local sDown = {CanvasPosition = Vector2.new(0, y)}
local downCreate = ts:Create(sFrame, ti, sDown)
downCreate:Play()
downCreate.Completed:Connect(function()
setVal(true)
end)
elseif sFrame.CanvasPosition.Y > (sFrame.CanvasSize.Y.Offset - sd.Value) then
print("pretty close to end!")
y = y + (sFrame.CanvasSize.Y.Offset - sFrame.CanvasPosition.Y)
local sDown = {CanvasPosition = Vector2.new(0, y)}
local downCreate = ts:Create(sFrame, ti, sDown)
downCreate:Play()
downCreate.Completed:Connect(function()
setVal(true)
end)
end
end)
sDetec.MouseWheelForward:Connect(function()
setVal(false)
if sFrame.CanvasPosition.Y >= sd.Value then
print("not close to beg!")
y = y - sd.Value
local sUp = {CanvasPosition = Vector2.new(0, y)}
local upCreate = ts:Create(sFrame, ti, sUp)
upCreate:Play()
upCreate.Completed:Connect(function()
setVal(true)
end)
elseif sFrame.AbsolutePosition.Y < sd.Value then
print("pretty close to beg!")
y = y - sFrame.CanvasPosition.Y
local sUp = {CanvasPosition = Vector2.new(0, y)}
local upCreate = ts:Create(sFrame, ti, sUp)
upCreate:Play()
upCreate.Completed:Connect(function()
setVal(true)
end)
end
end)
“PlatformSetter” (Device Detection):
local uis = game:GetService("UserInputService")
local keyboard = script.Keyboard
local isTouch = uis.TouchEnabled
local isKeyboard = uis.KeyboardEnabled
keyboard.Disabled = true
if isKeyboard == true then
keyboard.Disabled = false
end
script.Disabled = true
“ScrollingScript” (Topbar Collapsing / Expanding):
local sFrame = script.Parent
local ce = sFrame:WaitForChild("CollapseExpand")
local frame = sFrame.Parent
local sd = frame:WaitForChild("ScrollDetector")
local tb = frame:WaitForChild("Topbar")
local outline = frame:WaitForChild("Outline")
local player = game.Players.LocalPlayer
local ui = player:WaitForChild("PlayerGui")
local scDis = ui:WaitForChild("ScrollDistance")
local cp = sFrame.CanvasPosition.Y
local dur = 0.25
local style = "Linear"
local dir = "Out"
local isOut = false
ce.Changed:Connect(function()
if ce.Value == true then
local cp2 = sFrame.CanvasPosition.Y
if cp2 > cp and isOut == false then
tb:TweenPosition(UDim2.new(tb.Position.X.Scale, 0, tb.Position.Y.Scale, -(tb.Size.Y.Offset)), dir, style, dur, true)
outline:TweenSize(UDim2.new(outline.Size.X.Scale, 0, outline.Size.Y.Scale, 0), dir, style, dur, true)
sFrame:TweenSize(UDim2.new(sFrame.Size.X.Scale, 0, sFrame.Size.Y.Scale, 0), dir, style, dur, true)
sd:TweenSize(UDim2.new(sd.Size.X.Scale, 0, sd.Size.Y.Scale, 0), dir, style, dur, true)
isOut = true
elseif cp2 < cp and isOut == true then
tb:TweenPosition(UDim2.new(0, 0, 0, 0), dir, style, dur, true)
outline:TweenSize(UDim2.new(outline.Size.X.Scale, 0, outline.Size.Y.Scale, -tb.Size.Y.Offset), dir, style, dur, true)
sFrame:TweenSize(UDim2.new(sFrame.Size.X.Scale, 0, sFrame.Size.Y.Scale, -tb.Size.Y.Offset), dir, style, dur, true)
sd:TweenSize(UDim2.new(sd.Size.X.Scale, 0, sd.Size.Y.Scale, -tb.Size.Y.Offset), dir, style, dur, true)
isOut = false
end
cp = cp2
end
end)
“ScrollOutline” (Detect If the Player Has Scrolled to a Certain Region):
local sFrame = script.Parent
local frame = sFrame.Parent
local outline = frame.Outline
local sc = outline.Scrolling
local selected = sc:WaitForChild("Selected")
local sections = {"Gear", "Pets", "Perks"}
sFrame:GetPropertyChangedSignal("CanvasPosition"):Connect(function()
local sOne = sFrame[sections[1]]
local sTwo = sFrame[sections[2]]
local sThree = sFrame[sections[3]]
if sFrame.CanvasPosition.Y >= sOne.Position.Y.Offset and sFrame.CanvasPosition.Y < sTwo.Position.Y.Offset then --within first section
print("one")
selected.Position = UDim2.new(selected.Position.X.Scale, 0, 0, sc[sOne.Name].Position.Y.Offset)
elseif sFrame.CanvasPosition.Y >= sTwo.Position.Y.Offset and sFrame.CanvasPosition.Y < sThree.Position.Y.Offset then --within second section
print("two")
selected.Position = UDim2.new(selected.Position.X.Scale, 0, 0, sc[sTwo.Name].Position.Y.Offset)
elseif sFrame.CanvasPosition.Y >= sThree.Position.Y.Offset then
print("three")
selected.Position = UDim2.new(selected.Position.X.Scale, 0, 0, sc[sThree.Name].Position.Y.Offset)
end
end)
“ClickToScroll” (Click a Button In the Outline to Scroll to the Corresponding Section):
local button = script.Parent
local scrolling = button.Parent
local outline = scrolling.Parent
local frame = outline.Parent
local sFrame = frame:WaitForChild("Scrolling")
local selected = scrolling:WaitForChild("Selected")
local pfSetter = sFrame:WaitForChild("PlatformSetter")
local ts = game:GetService("TweenService")
local ti = TweenInfo.new(0.5, Enum.EasingStyle.Linear, Enum.EasingDirection.Out)
local keyboard = pfSetter:WaitForChild("Keyboard")
local disabled = keyboard.Disabled
button.MouseButton1Click:Connect(function()
if disabled == false then
keyboard.Disabled = true
local asocText = sFrame[button.Name]
local tween = {CanvasPosition = Vector2.new(0, asocText.Position.Y.Offset)}
local create = ts:Create(sFrame, ti, tween)
create:Play()
create.Completed:Connect(function()
keyboard.Disabled = false
end)
end
end)
Place File
UI.rbxl (31.9 KB)
Feedback
How useful is this tutorial to you?
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
0 voters
OK, so I feel like certain parts of this post may seem and are hard to understand (like the Step-By-Step with a lot of numbers).
Is this tutorial confusing to understand at some points? Maybe a bit wordy? Please vote on how clear the information was. 1 being not clear at all and 10 being very clear.
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
0 voters
Additionally, feel free to notify me on how I can make my tutorial(s) better, my code better, and/or how much I should explain each step (if needed). Input on tutorial structure (headings etc.) and readability is appreciated!
Thank you for reading through this very long tutorial and for your feedback.