Moveable + zoomable grid interface

I’m currently making an animation editor and I’m wondering how I could move around my grid. I have it working fine with zooming its just movement that’s I cant figure out, any help is appreciated.

(I used to have it setup to where movement would effect the for loops range however that caused many issues)

Code:

local function GridLine()
	local G = Instance.new("Frame")
	G.BorderSizePixel = 0
	G.Size = UDim2.new(0,1,1,0)
	G.BackgroundColor3 = Color3.new(1,1,1)
	G.ZIndex = 1
	G.Name = "Grid"
	G.AnchorPoint = Vector2.new(.5,.5)
	
	local TextLabel = Instance.new("TextLabel")
	TextLabel.Name = "Stamp"
	TextLabel.Size = UDim2.fromOffset(25,25)
	TextLabel.Parent = G

	return G
end

local function Map(n : number, start : number, stop : number, newStart : number, newStop : number, withinBounds : boolean) : number
	local value = ((n - start) / (stop - start)) * (newStop - newStart) + newStart

	if not withinBounds then
		return value
	end

	if newStart < newStop then
		return math.max(math.min(value, newStop), newStart)
	else
		return math.max(math.min(value, newStart), newStop)
	end
end

local function Lerp(start, goal, alpha)
	return start + (goal - start) * alpha
end

local _PG = 100

local GridInterface = {}
GridInterface.__index = GridInterface

	function GridInterface.new()
		local self = setmetatable({},GridInterface)
		
		self.Zoom = 50
		self.Movement = 0
		
		self.Lines = {}
		self.ActiveLines = {}
		
		return self
	end
	
	function GridInterface:RelativeKeyframe(Keyframe)
		local Spacing = _PG + self.Zoom
		
		local T = Keyframe.Time

		local Max = nil
		local Min = nil
		
		if T >= 0 then
			local I = 0
			
			repeat
				if (Spacing*I)/_PG >= T then
					Max = (Spacing*I)
					Min = (Spacing*(I-1))
				end
				
				I += 1
			until Max
		end
		
		local PT = Map(T,Min/_PG,Max/_PG,0,1,true)

		return Lerp(Min,Max,PT)
	end
	
	function GridInterface:update()
		self.Zoom = (math.sin(tick()*.1)^2)*35
		--self.Movement = math.sin(tick()*.01)*15
		
		local ViewportWidth = workspace.CurrentCamera.ViewportSize.X
		local Spacing = _PG + self.Zoom
		
		local Lines = math.ceil(ViewportWidth/Spacing)
		
		for _,Line in self.Lines do
			Line.Visible = false
		end
		
		for I = 0,Lines,1 do
			local Line
			
			if self.Lines[1] then
				Line = self.Lines[1]
				table.remove(self.Lines,1)
			else
				Line = GridLine()
			end

			Line.Position = UDim2.new(0,Spacing*I,.5,0)
			Line.Parent = script.Parent
			Line.Visible = true
			Line.Stamp.Text = math.floor((Spacing*I/_PG)*100)/100
			
			table.insert(self.ActiveLines,Line)
		end
		
		self.Lines = self.ActiveLines
		self.ActiveLines = {}
		
		return self.Lines
	end

return GridInterface

For anyone that reads this _PG is the pixel gap which is used as the default spacing between each grid line