diff --git a/AnimationGroup.lua b/AnimationGroup.lua index f6e1e45..90c55e6 100644 --- a/AnimationGroup.lua +++ b/AnimationGroup.lua @@ -43,7 +43,7 @@ function AG:New(name, parent) end local function CreateAnimationGroup(self, name, inherits_from) - local ag = AG.AnimationGroup:Bind(CreateFrame('Frame', nil, self)) + local ag = AG.AnimationGroup:Bind(CreateFrame('Frame', name, self)) ag:__Initialize(self) ag.__Initialize = nil @@ -60,27 +60,48 @@ function CreateFrame(...) return frame end - local function OnUpdate(self, elapsed) if self.paused then return end + local in_reverse = self.group.reverse - self.time = self.time + (self.group.reverse and -elapsed or elapsed) + self.time = self.time + (in_reverse and -elapsed or elapsed) + local animation_time = self.time - self.startDelay - if self.time > self.duration or (self.group.reverse and self.time < 0) then - AG:Stop(self) - AG:Fire(self.group, self, 'Finished') + self.progress = animation_time / self.duration + -- Fast min/max to ensure progress is bound within `0 < self.progress < 1' + self.progress = self.progress < 0 and 0 or self.progress + self.progress = self.progress > 1 and 1 or self.progress - return + -- These represent whether the time "pointer" is in the left- or the right delay part, at which it is not animating, but should remain visible. + local is_delayed_start = self.time < self.startDelay and 0 < self.time + local is_delayed_end = (self.totalTime - self.endDelay) < self.time and self.time < self.totalTime + + local is_animation_complete = (in_reverse and self.time < 0) or self.totalTime < self.time + self.delayed = is_delayed_start or is_delayed_end + + -- We should calculate smoothProgress before Users's callback_handlers + self.smoothProgress = self.smoothing_func(self.progress).y + + -- Calling user's callback_handlers + if type(self.handlers["OnUpdate"]) == 'function' then + self.handlers["OnUpdate"](self, elapsed) end - -- Temporary until all animation types are implemented + -- Calling animations OnUpdate if self.OnUpdate then self:OnUpdate(elapsed) end -end + -- Stop animation after last update + if is_animation_complete then + AG:Stop(self) + AG:Fire(self.group, self, 'Finished') + return + end + +end --[[ Global library routines @@ -109,27 +130,19 @@ function AG:StopGroup(group) AG:Stop(animation) end end - self:LoadProperties(group) - group.playing = false end function AG:SaveProperties(group) - group.properties.alpha = group.parent:GetAlpha() - group.properties.width = group.parent:GetWidth() - group.properties.height = group.parent:GetHeight() - group.properties.point = { group.parent:GetPoint() } + for k, v in pairs(group:GetAnimations()) do + v:SaveProperties() + end end function AG:LoadProperties(group) - group.parent:SetAlpha(group.properties.alpha) - group.parent:SetWidth(group.properties.width) - group.parent:SetHeight(group.properties.height) - - local point = group.properties.point - if point and point[1] then - group.parent:SetPoint(unpack(point)) + for k, v in pairs(group:GetAnimations()) do + v:LoadProperties() end end @@ -137,23 +150,25 @@ function AG:Stop(animation) animation.time = 0 if animation.OnUpdate then - animation:SetScript('OnUpdate', nil) + animation:_SetScript('OnUpdate', nil) end animation.playing = false end function AG:Play(animation) - if not animation.playing and animation.group.parent:IsVisible() then - animation.time = animation.group.reverse and animation.duration or 0 + if not animation.playing and animation.target:IsVisible() then + animation.totalTime = animation.startDelay + animation.duration + animation.endDelay + animation.time = animation.group.reverse and animation.totalTime or 0 + animation.progress = 0 + animation.smoothProgress = 0 animation.playing = true - animation:SetScript('OnUpdate', function() OnUpdate(this, arg1) end) + animation:_SetScript('OnUpdate', function() OnUpdate(this, arg1) end) end animation.paused = false end - function AG:Pause(animation) animation.paused = true end @@ -176,6 +191,7 @@ function AG:Fire(group, animation, signal) local all_finished = true local bouncing = group.loop_type == 'BOUNCE' + local repeating = group.loop_type == 'REPEAT' -- Only animations notify with `FINISHED' signals! if signal == 'Finished' then @@ -194,7 +210,7 @@ function AG:Fire(group, animation, signal) if callback_handlers[signal] then group_func = group.handlers['On' .. signal] - if not animation then + if not animation and group.animations[group.order + 1] then local handler_func for _, anim in next, group.animations[group.order + 1] do handler_func = anim.handlers['On' .. signal] @@ -202,7 +218,7 @@ function AG:Fire(group, animation, signal) table.insert(func, { anim, handler_func }) end end - else + elseif animation then func = animation.handlers['On' .. signal] end end @@ -222,8 +238,10 @@ function AG:Fire(group, animation, signal) if (signal == 'Finished' and all_finished) or signal == 'Bounce' then if group.shifted then if not group.finishing then - group.reverse = not group.reverse + group.reverse = bouncing and (not group.reverse) AG:PlayGroup(group) + group_func = group.handlers['OnLoop'] + table.insert(args, group.reverse and 'REVERSE' or 'FORWARD') end group.shifted = false else @@ -253,15 +271,10 @@ function AG:Fire(group, animation, signal) -- must therefore be performed AFTER the animation callback and BEFORE the -- group's callback if (signal == 'Finished' and all_finished and shift) then - if (group.finishing and bouncing) or (not bouncing) then + if (group.finishing and (bouncing or repeating)) or not (bouncing or repeating) then AG:StopGroup(group) - group_func = group.handlers['OnFinished'] table.insert(args, group.finishing) - else - group_func = group.handlers['OnLoop'] - - table.insert(args, group.reverse and 'REVERSE' or 'FORWARD') end end @@ -271,15 +284,24 @@ function AG:Fire(group, animation, signal) end -- We `bounce' if the boundary orders have no animations - if (not group.playing and shift and - (not group.finishing) and bouncing) then - group.reverse = not group.reverse + if (signal == 'Finished' and not group.playing and shift and + (not group.finishing) and (bouncing or repeating)) then + if repeating then + group.order = -1 + else + group.reverse = not group.reverse + end + group_func = group.handlers['OnLoop'] + table.insert(args, group.reverse and 'REVERSE' or 'FORWARD') + if type(group_func) == 'function' then + group_func(group, unpack(args)) + end AG:Fire(group, nil, 'Bounce') end end function AG:MoveOrder(group, animation, new_order) - local old_order = group.order + local old_order = animation.order for i, anim in next, group.animations[old_order + 1] do if anim == animation then diff --git a/alpha.lua b/alpha.lua index 216ea6a..14f4ea3 100644 --- a/alpha.lua +++ b/alpha.lua @@ -31,40 +31,45 @@ if AG.Alpha then return end local Alpha = AG:New('Alpha', AG.Animation) function Alpha:__Initialize() - self.alpha_change = nil - self.alpha_from = 1 + self.alpha_change = 0 + self.alpha_from = 0 self.alpha_to = 0 end -function Alpha:SetChange(change) - self.alpha_change = change +function Alpha:SaveProperties() + self.properties.alpha = self.target:GetAlpha() end -function Alpha:GetChange() - return self.alpha_change +function Alpha:LoadProperties() + if self.properties.alpha then self.target:SetAlpha(self.properties.alpha) end end -function Alpha:SetFromAlpha(alpha) - self.alpha_from = alpha - SetChange(self) +function Alpha:SetChange(change) + self.alpha_change = change end -function Alpha:SetToAlpha(alpha) - self.alpha_to = alpha - SetChange(self) +function Alpha:GetChange() + return self.alpha_change end -function Alpha:OnUpdate(elapsed) - local properties = self.group.properties +do + local function SetChange(self) + self.alpha_change = self.alpha_to - self.alpha_from + end - self.progress = self.smoothing_func(self.time / self.duration).y + function Alpha:SetFromAlpha(alpha) + self.alpha_from = alpha + SetChange(self) + end - local frame = self.group.parent - - frame:SetAlpha(properties.alpha + self.progress * self.alpha_change) + function Alpha:SetToAlpha(alpha) + self.alpha_to = alpha + SetChange(self) + end end +function Alpha:OnUpdate(elapsed) + local frame = self.target -local function SetChange() - self.alpha_change = self.alpha_from - self.alpha_to + frame:SetAlpha(self.alpha_from + self.smoothProgress * self.alpha_change) end diff --git a/animation.lua b/animation.lua index 83ee9b1..c17f471 100644 --- a/animation.lua +++ b/animation.lua @@ -43,7 +43,6 @@ function Animation:__SetScript(handler, func) end end - --[[ API --]] @@ -54,14 +53,12 @@ function Animation:Play() AG:Fire(self.group, self, 'Play') end - function Animation:Pause() AG:Pause(self) AG:Fire(self.group, self, 'Pause') end - function Animation:Stop() AG:Stop(self) @@ -69,7 +66,7 @@ function Animation:Stop() end function Animation:IsDone() - return not self.playing + return self.finished end function Animation:IsPlaying() @@ -85,25 +82,27 @@ function Animation:IsStopped() end function Animation:IsDelaying() - return not not self.delay + return self.delayed end function Animation:GetElapsed() - return self.time + return self.time < 0 and 0 or self.totalTime < self.time and self.totalTime or self.time end function Animation:SetStartDelay(delay_sec) - self.delay = delay_sec + self.startDelay = delay_sec end function Animation:GetStartDelay() - return self.delay + return self.startDelay end function Animation:SetEndDelay(delay_sec) + self.endDelay = delay_sec end function Animation:GetEndDelay() + return self.endDelay end function Animation:SetDuration(duration) @@ -118,10 +117,12 @@ function Animation:GetProgress() return self.progress end -function Animation:GetSmoothProgress() +function Animation:SetSmoothProgress(smoothProgress) + self.smoothProgress = smoothProgress end -function Animation:GetProgressWithDelay() +function Animation:GetSmoothProgress() + return self.smoothProgress end function Animation:SetMaxFramerate(framerate) @@ -152,3 +153,12 @@ end function Animation:GetRegionParent() return self.group.parent end + +function Animation:SetTarget(region) + self.target = region + self:SaveProperties() +end + +function Animation:GetTarget() + return self.target +end diff --git a/curves.lua b/curves.lua index c948fea..07c1850 100644 --- a/curves.lua +++ b/curves.lua @@ -42,24 +42,23 @@ end local mt = { __add = Point.Add, - __mul = Point.Multiply } + __mul = Point.Multiply +} function Point:New(x, y) - return setmetatable({x = x, y = y}, mt) + return setmetatable({ x = x, y = y }, mt) end local function QuadraticBezier(p_0, p_1, p_2) return function(t) - return - (1 - t) * ((1 - t) * p_0 + t * p_1) + + return (1 - t) * ((1 - t) * p_0 + t * p_1) + t * ((1 - t) * p_1 + t * p_2) end end local function CubicBezier(p_0, p_1, p_2, p_3) return function(t) - return - (1 - t) * QuadraticBezier(p_0, p_1, p_2)(t) + + return (1 - t) * QuadraticBezier(p_0, p_1, p_2)(t) + t * QuadraticBezier(p_1, p_2, p_3)(t) end end @@ -92,7 +91,7 @@ local CubicBezierEaseInOut = function() end local Linear = function(t) - return {x = 0, y = t} + return { x = 0, y = t } end Curves.curves = { @@ -103,4 +102,4 @@ Curves.curves = { ['LINEAR'] = Linear } -setmetatable(Curves, {__index = Curves.curves }) +setmetatable(Curves, { __index = Curves.curves }) diff --git a/group.lua b/group.lua index 097cab0..cf4218c 100644 --- a/group.lua +++ b/group.lua @@ -43,7 +43,6 @@ local function SetScript(self, handler, func) end end - --[[ API --]] @@ -128,20 +127,42 @@ function AnimationGroup:GetLoopState() return self.loop_state end +function AnimationGroup:GetAnimations() + local animations = {} + for i = 0, AG.ORDER_LIMIT - 1, 1 do + if self.animations[i + 1] then + for _, animation in next, self.animations[i + 1] do + if animation then + tinsert(animations, animation) + end + end + end + end + return animations +end + function AnimationGroup:CreateAnimation(animation_type, name, inherits_from) + animation_type = string.upper(string.sub(animation_type, 1, 1)) .. string.lower(string.sub(animation_type, 2)) local animation = AG[animation_type]:Bind(CreateFrame('Frame', name)) animation.group = self animation:SetParent(self) animation.type = animation_type - animation.duration = nil - animation.progress = nil + animation.duration = 0 + animation.progress = 0 + animation.smoothProgress = 0 + animation.startDelay = 0 + animation.endDelay = 0 + animation.totalTime = 0 + animation.target = animation.group.parent + animation.handlers = { ['OnLoad'] = true, ['OnPlay'] = true, ['OnPaused'] = true, ['OnStop'] = true, - ['OnFinished'] = true + ['OnFinished'] = true, + ['OnUpdate'] = true, } local default_smoothing = 'LINEAR' @@ -156,13 +177,21 @@ function AnimationGroup:CreateAnimation(animation_type, name, inherits_from) animation._SetScript = animation.SetScript animation.SetScript = animation.__SetScript + animation.properties = { + alpha = nil, + width = nil, + height = nil, + point = {} + } + + animation:SaveProperties() + animation.order = 0 table.insert(self.animations[animation.order + 1], animation) return animation end - --[[ Private --]] @@ -186,20 +215,14 @@ function AnimationGroup:__Initialize(parent) ['OnPlay'] = true, ['OnPaused'] = true, ['OnStop'] = true, - ['OnFinished'] = true + ['OnFinished'] = true, + ['OnLoop'] = true, } -- The original implementation claims to support up to 100 orders... yuck! -- Lets keep it at 10 for sanity. self.animations = { {} } - self.properties = { - alpha = nil, - width = nil, - height = nil, - point = {} - } - self._SetScript = self.SetScript self.SetScript = SetScript end diff --git a/path.lua b/path.lua index 865ecf8..0881ba9 100644 --- a/path.lua +++ b/path.lua @@ -39,8 +39,8 @@ end -- Adds a new path control point. function Path:CreateControlPoint(name, template, order) local control_point = { name = name, - template = template, - order = order } + template = template, + order = order } table.insert(self.control_points, control_points) end diff --git a/rotation.lua b/rotation.lua index 646c11b..58e7503 100644 --- a/rotation.lua +++ b/rotation.lua @@ -42,26 +42,45 @@ function Rotation:__Initialize() self.origin.y = 0 end +function Rotation:SaveProperties() + self.properties.width = self.target:GetWidth() + self.properties.height = self.target:GetHeight() + local point, relativeRegion, relativePoint, offsetX, offsetY = self.target:GetPoint() + self.properties.point = { point = point or "CENTER", + relativeRegion = relativeRegion or UIParent, + relativePoint = relativePoint or "CENTER", + offsetX = offsetX or 0, + offsetY = offsetY or 0 } +end + +function Rotation:LoadProperties() + if self.properties.width then self.target:SetWidth(self.properties.width) end + if self.properties.height then self.target:SetHeight(self.properties.height) end + + local point = self.properties.point + self.target:SetPoint(point.point, point.relativeRegion, point.relativePoint, point.offsetX, point.offsetY) +end + local GetRegions = function(self) - return { self.group.parent:GetRegions() } + return { self.target:GetRegions() } end local anchor_coords = { - ['TOP'] = { x = 0, y = 1 }, - ['LEFT'] = { x = -1, y = 0 }, - ['BOTTOM'] = { x = 0, y = -1 }, - ['RIGHT'] = { x = 1, y = 0 }, - ['TOPLEFT'] = { x = -1, y = 1 }, - ['TOPRIGHT'] = { x = 1, y = 1 }, + ['TOP'] = { x = 0, y = 1 }, + ['LEFT'] = { x = -1, y = 0 }, + ['BOTTOM'] = { x = 0, y = -1 }, + ['RIGHT'] = { x = 1, y = 0 }, + ['TOPLEFT'] = { x = -1, y = 1 }, + ['TOPRIGHT'] = { x = 1, y = 1 }, ['BOTTOMLEFT'] = { x = -1, y = -1 }, - ['BOTTOMRIGHT'] = { x = 1, y = -1 }, - ['CENTER'] = { x = 0, y = 0 } + ['BOTTOMRIGHT'] = { x = 1, y = -1 }, + ['CENTER'] = { x = 0, y = 0 } } local frame_corners = { - [1] = { x = -1, y = 1 }, -- upper left - [2] = { x = -1, y = -1 }, -- lower left - [3] = { x = 1, y = 1 }, -- upper right - [4] = { x = 1, y = -1 } -- lower right + [1] = { x = -1, y = 1 }, -- upper left + [2] = { x = -1, y = -1 }, -- lower left + [3] = { x = 1, y = 1 }, -- upper right + [4] = { x = 1, y = -1 } -- lower right } local corners = { 0, 0, 0, 0, 0, 0, 0, 0 } local function GetCoords(self, progress) @@ -69,7 +88,7 @@ local function GetCoords(self, progress) local _cos = cos(rad) local _sin = sin(rad) - local properties = self.group.properties + local properties = self.properties local origin = { x = anchor_coords[self.origin.point].x + @@ -95,10 +114,8 @@ local function GetCoords(self, progress) end function Rotation:OnUpdate(elapsed) - self.progress = self.smoothing_func(self.time / self.duration).y - local regions = GetRegions(self) - local coords = { GetCoords(self, self.progress) } + local coords = { GetCoords(self, self.smoothProgress) } for _, region in next, regions do if region.GetTexture then region:SetTexCoord(unpack(coords)) @@ -109,20 +126,23 @@ end function Rotation:SetDegrees(degrees) self.radians = (degrees / 360) * 2 * pi end + function Rotation:GetDegrees() return (self.radians / (2 * pi)) * 360 end + function Rotation:SetRadians(radians) self.radians = radians end + function Rotation:GetRadians() return self.radians end function Rotation:SetOrigin(point, offsetX, offsetY) self.origin.point = point - self.origin.x = x - self.origin.y = y + self.origin.x = offsetX + self.origin.y = offsetY end function Rotation:GetOrigin() diff --git a/scale.lua b/scale.lua index 91c069c..773570d 100644 --- a/scale.lua +++ b/scale.lua @@ -32,18 +32,31 @@ local Scale = AG:New('Scale', AG.Animation) function Scale:__Initialize() self.origin = {} - self.origin.point = nil - self.origin.x = nil - self.origin.y = nil + self.origin.point = "CENTER" + self.origin.x = 0 + self.origin.y = 0 self.scale = {} - self.scale.x = nil - self.scale.y = nil + self.scale.x = 0 + self.scale.y = 0 + self.from = {} + self.from.x = 0 + self.from.y = 0 +end + +function Scale:SaveProperties() + self.properties.width = self.target:GetWidth() + self.properties.height = self.target:GetHeight() +end + +function Scale:LoadProperties() + if self.properties.width then self.target:SetWidth(self.properties.width) end + if self.properties.height then self.target:SetHeight(self.properties.height) end end function Scale:SetOrigin(point, offset_x, offset_y) self.origin.point = point - self.origin.x = x - self.origin.y = y + self.origin.x = offset_x + self.origin.y = offset_y end function Scale:GetOrigin() @@ -57,18 +70,35 @@ function Scale:SetScale(x, y) self.scale.y = y end +function Scale:SetToScale(x, y) + self.scale.x = x + self.scale.y = y +end + function Scale:GetScale() local scale = self.scale return scale.x, scale.y end +function Scale:SetFromScale(x, y) + self.from.x = x + self.from.y = y +end + +function Scale:GetFromScale() + local scale = self.from + + return scale.x, scale.y +end + function Scale:OnUpdate(elapsed) - local properties = self.group.properties + local properties = self.properties - self.progress = self.smoothing_func(self.time / self.duration).y + local from_x = self.from.x + local from_y = self.from.y - local frame = self.group.parent - frame:SetWidth(properties.width + self.progress * (properties.width * self.scale.x)) - frame:SetHeight(properties.height + self.progress * (properties.height * self.scale.y)) + local frame = self.target + frame:SetWidth(properties.width * (from_x + self.smoothProgress * (self.scale.x - from_x))) + frame:SetHeight(properties.height * (from_y + self.smoothProgress * (self.scale.y - from_y))) end diff --git a/translation.lua b/translation.lua index f8f972f..60e186e 100644 --- a/translation.lua +++ b/translation.lua @@ -36,6 +36,20 @@ function Translation:__Initialize() self.offset.y = nil end +function Translation:SaveProperties() + local point, relativeRegion, relativePoint, offsetX, offsetY = self.target:GetPoint() + self.properties.point = { point = point or "CENTER", + relativeRegion = relativeRegion or UIParent, + relativePoint = relativePoint or "CENTER", + offsetX = offsetX or 0, + offsetY = offsetY or 0 } +end + +function Translation:LoadProperties() + local point = self.properties.point + self.target:SetPoint(point.point, point.relativeRegion, point.relativePoint, point.offsetX, point.offsetY) +end + function Translation:SetOffset(x, y) self.offset.x = x self.offset.y = y @@ -46,14 +60,12 @@ function Translation:GetOffset() end function Translation:OnUpdate(elapsed) - self.progress = self.smoothing_func(self.time / self.duration).y - - local frame = self.group.parent + local frame = self.target - local point = self.group.properties.point + local point = self.properties.point frame:ClearAllPoints() - frame:SetPoint(point[1], point[2], point[3], - point[4] + self.progress * self.offset.x, - point[5] + self.progress * self.offset.y) + frame:SetPoint(point.point, point.relativeRegion, point.relativePoint, + point.offsetX + self.smoothProgress * self.offset.x, + point.offsetY + self.smoothProgress * self.offset.y) end