One smoother option is that you could manually calculate the color of each keypoint every frame, which is complex but probably not a bad solution. Here’s a working implementation (sorry formatting is a little stuffed up mb):
local colors = {
BrickColor.new("Bright red").Color,
BrickColor.new("Bright orange").Color,
BrickColor.new("Bright yellow").Color,
BrickColor.new("Bright green").Color,
BrickColor.new("Bright blue").Color,
BrickColor.new("Royal purple").Color
}
local num_colors = #colors
local color_length = 1 / num_colors
local period = 3 -- how long it takes for full animation
game:GetService("RunService").RenderStepped:Connect(function()
local progress = (tick() % period) / period -- ranges from 0 to 1 based on progress
local newColors = {}
local wrapColor = false -- the color at the edges
for i = 1, num_colors + 1 do -- add 1 for extra edge
local color = colors[i] or colors[i-num_colors] -- accounting for +1
local position = progress + (i-1)/num_colors -- the current color's position
if position > 1 then position = position - 1 end -- wrap positions
if position == 0 or position == 1 then wrapColor = true end -- the color is at edge
table.insert(newColors, ColorSequenceKeypoint.new(position, color))
end
if not wrapColor then -- if there were no clean matches on the edge
local indexProgress = ((1 - progress) / color_length) + 1
local col1 = colors[math.floor(indexProgress)]
local col2 = colors[math.ceil(indexProgress)] or colors[1]
local finalCol = col1:Lerp(col2, indexProgress % 1) -- find the color between these two cols
table.insert(newColors, ColorSequenceKeypoint.new(0, finalCol))
table.insert(newColors, ColorSequenceKeypoint.new(1, finalCol))
end
table.sort(newColors, function(a, b)
return a.Time < b.Time
end)
script.Parent.Frame.UIGradient.Color = ColorSequence.new(newColors)
end)
Alternatively, if you’re tweening a frame background and not text itself, you could treat this like the infinitely scrolling background image problem (example How to make moving background scroll infinitely?). The solution here is to tween both of the gradient frames in one direction, then reset their positions and repeat.
local frame1, frame2
local tweenInfo = TweenInfo.new(1, Enum.EasingStyle.Linear)
local tween1 = game:GetService('TweenService'):Create(frame1, tweenInfo, {Position = UDim2.new(1, 0, 0, 0})
local tween2 = game:GetService('TweenService'):Create(frame2, tweenInfo, {Position = UDim2.new(0, 0, 0, 0})
tween1.Completed:Connect(function()
tween2:Cancel() -- make sure the other tween isn't running
frame1.Position = UDim2.new(0, 0, 0, 0)
frame2.Position = UDim2.new(-1, 0, 0, 0)
tween1:Play()
tween2:Play()
end)
frame1.Position = UDim2.new(0, 0, 0, 0)
frame2.Position = UDim2.new(-1, 0, 0, 0)
tween1:Play()
tween2:Play()
In this example, frame1
is the default frame you see at the start of the animation, and frame2
is to the left of it, hidden by the ClipDescendants property. Both tween to the right, and then when the animation is done it resets their positions to the default positions and tweens them to the right again.