Skip to content

Instantly share code, notes, and snippets.

@howmanysmall
Last active November 2, 2022 18:42
Show Gist options
  • Select an option

  • Save howmanysmall/70a99ed4baa4d4d6db32dea2e8944407 to your computer and use it in GitHub Desktop.

Select an option

Save howmanysmall/70a99ed4baa4d4d6db32dea2e8944407 to your computer and use it in GitHub Desktop.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local HttpService = game:GetService("HttpService")
local RunService = game:GetService("RunService")
local Janitor = require(ReplicatedStorage.Knit.Util.Janitor)
local Roact = require(ReplicatedStorage.Shared.Vendor.Roact)
local RoactHooked = require(ReplicatedStorage.Shared.Vendor.RoactHooked)
local UDim2 = require(ReplicatedStorage.Shared.Utility.DataTypes.UDim2)
local FUZZ = 0.2
local FORCE = 30
local FORCE_FUZZ = 20
local DRAG = 0.95
local GRAVITY = 0.2
local ROTATE_SPEED = 1
export type IConfettiProps = {
FireParticles: RBXScriptSignal,
}
local RandomLib = Random.new(os.clock() % 1 * 1E7)
local NextInteger = RandomLib.NextInteger
local NextNumber = RandomLib.NextNumber
local function Confetti(props: IConfettiProps)
local absoluteSize, setAbsoluteSize = RoactHooked.UseBinding(Vector2.zero)
local state, setState = RoactHooked.UseSetState({})
local forceUpdate = RoactHooked.UseForceUpdate()
local ref = RoactHooked.UseRef()
local onChange = RoactHooked.UseCallback(function(rbx: Frame)
setAbsoluteSize(rbx.AbsoluteSize)
end, {})
local fireConfetti = RoactHooked.UseCallback(function(amount: number, origin: Vector2, direction: Vector2)
local newParticleSet = table.clone(state)
for _ = 1, amount do
local velocity = direction.Unit
velocity += Vector2.new(NextNumber(RandomLib, -FUZZ, FUZZ), NextNumber(RandomLib, -FUZZ, FUZZ))
velocity = velocity.Unit*(FORCE + NextNumber(RandomLib, -FORCE_FUZZ, FORCE_FUZZ))
local confettiLength = NextNumber(RandomLib, 10, 20)
local confettiWidth = confettiLength*NextNumber(RandomLib, 0.4, 0.6)
newParticleSet[{
BackgroundColor3 = Color3.fromHSV(NextNumber(RandomLib), 0.5, 1),
Guid = HttpService:GenerateGUID(false),
Size = UDim2.fromOffset(confettiLength, confettiWidth),
X = origin.X,
Y = origin.Y,
Rotation = NextInteger(RandomLib, 0, 360),
Lifetime = 5 + NextNumber(RandomLib, 0, 2),
VelocityX = velocity.X,
VelocityY = velocity.Y,
}] = true
end
setState(newParticleSet)
end, {state})
local onFire = RoactHooked.UseCallback(function(amount: number)
local localAbsoluteSize = absoluteSize:getValue()
fireConfetti(amount, localAbsoluteSize*Vector2.yAxis, Vector2.new(1, -1))
fireConfetti(amount, localAbsoluteSize, Vector2.new(-1, -1))
end, {fireConfetti})
local onHeartbeat = RoactHooked.UseCallback(function(deltaTime: number)
local newParticleSet = table.clone(state)
for particle in newParticleSet do
local lifetime = particle.Lifetime
local newLifetime = lifetime - deltaTime
if newLifetime <= 0 then
newParticleSet[particle] = Roact.None
continue
else
particle.Lifetime = newLifetime
end
local velocityX = particle.VelocityX
local velocityY = particle.VelocityY
particle.X += velocityX
particle.Y += velocityY
particle.VelocityX = velocityX*DRAG
particle.VelocityY = velocityY*DRAG + GRAVITY
particle.Rotation += ROTATE_SPEED
end
setState(newParticleSet)
forceUpdate()
end, {state})
RoactHooked.UseEffect(function()
local janitor = Janitor.new()
local fireParticles = props.FireParticles
if fireParticles then
janitor:Add(fireParticles:Connect(onFire), "Disconnect")
end
janitor:Add(RunService.Heartbeat:Connect(onHeartbeat), "Disconnect")
return function()
janitor:Destroy()
end
end, {})
RoactHooked.UseEffect(function()
local rbx = ref:getValue()
if rbx then
setAbsoluteSize(rbx.AbsoluteSize)
end
end, {})
local children = {}
for particle in state do
children[particle.Guid] = Roact.createElement("Frame", {
BackgroundColor3 = particle.BackgroundColor3,
BackgroundTransparency = 1 - math.clamp(particle.Lifetime, 0, 1),
BorderSizePixel = 0,
Position = UDim2.fromOffset(particle.X, particle.Y),
Rotation = particle.Rotation,
Size = particle.Size,
})
end
return Roact.createElement("Frame", {
BackgroundTransparency = 1,
ClipsDescendants = true,
Size = UDim2.oneScale,
ZIndex = -100,
[Roact.Change.AbsoluteSize] = onChange,
[Roact.Ref] = ref,
}, children)
end
return RoactHooked.HookPure(Confetti, {Name = "Confetti"})
-- Compiled with roblox-ts v1.3.3
local TS = require(game:GetService("ReplicatedStorage"):WaitForChild("rbxts_include"):WaitForChild("RuntimeLib"))
local HttpService = game:GetService("HttpService")
local RunService = game:GetService("RunService")
local Hooks = TS.import(script, TS.getModule(script, "@rbxts", "roact-hooks").src)
local _roact = TS.import(script, TS.getModule(script, "@rbxts", "roact").src)
local Roact = _roact
local None = _roact.None
local Janitor = TS.import(script, TS.getModule(script, "@rbxts", "janitor").src).Janitor
local useForceUpdate = TS.import(script, script.Parent.Parent.Parent, "roact-hooks", "useForceUpdate").default
local useSetState = TS.import(script, script.Parent.Parent.Parent, "roact-hooks", "useSetState").default
local assign = TS.import(script, script.Parent.Parent.Parent, "roact-hooks", "utility", "roact-assign")
local oneScale = TS.import(script, game:GetService("ReplicatedStorage"), "TS", "modules", "utility", "udim2").oneScale
local fromEqual = TS.import(script, game:GetService("ReplicatedStorage"), "TS", "modules", "utility", "vector2").fromEqual
local FUZZ = 0.2
local FORCE = 30
local FORCE_FUZZ = 20
local DRAG = 0.95
local GRAVITY = 0.2
local ROTATE_SPEED = 1
local randomLib = Random.new((os.clock() % 1) * 1e7)
local Component = function(props, hooks)
local absoluteSize, setAbsoluteSize = hooks.useBinding(Vector2.zero)
local state, setState = useSetState(hooks, {})
local forceUpdate = useForceUpdate(hooks)
local ref = hooks.useValue(Roact.createRef()).value
local onChange = hooks.useCallback(function(rbx)
return setAbsoluteSize(rbx.AbsoluteSize)
end, {})
local fireConfetti = hooks.useCallback(function(amount, origin, direction)
local newParticleSet = table.clone(state)
do
local index = 0
local _shouldIncrement = false
while true do
if _shouldIncrement then
index += 1
else
_shouldIncrement = true
end
if not (index < amount) then
break
end
local velocity = direction.Unit
local _velocity = velocity
local _vector2 = Vector2.new(randomLib:NextNumber(-FUZZ, FUZZ), randomLib:NextNumber(-FUZZ, FUZZ))
velocity = _velocity + _vector2
local _unit = velocity.Unit
local _arg0 = FORCE + randomLib:NextNumber(-FORCE_FUZZ, FORCE_FUZZ)
velocity = _unit * _arg0
local confettiLength = randomLib:NextNumber(10, 20)
local confettiWidth = confettiLength * randomLib:NextNumber(0.4, 0.6)
local _arg0_1 = {
BackgroundColor3 = Color3.fromHSV(randomLib:NextNumber(), 0.5, 1),
Size = UDim2.fromOffset(confettiLength, confettiWidth),
Guid = HttpService:GenerateGUID(false),
X = origin.X,
Y = origin.Y,
Rotation = randomLib:NextInteger(0, 360),
Lifetime = 5 + randomLib:NextNumber(0, 2),
VelocityX = velocity.X,
VelocityY = velocity.Y,
}
newParticleSet[_arg0_1] = true
end
end
setState(newParticleSet)
end, { state })
local onFire = hooks.useCallback(function(amount)
local localAbsoluteSize = absoluteSize:getValue()
local _yAxis = Vector2.yAxis
fireConfetti(amount, localAbsoluteSize * _yAxis, Vector2.new(1, -1))
fireConfetti(amount, localAbsoluteSize, fromEqual(-1))
end, { fireConfetti })
local onHeartbeat = hooks.useCallback(function(deltaTime)
local newParticleSet = table.clone(state)
local deleteParticles = {}
for particle in pairs(newParticleSet) do
local lifetime = particle.Lifetime
local newLifetime = lifetime - deltaTime
if newLifetime <= 0 then
newParticleSet[particle] = nil
deleteParticles[particle] = None
continue
else
particle.Lifetime = newLifetime
end
local velocityX = particle.VelocityX
local velocityY = particle.VelocityY
particle.X += velocityX
particle.Y += velocityY
particle.VelocityX = velocityX * DRAG
particle.VelocityY = velocityY * DRAG + GRAVITY
particle.Rotation += ROTATE_SPEED
end
setState(function(particleSet)
return assign(assign(particleSet, newParticleSet), deleteParticles)
end)
forceUpdate()
end, { state })
hooks.useEffect(function()
local janitor = Janitor.new()
local fireParticles = props.FireParticles
if fireParticles then
janitor:Add(fireParticles:Connect(onFire), "Disconnect")
end
janitor:Add(RunService.Heartbeat:Connect(onHeartbeat), "Disconnect")
return function()
return janitor:Destroy()
end
end, {})
hooks.useEffect(function()
local frame = ref:getValue()
if frame then
onChange(frame)
end
end, {})
local children = {}
for particle in pairs(state) do
local _arg0 = Roact.createFragment({
[particle.Guid] = Roact.createElement("Frame", {
BackgroundColor3 = particle.BackgroundColor3,
BackgroundTransparency = 1 - math.clamp(particle.Lifetime, 0, 1),
BorderSizePixel = 0,
Position = UDim2.fromOffset(particle.X, particle.Y),
Rotation = particle.Rotation,
Size = particle.Size,
}),
})
table.insert(children, _arg0)
end
local _attributes = {
BackgroundTransparency = 1,
[Roact.Change.AbsoluteSize] = onChange,
ClipsDescendants = true,
[Roact.Ref] = ref,
Size = oneScale,
ZIndex = -100,
}
local _children = {}
local _length = #_children
for _k, _v in ipairs(children) do
_children[_length + _k] = _v
end
return Roact.createElement("Frame", _attributes, _children)
end
local Confetti = Hooks.new(Roact)(Component, {
componentType = "PureComponent",
name = "Confetti",
})
local default = Confetti
return {
Confetti = Confetti,
default = default,
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment