How to enable flexible scrollframe for PC users?

When I have a ScrollingFrame on a tablet, moving it in any direction is very smooth. If I try pull it outside the canvassize, it will smoothly return to it’s maximum boundaries.

If I use the same scrollingFrame on PC, I cant even move unless I use the scrollwheel.

This can be seen in my repro file:
Repro.rbxl (34.3 KB)

  1. Play as normal
    You can’t move unless you use the mouse scroll-wheel

  2. Switch on Device Simulator (pick any phone) and press Play
    Now the scrollframe moves smoothly.

How can I get the behavior from 2. to work in PC mode (1)?

1 Like

Although I am a little confused, my assumption on why this is happening is because of how PC scroll wheels work. A mouse scroll wheel moves in increments. If you try scrolling right now, unless you have one of those mice that unlock rotation, you will see that it snaps into a position, therefore not being smooth.

My best recommendation is to disable scrolling if you are on PC and implement your own scrolling algorithm, but detecting a scroll and then either incrementing by a small amount or tweening to the desired location.

1 Like

Not sure why you would want it, but here you go (local script in the screen gui):

--!strict

local TweenService = game:GetService("TweenService")
local UserInputService = game:GetService("UserInputService")
local Players = game:GetService("Players")

local TWEEN_INFO = TweenInfo.new(2, Enum.EasingStyle.Quint)

local client = Players.LocalPlayer
local scrollingFrame = script.Parent.ScrollingFrame
local mouse = client:GetMouse()
local mouseMoveConnection: RBXScriptConnection
local lastMouseX: number
local lastMouseY: number

local backTweens: {[GuiObject]: Tween} = {}

local function childAdded(child: Instance)
	if child:IsA("GuiObject") then
		backTweens[child] = TweenService:Create(child, TWEEN_INFO, {Position = child.Position})
	end
end

local function childRemoved(child: Instance)
	if child:IsA("GuiObject") then
		backTweens[child] = nil
	end
end

local function mouseMove()
	local dx = mouse.X - lastMouseX
	local dy = mouse.Y - lastMouseY
	
	for element, _ in pairs(backTweens) do
		local elementX = element.Position.X.Offset
		local elementY = element.Position.Y.Offset
		
		local newPos = UDim2.fromOffset(elementX + dx * 5, elementY + dy * 5)
		
		TweenService:Create(element, TWEEN_INFO, {Position = newPos}):Play()
	end
	
	lastMouseX = mouse.X
	lastMouseY = mouse.Y
end

local function inputBegan(input: InputObject)
	if input.UserInputType == Enum.UserInputType.MouseButton1 then
		lastMouseX = mouse.X
		lastMouseY = mouse.Y

		mouseMoveConnection = mouse.Move:Connect(mouseMove)
	end
end

local function inputEnded(input: InputObject)
	if input.UserInputType == Enum.UserInputType.MouseButton1 then
		mouseMoveConnection:Disconnect()
		
		for element, tweenBack in pairs(backTweens) do
			tweenBack:Play()
		end
	end
end

scrollingFrame.ChildAdded:Connect(childAdded)
scrollingFrame.ChildRemoved:Connect(childRemoved)
UserInputService.InputBegan:Connect(inputBegan)
UserInputService.InputEnded:Connect(inputEnded)

for _, child in ipairs(scrollingFrame:GetChildren()) do
	childAdded(child)
end

It’s relatively simple to make it register touches and pans alongside the clicks and moves, but when testing it in the emulator, it gets overriden by the default behavior, so no need to do that.

1 Like