459459-- 2. Custom OnUpdate animators — operate directly on the 4 edge textures
460460-- by modulating SetAlpha each frame. No LCG involved. Tick functions
461461-- live in `customTicks` below; the shared driver frame is created
462- -- lazily via ensureDriver(border ).
462+ -- lazily via the shared anim driver (registerAnimTick ).
463463-- "WIPE" sweep a bright highlight clockwise around perimeter
464464-- "RIPPLE" all edges pulse alpha with per-edge phase offsets
465465-- "SEGMENT_REVEAL" edges fade in sequentially top→right→bottom→left
@@ -503,13 +503,45 @@ end
503503-- Hides it, and the aura-container teardown path calls StopAnimation on every
504504-- slot border so a de-configured / winner-changed / rebuilt AD border leaves no
505505-- orphaned ticking driver.
506- local function ensureDriver (border )
507- if border .animDriver then return border .animDriver end
508- local driverParent = border ._secretRect and UIParent or border
509- local d = CreateFrame (" Frame" , nil , driverParent )
510- d .elapsed = 0
511- border .animDriver = d
512- return d
506+ -- Shared OnUpdate driver: ONE UIParent-hosted frame ticks a registry of every
507+ -- animated border, instead of a CreateFrame + OnUpdate per border. secretRect
508+ -- borders (AD / aura-container slot children) MUST be driven externally — the
509+ -- button subtree's onUpdateMode="disabled" suppresses OnUpdate through every
510+ -- descendant, so a driver parented under the border there would install but never
511+ -- fire. A single shared host keeps the per-border cost to one registry entry.
512+ --
513+ -- Each entry carries its own accumulated elapsed; ticks get (border, elapsed, dt)
514+ -- — elapsed for offset/phase-from-absolute effects (custom overlays, DF_DASH), dt
515+ -- for the DF_PULSATE phase accumulator. A NORMAL border is skipped while hidden
516+ -- (preserving the old per-border driver's auto-hide); secretRect borders always
517+ -- tick (their textures' visibility rides the slot's secret show/hide, and a tick
518+ -- on a hidden texture is a harmless SetAlpha — same as the prior UIParent driver).
519+ local animRegistry = {} -- [border] = { fn = fn, elapsed = number }
520+ local sharedAnimDriver
521+ local function ensureSharedAnimDriver ()
522+ if sharedAnimDriver then return sharedAnimDriver end
523+ sharedAnimDriver = CreateFrame (" Frame" , nil , UIParent )
524+ sharedAnimDriver :SetScript (" OnUpdate" , function (_ , dt )
525+ for border , e in pairs (animRegistry ) do
526+ if border ._secretRect or border :IsShown () then
527+ e .elapsed = e .elapsed + dt
528+ e .fn (border , e .elapsed , dt )
529+ end
530+ end
531+ end )
532+ return sharedAnimDriver
533+ end
534+ -- Register/replace this border's per-frame tick. initialElapsed seeds the
535+ -- accumulator (DF_DASH resumes its march across restarts; others start at 0).
536+ local function registerAnimTick (border , fn , initialElapsed )
537+ ensureSharedAnimDriver ()
538+ local e = animRegistry [border ]
539+ if not e then e = {}; animRegistry [border ] = e end
540+ e .fn = fn
541+ e .elapsed = initialElapsed or 0
542+ end
543+ local function unregisterAnimTick (border )
544+ animRegistry [border ] = nil
513545end
514546
515547-- Reset all four edges to fully opaque. Called from StopAnimation so the
@@ -963,11 +995,7 @@ function Border:StopAnimation(border)
963995 if border .animRect then stopAll (border .animRect ) end
964996 if border .glowExtent then stopAll (border .glowExtent ) end
965997 end
966- if border .animDriver then
967- border .animDriver :SetScript (" OnUpdate" , nil )
968- border .animDriver :Hide ()
969- border .animDriver .elapsed = 0
970- end
998+ unregisterAnimTick (border )
971999 -- Hide all overlay sets from prior animation passes. The cornerExtras
9721000 -- field is from a previous-rev CORNERS_ONLY implementation; we keep
9731001 -- the Hide-loop for backward compat on profiles where the field was
@@ -1031,8 +1059,8 @@ local function animSpecHash(anim)
10311059 }, " |" )
10321060end
10331061
1034- -- OnUpdate-driver effects: those whose motion is driven by border.animDriver's
1035- -- OnUpdate (as opposed to LCG glows or the static shape modes). The dedupe in
1062+ -- OnUpdate-driver effects: those whose motion is driven by the shared anim
1063+ -- driver's OnUpdate (as opposed to LCG glows or the static shape modes). The dedupe in
10361064-- StartAnimation verifies the driver is actually live for these before no-opping.
10371065local DRIVER_ANIMS = { DF_DASH = true , WIPE = true , RIPPLE = true , SEGMENT_REVEAL = true , DF_PULSATE = true }
10381066
@@ -1059,9 +1087,7 @@ function Border:StartAnimation(border, spec)
10591087 -- frequency slider off 1 and back" symptom). When the driver is dead, fall
10601088 -- through and restart instead of no-opping.
10611089 if border ._animHash == newHash then
1062- local d = border .animDriver
1063- if not DRIVER_ANIMS [border .activeAnimation ]
1064- or (d and d :IsShown () and d :GetScript (" OnUpdate" )) then
1090+ if not DRIVER_ANIMS [border .activeAnimation ] or animRegistry [border ] then
10651091 return
10661092 end
10671093 end
@@ -1171,12 +1197,8 @@ function Border:StartAnimation(border, spec)
11711197 local tick = customTicks [anim .type ]
11721198 if tick then
11731199 setupAnimOverlay (border , anim )
1174- local d = ensureDriver (border )
1175- d .elapsed = 0
1176- d :Show ()
1177- d :SetScript (" OnUpdate" , function (self , dt )
1178- self .elapsed = (self .elapsed or 0 ) + dt
1179- tick (border , anim , self .elapsed )
1200+ registerAnimTick (border , function (b , el )
1201+ tick (b , anim , el )
11801202 end )
11811203 border .activeAnimation = anim .type
11821204 return
@@ -1189,7 +1211,7 @@ function Border:StartAnimation(border, spec)
11891211 -- AD's legacy expiring border pulse; exposed as a first-class animation
11901212 -- type so it works as either a continuous Border Animation OR as the
11911213 -- value the new Expiring Animation dropdown will swap in below
1192- -- threshold (Stage 5.1d.2+). Uses ensureDriver's OnUpdate frame ; on
1214+ -- threshold (Stage 5.1d.2+). Uses the shared anim driver ; on
11931215 -- StopAnimation the existing resetEdgeAlphas() restores the edges
11941216 -- back to alpha 1 so the next render is clean.
11951217 if anim .type == " DF_PULSATE" then
@@ -1208,8 +1230,6 @@ function Border:StartAnimation(border, spec)
12081230 -- path at the top of StartAnimation can change the pulse speed on the
12091231 -- already-running driver without re-SetScript'ing.
12101232 border ._dfPulsatePeriod = 2 / rawFreq
1211- local d = ensureDriver (border )
1212- d :Show ()
12131233 -- Advance a PHASE accumulator in [0,1) by dt/period each frame rather
12141234 -- than deriving phase from absolute elapsed. Two consequences:
12151235 -- * Changing the period (frequency) only changes how fast the phase
@@ -1221,7 +1241,7 @@ function Border:StartAnimation(border, spec)
12211241 -- wave = (1 - cos(2π·phase)) / 2 is a smooth 0→1→0 (full→low→full)
12221242 -- curve with zero-slope endpoints, so each cycle blends seamlessly
12231243 -- into the next with no visible seam at the loop point.
1224- d : SetScript ( " OnUpdate " , function (self , dt )
1244+ registerAnimTick ( border , function (border , el , dt )
12251245 local p = border ._dfPulsatePeriod or 2
12261246 local ph = ((border ._dfPulsatePhase or 0 ) + dt / p ) % 1
12271247 border ._dfPulsatePhase = ph
@@ -1256,16 +1276,12 @@ function Border:StartAnimation(border, spec)
12561276 -- Marching: OnUpdate advances the offset, reading colour/size from
12571277 -- the fields so a live recolour is picked up next tick. elapsed
12581278 -- persists across restarts so a spec change doesn't snap the ants.
1259- local d = ensureDriver (border )
1260- d .elapsed = border ._dfDashElapsed or 0
1261- d :Show ()
1262- d :SetScript (" OnUpdate" , function (self , dt )
1263- self .elapsed = (self .elapsed or 0 ) + dt
1264- border ._dfDashElapsed = self .elapsed
1265- local offset = (self .elapsed * marchSpeed ) % DF_DASH_PATTERN
1279+ registerAnimTick (border , function (border , el )
1280+ border ._dfDashElapsed = el
1281+ local offset = (el * marchSpeed ) % DF_DASH_PATTERN
12661282 drawDashes (border , offset , border ._dfDashTh , border ._dfDashInset ,
12671283 border ._dfDashR , border ._dfDashG , border ._dfDashB , border ._dfDashA )
1268- end )
1284+ end , border . _dfDashElapsed or 0 )
12691285 else
12701286 -- Static: draw once, no driver (cheaper).
12711287 drawDashes (border , 0 , border ._dfDashTh , border ._dfDashInset , r , g , b , a )
0 commit comments