Skip to content

Instantly share code, notes, and snippets.

@eldonwilliams
Created June 10, 2022 19:46
Show Gist options
  • Save eldonwilliams/a1411d0e8ca685b28962e70c0f681bb8 to your computer and use it in GitHub Desktop.
Save eldonwilliams/a1411d0e8ca685b28962e70c0f681bb8 to your computer and use it in GitHub Desktop.
local Spr = require(game:GetService("ReplicatedStorage"):WaitForChild("Packages"):WaitForChild("Spr"))
export type TargetInstance = Frame
export type InitialPositionFunction = (target: TargetInstance) -> Vector2
export type NewSpringListElementParams = {
RenderInstance: GuiObject,
SpringDamping: number,
SpringFrequency: number,
CalculateInitalPosition: InitialPositionFunction,
TargetParent: Instance,
}
local function vector2ToUdim2(v2: Vector2): UDim2
return UDim2.fromOffset(v2.X, v2.Y)
end
local function relativeToAbsolutePos(object: GuiObject, abs: Vector2): UDim2
return vector2ToUdim2(abs - (object.Parent:IsA("GuiObject") and object.Parent.AbsolutePosition or Vector2.new()))
end
-- A SpringListElement is a object that is in a UIListSpring, it handles the invisible pointer Instance and the actual moving Instance
local SpringListElement = {}
SpringListElement.__index = SpringListElement
function SpringListElement.new(params: NewSpringListElementParams)
local instance = setmetatable({
_RenderInstance = params.RenderInstance,
_TargetParent = params.TargetParent,
_TargetInstance = nil,
_Damping = params.SpringDamping,
_Freq = params.SpringFrequency,
_Connections = {} :: Array<RBXScriptConnection>,
_Active = true,
_CalcInitialPosition = params.CalculateInitalPosition,
}, SpringListElement)
instance:SetupForTracking()
return instance
end
function SpringListElement:SetupForTracking()
self._TargetInstance = Instance.new("Frame")
self._TargetInstance.Parent = self._TargetParent or self._RenderInstance.Parent
self._TargetInstance.Name = "TARGET"
self._TargetInstance.Transparency = 1
self._TargetInstance.AnchorPoint = Vector2.new(0.5,0.5)
self._TargetInstance.LayoutOrder = self._RenderInstance.LayoutOrder
self._RenderInstance.Visible = true
self._RenderInstance.Position = relativeToAbsolutePos(self._RenderInstance, self._CalcInitialPosition(self._TargetInstance))
local function updatePosition()
Spr.target(self._RenderInstance, self._Damping, self._Freq, {
Position = relativeToAbsolutePos(self._RenderInstance, self._TargetInstance.AbsolutePosition),
});
end
local function updateTargetSize()
self._TargetInstance.Size = vector2ToUdim2(self._RenderInstance.AbsoluteSize)
end
table.insert(self._Connections, self._TargetInstance:GetPropertyChangedSignal("AbsolutePosition"):Connect(updatePosition))
table.insert(self._Connections, self._TargetInstance:GetPropertyChangedSignal("AbsoluteSize"):Connect(updatePosition))
table.insert(self._Connections, self._RenderInstance:GetPropertyChangedSignal("AbsoluteSize"):Connect(updateTargetSize))
updateTargetSize()
updatePosition()
end
function SpringListElement:SetActive(active: boolean)
if (active == self._Active) then
return
end
self._Active = active
if (active == false) then
Spr.stop(self._RenderInstance)
self._TargetInstance:Destroy()
for _, connection in pairs(self._Connections) do
connection:Disconnect()
end
else
self:SetupForTracking()
end
end
function SpringListElement:Destroy()
if (self._TargetInstance.Parent == nil) then
return
end
self:SetActive(false)
end
return SpringListElement
local SpringListElement = require(script:FindFirstChild("SpringListElement"))
export type NewUIListSpringParams = {
InititalChildren: Array<GuiObject>,
Damping: number,
Frequency: number,
Parent: GuiObject,
DefaultInitalPosition: SpringListElement.InitialPositionFunction,
}
--[[
A UIListSpring extends the UIList object but moves the attached objects using springs
]]
local UIListSpring = {}
UIListSpring.__index = UIListSpring
function UIListSpring.new(params: NewUIListSpringParams)
local instance = setmetatable({
_Children = {} :: Array<SpringListElement.SpringListElement>,
_Damp = params.Damping,
_Freq = params.Frequency,
_Parent = params.Parent,
_InitialPosition = params.DefaultInitalPosition,
}, UIListSpring)
instance:Setup(params)
return instance
end
function UIListSpring:AddChild(object: GuiObject, initalPosition: SpringListElement.InitialPositionFunction)
local new = SpringListElement.new({
CalculateInitalPosition = initalPosition or self._InitialPosition,
RenderInstance = object,
SpringDamping = self._Damp,
SpringFrequency = self._Freq,
TargetParent = self._Parent,
})
table.insert(self._Children, new)
return new
end
function UIListSpring:RemoveChild(object)
local found = table.find(self._Children, object)
if (not found) then
return false
end
self._Children[found]:Destroy()
table.remove(self._Children, found)
return true
end
function UIListSpring:Setup(params: NewUIListSpringParams)
for _, child in pairs(params.InititalChildren) do
self:AddChild(child)
end
end
export type UIListSpringInstance = typeof(UIListSpring.new())
export type SpringListElementInstance = typeof(SpringListElement.new())
return UIListSpring
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment