How do I diagonally stretch an ImageLabel while keeping its texture in place?

I have a rotated UI ImageLabel with a finish-line pattern I want to stretch diagonally from the bottom left corner to the top right corner of the screen, like so:


At first I simply tried moving it across the screen, but this creates a disorienting effect because of the moving black-white pattern. So instead I wanted to try changing the scale of the ImageLabel. For non-rotated UI elements this would work well because you can set the AnchorPoint.X to 0 and increasing the Size.Scale.X would make the element expend towards the right, but when you are working with rotations, there’s an issue; The rotation of an object is applied at its center rather than its AnchorPoint, so if you were to scale the ImageLabel, it will not stay in place due to this rotation behavior.

Does anyone have any suggestions on how I can achieve my desired effect?

There are various ways you can approach this, but essentially you want to solve for a Position that will keep the label anchored at the bottom left as you resize it. This is not only because the visual effect you want starts from the left, but also because textures extend/tile horizontally to the right.

My best effort at solving this so far has an issue with pixel rounding which causes some minor jittering - I’m not sure how solvable this is (see below).

Outline of my approach:

  1. Setting up the label:
    • Set the AnchorPoint to (0.5, 0.5)
    • Set the Rotation as appropriate
  2. Updating the label:
    • Set the Size as appropriate for the current time
    • Position the label at the center of the screen and then adjust this according to the current time so that the center-left pixel of the label is anchored at the bottom-left corner

Here is the effect:


Here is a quick implementation of the approach described above (run this as a server script to demo it). The key logic is in the update function.

local SCREEN_SIZE = Vector2.new(500, 300)
local LABEL_HEIGHT = 64

local starterGui = game:GetService("StarterGui")
local screenGui = Instance.new("ScreenGui")

local frame = Instance.new("Frame")
frame.AnchorPoint = Vector2.new(0.5, 0.5)
frame.Position = UDim2.fromScale(0.5, 0.5)
frame.Size = UDim2.fromOffset(SCREEN_SIZE.x, SCREEN_SIZE.y)
frame.BackgroundColor3 = Color3.fromRGB(255, 255, 255)

local label = Instance.new("ImageLabel")
label.AnchorPoint = Vector2.new(0.5, 0.5)
label.BackgroundTransparency = 0.5
label.Image = "rbxassetid://5087791203"
label.ImageTransparency = 0.5
label.ScaleType = Enum.ScaleType.Tile
label.TileSize = UDim2.fromOffset(16, 16)

label.Parent = frame
frame.Parent = screenGui
screenGui.Parent = starterGui


local diagonal = SCREEN_SIZE.magnitude
local angle = math.atan2(-SCREEN_SIZE.y, SCREEN_SIZE.x)

label.Rotation = math.deg(angle)

local function update(t)
	label.Size = UDim2.fromOffset(diagonal * t, LABEL_HEIGHT)
	
	local offset = (t - 1) * SCREEN_SIZE * 0.5
	label.Position = UDim2.new(0.5, offset.x, 0.5, -offset.y)
end

local steps = 100
for i = 0, steps do
	local t = i / steps
	update(t)
	wait()
end

1 Like

An alternative way to get around the AnchorPoint rotation issue is to create an invisible, zero-size frame at the point around which you want your label to rotate, then parent your label to that.

Set the invisible frame’s rotation to your desired rotation, then simply resize and position the label as normal.

This is a more general solution for rotating around a specific point - note that it has the same jittering issues while resizing.

local SCREEN_SIZE = Vector2.new(500, 300)
local LABEL_HEIGHT = 64

local starterGui = game:GetService("StarterGui")
local screenGui = Instance.new("ScreenGui")

local frame = Instance.new("Frame")
...

local invisible = Instance.new("Frame")
invisible.Position = UDim2.fromScale(0, 1)
invisible.Size = UDim2.fromOffset(0, 0)
invisible.BackgroundTransparency = 1

local label = Instance.new("ImageLabel")
label.AnchorPoint = Vector2.new(0, 0.5)
label.Position = UDim2.fromScale(0, 0)
label.BackgroundTransparency = 0.5
...

label.Parent = invisible
invisible.Parent = frame
frame.Parent = screenGui
screenGui.Parent = starterGui

local diagonal = SCREEN_SIZE.magnitude
local angle = math.atan2(-SCREEN_SIZE.y, SCREEN_SIZE.x)

invisible.Rotation = math.deg(angle)

local function update(t) 
	label.Size = UDim2.fromOffset(diagonal * t, LABEL_HEIGHT)
end

...
1 Like