How to make ScrollingFrame draggable without a scroll bar?

Hello,

for usability sake, I would like my non-interactive scrollingframes to be also draggable “inside” rather than only using scroll bar or mouse wheel. Is it possible to do without writing too much code?

scrollingframe

Thank you for any help!

Have you tried increasing the size of the scroll bar to fit the entire thing, then lowering the transparency?

I actually did, but scroll bar is a different kind of element and making it wider makes the scrollingframe smaller. Can’t go “under”.

1 Like

Ill show you how in 2 simple steps (probably more but whatever)

  • Step 1
    We are gonna do this in a local script because we are using the mouse position, and for perfomance sake
local plr = game:GetService("Players").LocalPlayer
local mouse = plr:GetMouse()
local ScrollingFrame -- here you put your scrolling frame thats inside the surface gui
local SurfaceGuiParent -- Surface gui parent
local MaxRaycastDistance = 1000 --How many studs the raycast is gonna travel
local CanDragGui = true 

Thats all the variables your gonna need.

  • Step 2
    Now each time the player clicks we are gonna send a raycast and check if the instance that the raycast hit is the surface gui parent
ocal plr = game:GetService("Players").LocalPlayer
local chr = plr.Character or plr.CharacterAdded:Wait()
local mouse = plr:GetMouse()
local ScrollingFrame -- here you put your scrolling frame thats inside the surface gui
local SurfaceGuiParent -- Surface gui parent 
local MaxRaycastDistance = 1000 --How many studs the raycast is gonna travel
local CanDragGui = true

local function DragGui()
if chr.PrimaryPart ~= nil  then
CanDragGui = true
local RayCast = workspace:Raycast(chr.PrimaryPart.Position, mouse.Hit.Position.Unit*MaxRaycastDistance)
if RayCast ~= nil and RayCast.Instance == SurfaceGuiParent then
task.spawn(function()
repeat
task.wait()
ScrollingFrame.Position = 
ScrollingFrame.Position + UDim2.new(0,0,mouse.Y,0) --i think all the codes works but here i have no idea.. maybe you could check
--if the mouse.Y is greater or less than 0.5 and add add/substract an offset
until CanDragGui == false
end)
end
end
end

mouse.Button1Down:Connect(DragGui)
mouse.Button1Up:Connect(function()
CanDragGui = false
end)

Uhh yeah didnt explain but let me know your questions and see if it works gonna do some changes bc i messed up

1 Like

Unfortunately, I am unaware of any other workarounds. I guess you need to “script” a fix for your quality of life feature or copy a game that successfully pulled it off.

1 Like

Can you use the events that detect mouse enter, and mouse movement, etc… When its a surface gui object?
Might save you on raycasting.

As far as i know those dont work on surface guis and also Mouse.Enter and Leave are really buggy

I tried it out, and the events do fire, on a frame in a surface gui.
I’ll play around with it and see if I can get something to scroll

1 Like

SurfaceGui Scrolling.rbxl (55.4 KB)

image

image
image

local inFrame = false
local isDown = false
local downPos = nil

script.Parent.MouseEnter:Connect(function()
	inFrame = true
	print("inFrame = true")
end)

script.Parent.MouseLeave:Connect(function()
	inFrame = false
	print("inFrame = false")
end)

script.Parent.InputBegan:Connect(function(input, gameProcessed)
	if input.UserInputType == Enum.UserInputType.MouseButton1 then
		isDown = true
		print("isDown = true")
	elseif input.UserInputType == Enum.UserInputType.Touch then
		print("A touchscreen input has started at", input.Position)
	elseif input.UserInputType == Enum.UserInputType.Gamepad1 then
		print("A button is being pressed on a gamepad! Button:", input.KeyCode)
	end

end)

function DoMovement(input)
	if inFrame and isDown then
		if downPos then
		script.Parent.CanvasPosition = Vector2.new(0,script.Parent.CanvasPosition.Y + (downPos.Y - input.Position.Y))
		end
		downPos = input.Position	
	else
		downPos = nil
	end
end

script.Parent.InputChanged:Connect(function(input,gameProcessed)
	if input.UserInputType == Enum.UserInputType.MouseMovement then
		DoMovement(input)
	elseif input.UserInputType == Enum.UserInputType.Gamepad1 then
		if input.KeyCode == Enum.KeyCode.Thumbstick1 then
			print("The left thumbstick has been moved!")
			DoMovement(input)
		end
	elseif input.UserInputType == Enum.UserInputType.Touch then
		print("The user's finger is moving on the screen!")
		DoMovement(input)
	end
end)


script.Parent.InputEnded:Connect(function(input, gameProcessed)
	if input.UserInputType == Enum.UserInputType.MouseButton1 then
		isDown = false
		print("isDown = false")
	elseif input.UserInputType == Enum.UserInputType.Touch then
		print("A touchscreen input has been released at", input.Position)
	elseif input.UserInputType == Enum.UserInputType.Gamepad1 then
		print("A button has been released on a gamepad! Button:", input.KeyCode)
	end
end)

local label= script.Parent:WaitForChild("TextLabel")
label.Parent = nil

for n = 1,10 do
	local l = label:Clone()
	l.Text = "Line "..n
	l.Parent = script.Parent
end

script.Parent.CanvasSize = UDim2.new(1,0,0,100*10)

1 Like

Thank you so much, this works like charm!

scrollingframe2

For some weird reason it worked out of the box in your example, but didn’t in my own project. Couldn’t figure out why, so moved the code to a module and used it where my other interface-related code is. Sweet!

P.S. in case anyone else has trouble setting it up with “child” scripts, here’s module I’m using. Don’t mind the underscores, it’s a condition I have.

local ScrollInput = {}

local _inFrame = false 
local _downPos = nil 
local _isDown = false 

function ScrollInput.Setup(_frame)

	_frame.MouseEnter:Connect(function()
		_inFrame = true
	end)

	_frame.MouseLeave:Connect(function()
		_inFrame = false
	end)
	
	_frame.InputBegan:Connect(function(input, gameProcessed)
		if input.UserInputType == Enum.UserInputType.MouseButton1 then
			_isDown = true
			print("isDown = true")
		elseif input.UserInputType == Enum.UserInputType.Touch then
			print("A touchscreen input has started at", input.Position)
		elseif input.UserInputType == Enum.UserInputType.Gamepad1 then
			print("A button is being pressed on a gamepad! Button:", input.KeyCode)
		end

	end)



	_frame.InputChanged:Connect(function(input,gameProcessed)
		if input.UserInputType == Enum.UserInputType.MouseMovement then
			ScrollInput.Move(_frame, input)
		elseif input.UserInputType == Enum.UserInputType.Gamepad1 then
			if input.KeyCode == Enum.KeyCode.Thumbstick1 then
				print("The left thumbstick has been moved!")
				ScrollInput.Move(_frame, input)
			end
		elseif input.UserInputType == Enum.UserInputType.Touch then
			print("The user's finger is moving on the screen!")
			ScrollInput.Move(_frame, input)
		end
	end)


	_frame.InputEnded:Connect(function(input, gameProcessed)
		if input.UserInputType == Enum.UserInputType.MouseButton1 then
			_isDown = false
			print("isDown = false")
		elseif input.UserInputType == Enum.UserInputType.Touch then
			print("A touchscreen input has been released at", input.Position)
		elseif input.UserInputType == Enum.UserInputType.Gamepad1 then
			print("A button has been released on a gamepad! Button:", input.KeyCode)
		end
	end)
end

function ScrollInput.Move(_frame, _input)
	if _inFrame and _isDown then
		if _downPos then
			_frame.CanvasPosition = Vector2.new(0, _frame.CanvasPosition.Y + (_downPos.Y - _input.Position.Y))
		end
		_downPos = _input.Position	
	else
		_downPos = nil
	end
end

return ScrollInput

1 Like

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