@@ -55,10 +55,11 @@ import {
5555 hideOverlap , shiftLayoutOnXY ,
5656 restoreIgnore ,
5757 LabelLayoutWithGeometry ,
58- newLabelLayoutWithGeometry ,
58+ newLabelLayoutWithGeometry
5959} from './labelLayoutHelper' ;
6060import { labelInner , animateLabelValue } from './labelStyle' ;
6161import { normalizeRadian } from 'zrender/src/contain/util' ;
62+ import { throttle , ThrottleController } from '../util/throttle' ;
6263
6364interface LabelDesc {
6465 label : ZRText
@@ -194,10 +195,129 @@ function extendWithKeys(target: Dictionary<any>, source: Dictionary<any>, keys:
194195
195196const LABEL_LAYOUT_PROPS = [ 'x' , 'y' , 'rotation' ] ;
196197
198+ /**
199+ * Emphasis manager for handling label emphasis state changes
200+ */
201+ class EmphasisManager {
202+ // eslint-disable-next-line no-undef
203+ private currentEmphasisLabels : Set < Element > = new Set ( ) ;
204+ private labelsNeedsHideOverlap : LabelLayoutWithGeometry [ ] = [ ] ;
205+ // eslint-disable-next-line no-undef
206+ private originalStates : Map < Element , boolean > = new Map ( ) ;
207+
208+ setLabelsNeedsHideOverlap ( labels : LabelLayoutWithGeometry [ ] ) : void {
209+ this . clear ( ) ;
210+ if ( labels . length === 0 ) {
211+ return ;
212+ }
213+
214+ this . labelsNeedsHideOverlap = labels ;
215+
216+ // Record original ignore states only when needed
217+ labels . forEach ( item => {
218+ this . originalStates . set ( item . label , item . label . ignore ) ;
219+ if ( item . labelLine ) {
220+ this . originalStates . set ( item . labelLine , item . labelLine . ignore ) ;
221+ }
222+ } ) ;
223+ }
224+
225+ handleEmphasisChange ( targetLabel : Element , isEnteringEmphasis : boolean ) : void {
226+ // Early return if no labels need hideOverlap processing
227+ if ( this . labelsNeedsHideOverlap . length === 0 ) {
228+ return ;
229+ }
230+
231+ if ( isEnteringEmphasis ) {
232+ this . currentEmphasisLabels . add ( targetLabel ) ;
233+ }
234+ else {
235+ this . currentEmphasisLabels . delete ( targetLabel ) ;
236+ }
237+
238+ if ( this . currentEmphasisLabels . size === 0 ) {
239+ // No emphasis labels, restore original state
240+ this . restoreOriginalState ( ) ;
241+ }
242+ else {
243+ // Re-sort with emphasis labels first and call hideOverlap
244+ this . reorderAndHideOverlap ( ) ;
245+ }
246+
247+ // Note: api.updateLabelLayout() will be called externally
248+ }
249+
250+ private reorderAndHideOverlap = throttle ( ( ) => {
251+ if ( this . labelsNeedsHideOverlap . length === 0 ) {
252+ return ;
253+ }
254+
255+ // Create a copy for reordering
256+ const reorderedLabels = [ ...this . labelsNeedsHideOverlap ] ;
257+
258+ // Sort: emphasis labels first, then by original priority
259+ reorderedLabels . sort ( ( a , b ) => {
260+ const aIsEmphasis = this . currentEmphasisLabels . has ( a . label ) ? 1 : 0 ;
261+ const bIsEmphasis = this . currentEmphasisLabels . has ( b . label ) ? 1 : 0 ;
262+
263+ // Emphasis labels come first
264+ if ( aIsEmphasis !== bIsEmphasis ) {
265+ return bIsEmphasis - aIsEmphasis ;
266+ }
267+
268+ // Then by original priority
269+ return ( ( b . suggestIgnore ? 1 : 0 ) - ( a . suggestIgnore ? 1 : 0 ) )
270+ || ( b . priority - a . priority ) ;
271+ } ) ;
272+
273+ // First restore all to show state
274+ reorderedLabels . forEach ( item => {
275+ item . label . ignore = false ;
276+ const emphasisState = item . label . ensureState ( 'emphasis' ) ;
277+ emphasisState . ignore = false ;
278+
279+ if ( item . labelLine ) {
280+ item . labelLine . ignore = false ;
281+ const lineEmphasisState = item . labelLine . ensureState ( 'emphasis' ) ;
282+ lineEmphasisState . ignore = false ;
283+ }
284+ } ) ;
285+
286+ // Call hideOverlap with isOrdered = true
287+ hideOverlap ( reorderedLabels , true ) ;
288+ } , 16 , true ) ;
289+
290+ private restoreOriginalState = throttle ( ( ) => {
291+ this . labelsNeedsHideOverlap . forEach ( item => {
292+ const originalIgnore = this . originalStates . get ( item . label ) ?? false ;
293+ item . label . ignore = originalIgnore ;
294+
295+ // For emphasis state, use the original hideOverlap logic
296+ const emphasisState = item . label . ensureState ( 'emphasis' ) ;
297+ emphasisState . ignore = originalIgnore ;
298+
299+ if ( item . labelLine ) {
300+ const originalLineIgnore = this . originalStates . get ( item . labelLine ) ?? false ;
301+ item . labelLine . ignore = originalLineIgnore ;
302+
303+ const lineEmphasisState = item . labelLine . ensureState ( 'emphasis' ) ;
304+ lineEmphasisState . ignore = originalLineIgnore ;
305+ }
306+ } ) ;
307+ } , 16 , true ) ;
308+
309+ clear ( ) : void {
310+ this . currentEmphasisLabels . clear ( ) ;
311+ this . labelsNeedsHideOverlap = [ ] ;
312+ this . originalStates . clear ( ) ;
313+ }
314+ }
315+
197316class LabelManager {
198317
199318 private _labelList : LabelDesc [ ] = [ ] ;
200319 private _chartViewList : ChartView [ ] = [ ] ;
320+ private _emphasisManager : EmphasisManager = new EmphasisManager ( ) ;
201321
202322 constructor ( ) { }
203323
@@ -323,6 +443,32 @@ class LabelManager {
323443 // Can only attach the text on the element with dataIndex
324444 if ( textEl && ! ( textEl as ECElement ) . disableLabelLayout ) {
325445 this . _addLabel ( ecData . dataIndex , ecData . dataType , seriesModel , textEl , layoutOption ) ;
446+ // Add emphasis state change listener for hideOverlap labels
447+ const resolvedLayoutOption = isFunction ( layoutOption ) ? null : layoutOption ;
448+ if ( resolvedLayoutOption && resolvedLayoutOption . hideOverlap ) {
449+ const hostEl = child as ECElement ;
450+ const originalOnHoverStateChange = hostEl . onHoverStateChange ;
451+ const labelManager = this ;
452+
453+ hostEl . onHoverStateChange = function ( toState : string ) {
454+ // Call original handler first
455+ if ( originalOnHoverStateChange ) {
456+ originalOnHoverStateChange . call ( this , toState ) ;
457+ }
458+
459+ // Handle emphasis state change for hideOverlap labels
460+ if ( toState === 'emphasis' || toState === 'normal' ) {
461+ // Find the label element - could be textEl or child itself
462+ const labelElement = textEl || this ;
463+
464+ // Use EmphasisManager to handle the state change
465+ labelManager . _emphasisManager . handleEmphasisChange (
466+ labelElement ,
467+ toState === 'emphasis'
468+ ) ;
469+ }
470+ } ;
471+ }
326472 }
327473 } ) ;
328474 }
@@ -466,6 +612,7 @@ class LabelManager {
466612
467613 restoreIgnore ( labelsNeedsHideOverlap ) ;
468614 hideOverlap ( labelsNeedsHideOverlap ) ;
615+ this . _emphasisManager . setLabelsNeedsHideOverlap ( labelsNeedsHideOverlap ) ;
469616 }
470617
471618 /**
0 commit comments