if not WeakAuras.IsLibsOK() then return end ---@type string local AddonName = ... ---@class Private local Private = select(2, ...) local L = WeakAuras.L -- Animations local animations = {} local pending_controls = {} local anim_function_strings = Private.anim_function_strings; local function noopErrorHandler() end local function RunAnimation(key, anim, elapsed, time) Private.StartProfileUID(anim.auraUID) local finished = false if(anim.duration_type == "seconds") then if anim.duration > 0 then anim.progress = anim.progress + (elapsed / anim.duration) else anim.progress = anim.progress + (elapsed / 1) end if(anim.progress >= 1) then anim.progress = 1 finished = true end elseif(anim.duration_type == "relative") then local region = anim.region if ((region.progressType == "timed" and region.duration < 0.01) or (region.progressType == "static" and region.value < 0.01)) then anim.progress = 0 if(anim.type == "start" or anim.type == "finish") then finished = true end else local relativeProgress = 0 if(region.progressType == "static") then relativeProgress = region.value / region.total elseif (region.progressType == "timed") then relativeProgress = 1 - ((region.expirationTime - time) / region.duration) end relativeProgress = region.inverse and (1 - relativeProgress) or relativeProgress anim.progress = anim.duration > 0 and relativeProgress / anim.duration or 0 local iteration = math.floor(anim.progress) --anim.progress = anim.progress - iteration if not(anim.iteration) then anim.iteration = iteration elseif(anim.iteration ~= iteration) then anim.iteration = nil finished = true end end else anim.progress = 1 end local progress = anim.inverse and (1 - anim.progress) or anim.progress progress = anim.easeFunc(progress, anim.easeStrength or 3) Private.ActivateAuraEnvironmentForRegion(anim.region) if(anim.translateFunc) then local errorHandler = WeakAuras.IsOptionsOpen() and noopErrorHandler or Private.GetErrorHandlerUid(anim.auraUID, L["Slide Animation"]) if (anim.region.SetOffsetAnim) then local ok, x, y = xpcall(anim.translateFunc, errorHandler, progress, 0, 0, anim.dX, anim.dY) anim.region:SetOffsetAnim(x, y) else anim.region:ClearAllPoints() local ok, x, y = xpcall(anim.translateFunc, errorHandler, progress, anim.startX, anim.startY, anim.dX, anim.dY) if (ok) then anim.region:SetPoint(anim.selfPoint, anim.anchor, anim.anchorPoint, x, y) end end end if(anim.alphaFunc) then local errorHandler = WeakAuras.IsOptionsOpen() and noopErrorHandler or Private.GetErrorHandlerUid(anim.auraUID, L["Fade Animation"]) local ok, alpha = xpcall(anim.alphaFunc, errorHandler, progress, anim.startAlpha, anim.dAlpha) if (ok) then if (anim.region.SetAnimAlpha) then anim.region:SetAnimAlpha(alpha) else anim.region:SetAlpha(alpha) end end end if(anim.scaleFunc) then local errorHandler = WeakAuras.IsOptionsOpen() and noopErrorHandler or Private.GetErrorHandlerUid(anim.auraUID, L["Zoom Animation"]) local ok, scaleX, scaleY = xpcall(anim.scaleFunc, errorHandler, progress, 1, 1, anim.scaleX, anim.scaleY) if (ok) then if(anim.region.Scale) then anim.region:Scale(scaleX, scaleY) else anim.region:SetWidth(anim.startWidth * scaleX) anim.region:SetHeight(anim.startHeight * scaleY) end end end if(anim.rotateFunc and anim.region.SetAnimRotation) then local errorHandler = WeakAuras.IsOptionsOpen() and noopErrorHandler or Private.GetErrorHandlerUid(anim.auraUID, L["Rotate Animation"]) local ok, rotate = xpcall(anim.rotateFunc, errorHandler, progress, anim.region:GetBaseRotation(), anim.rotate) if (ok) then anim.region:SetAnimRotation(rotate) end end if(anim.colorFunc and anim.region.ColorAnim) then local errorHandler = WeakAuras.IsOptionsOpen() and noopErrorHandler or Private.GetErrorHandlerUid(anim.auraUID, L["Color Animation"]) local startR, startG, startB, startA = anim.region:GetColor() startR, startG, startB, startA = startR or 1, startG or 1, startB or 1, startA or 1 local ok, r, g, b, a = xpcall(anim.colorFunc, errorHandler, progress, startR, startG, startB, startA, anim.colorR, anim.colorG, anim.colorB, anim.colorA) if (ok) then local errorHandler = Private.GetErrorHandlerId(anim.region.id, "Custom Color") xpcall(anim.region.ColorAnim, errorHandler, anim.region, r, g, b, a) end end Private.ActivateAuraEnvironment(nil) if(finished) then if not(anim.loop) then if (anim.region.SetOffsetAnim) then anim.region:SetOffsetAnim(0, 0) else if(anim.startX) then anim.region:SetPoint(anim.selfPoint, anim.anchor, anim.anchorPoint, anim.startX, anim.startY) end end if (anim.region.SetAnimAlpha) then anim.region:SetAnimAlpha(nil) elseif(anim.startAlpha) then anim.region:SetAlpha(anim.startAlpha) end if(anim.startWidth) then if(anim.region.Scale) then anim.region:Scale(1, 1) else anim.region:SetWidth(anim.startWidth) anim.region:SetHeight(anim.startHeight) end end if(anim.region.SetAnimRotation) then anim.region:SetAnimRotation(nil) end if(anim.region.ColorAnim) then anim.region:ColorAnim(nil) end animations[key] = nil end if(anim.loop) then Private.Animate(anim.namespace, anim.auraUID, anim.type, anim.anim, anim.region, anim.inverse, anim.onFinished, anim.loop, anim.region.cloneId) elseif(anim.onFinished) then anim.onFinished() end end Private.StopProfileUID(anim.auraUID) end local updatingAnimations; local last_update = GetTime(); local function UpdateAnimations() if not updatingAnimations then return end Private.StartProfileSystem("animations"); for groupUid, groupRegion in pairs(pending_controls) do pending_controls[groupUid] = nil; groupRegion:DoPositionChildren(); end local time = GetTime(); local elapsed = time - last_update; last_update = time; for key, anim in pairs(animations) do RunAnimation(key, anim, elapsed, time) end Private.StopProfileSystem("animations"); end local frame = CreateFrame("Frame") Private.frames["WeakAuras Animation Frame"] = frame frame:SetScript("OnUpdate", UpdateAnimations) function Private.RegisterGroupForPositioning(uid, region) pending_controls[uid] = region if not updatingAnimations then updatingAnimations = true last_update = GetTime() end end function Private.Animate(namespace, uid, type, anim, region, inverse, onFinished, loop, cloneId) local key = tostring(region); local valid; if(anim and anim.type == "custom" and (anim.use_translate or anim.use_alpha or (anim.use_scale and region.Scale) or (anim.use_rotate and region.SetAnimRotation) or (anim.use_color and region.Color))) then valid = true; elseif(anim and anim.type == "preset" and anim.preset and Private.anim_presets[anim.preset]) then anim = Private.anim_presets[anim.preset]; valid = true; end if(valid) then local progress, duration, selfPoint, anchor, anchorPoint, startX, startY, startAlpha, startWidth, startHeight, easeType, easeStrength; local translateFunc, alphaFunc, scaleFunc, rotateFunc, colorFunc, easeFunc; if(animations[key]) then if(animations[key].type == type and not loop) then return "no replace"; end anim.x = anim.x or 0; anim.y = anim.y or 0; selfPoint, anchor, anchorPoint, startX, startY = animations[key].selfPoint, animations[key].anchor, animations[key].anchorPoint, animations[key].startX, animations[key].startY; anim.alpha = anim.alpha or 0; startAlpha = animations[key].startAlpha; anim.scalex = anim.scalex or 1; anim.scaley = anim.scaley or 1; startWidth, startHeight = animations[key].startWidth, animations[key].startHeight; anim.rotate = anim.rotate or 0; anim.colorR = anim.colorR or 1; anim.colorG = anim.colorG or 1; anim.colorB = anim.colorB or 1; anim.colorA = anim.colorA or 1; else anim.x = anim.x or 0; anim.y = anim.y or 0; if not region.SetOffsetAnim then selfPoint, anchor, anchorPoint, startX, startY = region:GetPoint(1); end anim.alpha = anim.alpha or 0; startAlpha = region:GetAlpha(); anim.scalex = anim.scalex or 1; anim.scaley = anim.scaley or 1; startWidth, startHeight = region:GetWidth(), region:GetHeight(); anim.rotate = anim.rotate or 0; anim.colorR = anim.colorR or 1; anim.colorG = anim.colorG or 1; anim.colorB = anim.colorB or 1; anim.colorA = anim.colorA or 1; end if(anim.use_translate) then if not(anim.translateType == "custom" and anim.translateFunc) then anim.translateType = anim.translateType or "straightTranslate"; anim.translateFunc = anim_function_strings[anim.translateType] end if (anim.translateFunc) then translateFunc = WeakAuras.LoadFunction("return " .. anim.translateFunc, uid); else if (region.SetOffsetAnim) then region:SetOffsetAnim(0, 0); else region:SetPoint(selfPoint, anchor, anchorPoint, startX, startY); end end else if (region.SetOffsetAnim) then region:SetOffsetAnim(0, 0); else region:SetPoint(selfPoint, anchor, anchorPoint, startX, startY); end end if(anim.use_alpha) then if not(anim.alphaType == "custom" and anim.alphaFunc) then anim.alphaType = anim.alphaType or "straight"; anim.alphaFunc = anim_function_strings[anim.alphaType] end if (anim.alphaFunc) then alphaFunc = WeakAuras.LoadFunction("return " .. anim.alphaFunc, uid); else if (region.SetAnimAlpha) then region:SetAnimAlpha(nil); else region:SetAlpha(startAlpha); end end else if (region.SetAnimAlpha) then region:SetAnimAlpha(nil); else region:SetAlpha(startAlpha); end end if(anim.use_scale) then if not(anim.scaleType == "custom" and anim.scaleFunc) then anim.scaleType = anim.scaleType or "straightScale"; anim.scaleFunc = anim_function_strings[anim.scaleType] end if (anim.scaleFunc) then scaleFunc = WeakAuras.LoadFunction("return " .. anim.scaleFunc, uid); else region:Scale(1, 1); end elseif(region.Scale) then region:Scale(1, 1); end if(anim.use_rotate) then if not(anim.rotateType == "custom" and anim.rotateFunc) then anim.rotateType = anim.rotateType or "straight"; anim.rotateFunc = anim_function_strings[anim.rotateType] end if (anim.rotateFunc) then rotateFunc = WeakAuras.LoadFunction("return " .. anim.rotateFunc, uid); else region:SetAnimRotation(nil) end elseif(region.SetAnimRotation) then region:SetAnimRotation(nil) end if(anim.use_color) then if not(anim.colorType == "custom" and anim.colorFunc) then anim.colorType = anim.colorType or "straightColor"; anim.colorFunc = anim_function_strings[anim.colorType] end if (anim.colorFunc) then colorFunc = WeakAuras.LoadFunction("return " .. anim.colorFunc, uid); else region:ColorAnim(nil); end elseif(region.ColorAnim) then region:ColorAnim(nil); end easeFunc = Private.anim_ease_functions[anim.easeType or "none"] duration = Private.ParseNumber(anim.duration) or 0; progress = 0; if(namespace == "display" and type == "main" and not onFinished and not anim.duration_type == "relative") then local data = Private.GetDataByUID(uid); if(data and data.parent) then local parentRegion = WeakAuras.GetRegion(data.parent) if(parentRegion and parentRegion.controlledRegions) then for index, regionData in pairs(parentRegion.controlledRegions) do local childRegion = regionData.region; local childKey = regionData.key; if(childKey and childKey ~= tostring(region) and animations[childKey] and animations[childKey].type == "main" and duration == animations[childKey].duration) then progress = animations[childKey].progress; break; end end end end end local animation = animations[key] or {} animations[key] = animation animation.progress = progress animation.startX = startX animation.startY = startY animation.startAlpha = startAlpha animation.startWidth = startWidth animation.startHeight = startHeight animation.dX = (anim.use_translate and anim.x) animation.dY = (anim.use_translate and anim.y) animation.dAlpha = (anim.use_alpha and (anim.alpha - startAlpha)) animation.scaleX = (anim.use_scale and anim.scalex) animation.scaleY = (anim.use_scale and anim.scaley) animation.rotate = anim.rotate animation.colorR = (anim.use_color and anim.colorR) animation.colorG = (anim.use_color and anim.colorG) animation.colorB = (anim.use_color and anim.colorB) animation.colorA = (anim.use_color and anim.colorA) animation.translateFunc = translateFunc animation.alphaFunc = alphaFunc animation.scaleFunc = scaleFunc animation.rotateFunc = rotateFunc animation.colorFunc = colorFunc animation.region = region animation.selfPoint = selfPoint animation.anchor = anchor animation.anchorPoint = anchorPoint animation.duration = duration animation.duration_type = anim.duration_type or "seconds" animation.inverse = inverse animation.easeType = anim.easeType animation.easeFunc = easeFunc animation.easeStrength = anim.easeStrength animation.type = type animation.loop = loop animation.onFinished = onFinished animation.namespace = namespace; animation.anim = anim; animation.auraUID = uid if not(updatingAnimations) then last_update = GetTime() updatingAnimations = true; end return true; else if(animations[key]) then if(animations[key].type ~= type or loop) then Private.CancelAnimation(region, true, true, true, true, true); end end return false; end end function Private.CancelAnimation(region, resetPos, resetAlpha, resetScale, resetRotation, resetColor, doOnFinished) local key = tostring(region); local anim = animations[key]; if(anim) then if(resetPos) then if (anim.region.SetOffsetAnim) then anim.region:SetOffsetAnim(0, 0); else anim.region:ClearAllPoints(); anim.region:SetPoint(anim.selfPoint, anim.anchor, anim.anchorPoint, anim.startX, anim.startY); end end if(resetAlpha) then if (anim.region.SetAnimAlpha) then anim.region:SetAnimAlpha(nil); else anim.region:SetAlpha(anim.startAlpha); end end if(resetScale) then if(anim.region.Scale) then anim.region:Scale(1, 1); else anim.region:SetWidth(anim.startWidth); anim.region:SetHeight(anim.startHeight); end end if(resetRotation and anim.region.SetAnimRotation) then anim.region:SetAnimRotation(nil) end if(resetColor and anim.region.ColorAnim) then anim.region:ColorAnim(nil); end animations[key] = nil; if(doOnFinished and anim.onFinished) then anim.onFinished(); end return true; else return false; end end