Help with scrollingframe

Hello. I am trying to make a system in a scrolling frame that allows you to pan across the canvas when click is held down, similar to the Minecraft advancement screen.

My thought process is to convert the players original and final mouse position at the beginning and end of inputs to a normalized CanvasPosition/AbsolutePosition vector, and then add/subtract that value from their current canvas position.

Here is my code:

local scrollFrame = script.Parent
local UIS = game:GetService("UserInputService")

local dragging = false
local dragStart = nil
local startCanvas = nil
local mouse = game.Players.LocalPlayer:GetMouse()

local originalMousePosX
local originalMousePosY

-- absolute canvas size -> (screengui.absoluteSizeX * canvasScaleX), (screengui.absoluteSizeY * canvasScaleY)

local function normalize(mouseX, mouseY)
	local frameTopLeft = script.Parent.AbsolutePosition
	local canvasPosition = script.Parent.CanvasPosition
	local canvasSize = script.Parent.AbsoluteCanvasSize
	
	-- Convert mouse to position relative to the canvas
	local relativeX = (mouseX - frameTopLeft.X + canvasPosition.X) / canvasSize.X
	local relativeY = (mouseY - frameTopLeft.Y + canvasPosition.Y) / canvasSize.Y

	return relativeX, relativeY
end


-- start dragging
scrollFrame.InputBegan:Connect(function(input)
	if input.UserInputType == Enum.UserInputType.MouseButton1 then
		dragging = true
		
		originalMousePosX, originalMousePosY = normalize(mouse.X, mouse.Y)
	end
end)

-- stop dragging
scrollFrame.InputEnded:Connect(function(input)
	if input.UserInputType == Enum.UserInputType.MouseButton1 then
		dragging = false
		
		local newMouseX, newMouseY = normalize(mouse.X, mouse.Y)
		
		local deltaX = newMouseX - originalMousePosX
		local deltaY = newMouseY - originalMousePosY
		
		print(deltaX, deltaY)
		
	end
end)

-- drag
scrollFrame.InputChanged:Connect(function(input)
	if dragging and input.UserInputType == Enum.UserInputType.MouseMovement then
		script.Parent.Frame.Position = UDim2.fromScale(normalize(mouse.X, mouse.Y))
	end
end)

I’ve successfully been able to drag a frame across the canvas when the player scrolls as a kind of proof of concept, but don’t know where to go from here.

I fixed the issue, heres the full script if anyone wants it

local scrollFrame = script.Parent
local dragging = false
local dragStartMousePos = Vector2.new()
local startCanvasPos = Vector2.new()

-- Top left (Min)
local MinCanvasPosition = UDim2.fromScale(0, 0)

-- Bottom Right (Max)
local MaxCanvasPositionX = scrollFrame.AbsoluteCanvasSize.X - scrollFrame.AbsoluteWindowSize.X
local MaxCanvasPositionY = scrollFrame.AbsoluteCanvasSize.Y - scrollFrame.AbsoluteWindowSize.Y

-- absolute canvas size -> (screengui.absoluteSizeX * canvasScaleX), (screengui.absoluteSizeY * canvasScaleY)

-- goal: go from absolute position in the frame to canvas position


-- Converts the players mouse position into a valid relative gui position
local function normalize(mouseX, mouseY)
	local frameTopLeft = script.Parent.AbsolutePosition
	local canvasPosition = script.Parent.CanvasPosition
	local canvasSize = script.Parent.AbsoluteCanvasSize

	-- Convert mouse to position relative to the canvas
	local relativeX = (mouseX - frameTopLeft.X + canvasPosition.X) / canvasSize.X
	local relativeY = (mouseY - frameTopLeft.Y + canvasPosition.Y) / canvasSize.Y

	return relativeX, relativeY
end

-- Converts relative gui position to a position relative to the abs size of the canvas
local function convertScaleToAbsolute(scalePosX, scalePosY)
	local canvasSize = scrollFrame.AbsoluteCanvasSize
	local absX = scalePosX * canvasSize.X
	local absY = scalePosY * canvasSize.Y
	return absX, absY
end

-- convert mouse position relative 2 gui size to actual canvas position values
local function absoluteToCanvasPosition(absX, absY)
	local canvasSize = scrollFrame.AbsoluteCanvasSize
	local windowSize = scrollFrame.AbsoluteSize

	-- Clamp the scroll position so it stays inside valid scroll bounds
	local maxScrollX = math.max(canvasSize.X - windowSize.X, 0)
	local maxScrollY = math.max(canvasSize.Y - windowSize.Y, 0)

	-- Clamp absX and absY to [0, maxScroll]
	local scrollX = math.clamp(absX, 0, maxScrollX)
	local scrollY = math.clamp(absY, 0, maxScrollY)

	return Vector2.new(scrollX, scrollY)
end


-- start dragging
scrollFrame.InputBegan:Connect(function(input)
	if input.UserInputType == Enum.UserInputType.MouseButton1 then
		dragging = true
		-- Save mouse position on drag start
		dragStartMousePos = Vector2.new(input.Position.X, input.Position.Y)
		-- Save current scroll position on drag start
		startCanvasPos = scrollFrame.CanvasPosition
	end
end)

-- stop dragging
scrollFrame.InputEnded:Connect(function(input)
	if input.UserInputType == Enum.UserInputType.MouseButton1 then
		dragging = false
	end
end)

-- drag
scrollFrame.InputChanged:Connect(function(input)
	if dragging and input.UserInputType == Enum.UserInputType.MouseMovement then
		local currentMousePos = Vector2.new(input.Position.X, input.Position.Y)
		-- Calculate delta movement from drag start
		local delta = currentMousePos - dragStartMousePos

		-- Subtract delta to scroll opposite direction of mouse drag
		local newCanvasPos = startCanvasPos - delta

		-- Clamp new scroll position to valid canvas bounds
		local canvasSize = scrollFrame.AbsoluteCanvasSize
		local windowSize = scrollFrame.AbsoluteSize
		local maxScrollX = math.max(canvasSize.X - windowSize.X, 0)
		local maxScrollY = math.max(canvasSize.Y - windowSize.Y, 0)
		newCanvasPos = Vector2.new(
			math.clamp(newCanvasPos.X, 0, maxScrollX),
			math.clamp(newCanvasPos.Y, 0, maxScrollY)
		)

		scrollFrame.CanvasPosition = newCanvasPos
	end
end)

How can you implement it:

  • Get mouse delta
  • Move canvas position according to delta (you could multiply it for more sensibility).

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