A Deviate Faerie Dragon
Join Date: Jul 2016
Posts: 13

Originally Posted by semlar
I wanted to post here because I came up with a technique based on the one outlined in this topic which can be applied to any source texture, rather than relying on semicircles.

And somehow this broke in Legion. I'm not at the bottom of all the problems, but I have something that works on Legion.
Code:
 Usage:
 spinner = CreateSpinner(parent)
 spinner:SetTexture('texturePath')
 spinner:SetBlendMode('blendMode')
 spinner:SetVertexColor(r, g, b)
 spinner:SetClockwise(boolean)  true to fill clockwise, false to fill counterclockwise
 spinner:SetReverse(boolean)  true to empty the bar instead of filling it
 spinner:SetValue(percent)  value between 0 and 1 to fill the bar to
 Some math stuff
local cos, sin, pi2, halfpi = math.cos, math.sin, math.rad(360), math.rad(90)
local function Transform(tx, x, y, angle, aspect)  Translates texture to x, y and rotates about its center
local c, s = cos(angle), sin(angle)
local y, oy = y / aspect, 0.5 / aspect
local ULx, ULy = 0.5 + (x  0.5) * c  (y  oy) * s, (oy + (y  oy) * c + (x  0.5) * s) * aspect
local LLx, LLy = 0.5 + (x  0.5) * c  (y + oy) * s, (oy + (y + oy) * c + (x  0.5) * s) * aspect
local URx, URy = 0.5 + (x + 0.5) * c  (y  oy) * s, (oy + (y  oy) * c + (x + 0.5) * s) * aspect
local LRx, LRy = 0.5 + (x + 0.5) * c  (y + oy) * s, (oy + (y + oy) * c + (x + 0.5) * s) * aspect
tx:SetTexCoord(ULx, ULy, LLx, LLy, URx, URy, LRx, LRy)
end
 Permanently pause our rotation animation after it starts playing
local function OnPlayUpdate(self)
self:SetScript('OnUpdate', nil)
self:Pause()
end
local function OnPlay(self)
self:SetScript('OnUpdate', OnPlayUpdate)
end
local function SetValue(self, value)
 Correct invalid ranges, preferably just don't feed it invalid numbers
if value > 1 then value = 1
elseif value < 0 then value = 0 end
 Reverse our normal behavior
if self._reverse then
value = 1  value
end
 Determine which quadrant we're in
local q, quadrant = self._clockwise and (1  value) or value  4  floor(value / 0.25)
if q >= 0.75 then
quadrant = 1
elseif q >= 0.5 then
quadrant = 2
elseif q >= 0.25 then
quadrant = 3
else
quadrant = 4
end
if self._quadrant ~= quadrant then
self._quadrant = quadrant
 Show/hide necessary textures if we need to
if self._clockwise then
for i = 1, 4 do
self._textures[i]:SetShown(i < quadrant)
end
else
for i = 1, 4 do
self._textures[i]:SetShown(i > quadrant)
end
end
 Move scrollframe/wedge to the proper quadrant
self._scrollframe:Hide();
self._scrollframe:SetAllPoints(self._textures[quadrant])
self._scrollframe:Show();
end
 Rotate the things
local rads = value * pi2
if not self._clockwise then rads = rads + halfpi end
Transform(self._wedge, 0.5, 0.5, rads, self._aspect)
self._rotation:SetDuration(0.000001)
self._rotation:SetEndDelay(2147483647)
self._rotation:SetOrigin('BOTTOMRIGHT', 0, 0)
self._rotation:SetRadians(rads);
self._group:Play();
end
local function SetClockwise(self, clockwise)
self._clockwise = clockwise
end
local function SetReverse(self, reverse)
self._reverse = reverse
end
local function OnSizeChanged(self, width, height)
self._wedge:SetSize(width, height)  it's important to keep this texture sized correctly
self._aspect = width / height  required to calculate the texture coordinates
end
 Creates a function that calls a method on all textures at once
local function CreateTextureFunction(func, self, ...)
return function(self, ...)
for i = 1, 4 do
local tx = self._textures[i]
tx[func](tx, ...)
end
self._wedge[func](self._wedge, ...)
end
end
 Pass calls to these functions on our frame to its textures
local TextureFunctions = {
SetTexture = CreateTextureFunction('SetTexture'),
SetBlendMode = CreateTextureFunction('SetBlendMode'),
SetVertexColor = CreateTextureFunction('SetVertexColor'),
}
local function CreateSpinner(parent)
local spinner = CreateFrame('Frame', nil, parent)
 ScrollFrame clips the actively animating portion of the spinner
local scrollframe = CreateFrame('ScrollFrame', nil, spinner)
scrollframe:SetPoint('BOTTOMLEFT', spinner, 'CENTER')
scrollframe:SetPoint('TOPRIGHT')
spinner._scrollframe = scrollframe
local scrollchild = CreateFrame('frame', nil, scrollframe)
scrollframe:SetScrollChild(scrollchild)
scrollchild:SetAllPoints(scrollframe)
 Wedge thing
local wedge = scrollchild:CreateTexture()
wedge:SetPoint('BOTTOMRIGHT', spinner, 'CENTER')
spinner._wedge = wedge
 Top Right
local trTexture = spinner:CreateTexture()
trTexture:SetPoint('BOTTOMLEFT', spinner, 'CENTER')
trTexture:SetPoint('TOPRIGHT')
trTexture:SetTexCoord(0.5, 1, 0, 0.5)
 Bottom Right
local brTexture = spinner:CreateTexture()
brTexture:SetPoint('TOPLEFT', spinner, 'CENTER')
brTexture:SetPoint('BOTTOMRIGHT')
brTexture:SetTexCoord(0.5, 1, 0.5, 1)
 Bottom Left
local blTexture = spinner:CreateTexture()
blTexture:SetPoint('TOPRIGHT', spinner, 'CENTER')
blTexture:SetPoint('BOTTOMLEFT')
blTexture:SetTexCoord(0, 0.5, 0.5, 1)
 Top Left
local tlTexture = spinner:CreateTexture()
tlTexture:SetPoint('BOTTOMRIGHT', spinner, 'CENTER')
tlTexture:SetPoint('TOPLEFT')
tlTexture:SetTexCoord(0, 0.5, 0, 0.5)
 /41\  Clockwise texture arrangement
 \32/ 
spinner._textures = {trTexture, brTexture, blTexture, tlTexture}
spinner._quadrant = nil  Current active quadrant
spinner._clockwise = true  fill clockwise
spinner._reverse = false  Treat the provided value as its inverse, eg. 75% will display as 25%
spinner._aspect = 1  aspect ratio, width / height of spinner frame
spinner:HookScript('OnSizeChanged', OnSizeChanged)
for method, func in pairs(TextureFunctions) do
spinner[method] = func
end
spinner.SetClockwise = SetClockwise
spinner.SetReverse = SetReverse
spinner.SetValue = SetValue
local group = wedge:CreateAnimationGroup()
group:SetScript('OnFinished', function() group:Play() end);
local rotation = group:CreateAnimation('Rotation')
spinner._rotation = rotation
spinner._group = group;
return spinner
end

 Demo

local spinner1 = CreateSpinner(UIParent)
spinner1:SetPoint('BOTTOMRIGHT', UIParent, 'CENTER', 2, 2)
spinner1:SetSize(64, 64)
spinner1:SetTexture('interface/icons/inv_mushroom_11')
spinner1:SetClockwise(false)
spinner1:SetReverse(false)
local spinner2 = CreateSpinner(UIParent)
spinner2:SetPoint('BOTTOMLEFT', UIParent, 'CENTER', 2, 2)
spinner2:SetSize(64, 64)
spinner2:SetTexture('interface/icons/inv_mushroom_11')
spinner2:SetClockwise(true)
spinner2:SetReverse(false)
local spinner3 = CreateSpinner(UIParent)
spinner3:SetPoint('TOPRIGHT', UIParent, 'CENTER', 2, 2)
spinner3:SetSize(64, 64)
spinner3:SetTexture('interface/icons/inv_mushroom_11')
spinner3:SetClockwise(true)
spinner3:SetReverse(true)
local spinner4 = CreateSpinner(UIParent)
spinner4:SetPoint('TOPLEFT', UIParent, 'CENTER', 2, 2)
spinner4:SetSize(64, 64)
spinner4:SetTexture('interface/icons/inv_mushroom_11')
spinner4:SetClockwise(false)
spinner4:SetReverse(true)
local f = CreateFrame('frame')
local timespent = 0
f:SetScript('OnUpdate', function(self, elapsed)
timespent = timespent + elapsed
if timespent >= 3 then
timespent = 0
end
local value = timespent / 3
spinner1:SetValue(value)
spinner2:SetValue(value)
spinner3:SetValue(value)
spinner4:SetValue(value)
end)
And the diff:
Code:
diff git a/test4.lua b/test4.lua
index 5dbe0bc..8a0ccfe 100644
 a/test4.lua
+++ b/test4.lua
@@ 64,14 +64,20 @@ local function SetValue(self, value)
end
end
 Move scrollframe/wedge to the proper quadrant
+ self._scrollframe:Hide();
self._scrollframe:SetAllPoints(self._textures[quadrant])
+ self._scrollframe:Show();
end
 Rotate the things
local rads = value * pi2
if not self._clockwise then rads = rads + halfpi end
Transform(self._wedge, 0.5, 0.5, rads, self._aspect)
 self._rotation:SetRadians(rads)
+ self._rotation:SetDuration(0.000001)
+ self._rotation:SetEndDelay(2147483647)
+ self._rotation:SetOrigin('BOTTOMRIGHT', 0, 0)
+ self._rotation:SetRadians(rads);
+ self._group:Play();
end
local function SetClockwise(self, clockwise)
@@ 166,14 +172,10 @@ local function CreateSpinner(parent)
spinner.SetValue = SetValue
local group = wedge:CreateAnimationGroup()
+ group:SetScript('OnFinished', function() group:Play() end);
local rotation = group:CreateAnimation('Rotation')
spinner._rotation = rotation
 rotation:SetDuration(0)
 rotation:SetEndDelay(1)
 rotation:SetOrigin('BOTTOMRIGHT', 0, 0)
 group:SetScript('OnPlay', OnPlay)
 group:Play()

+ spinner._group = group;
return spinner
end
