Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[5.0][FEATURE] Label enhancement and state transition #12911

Merged
merged 90 commits into from
Jul 6, 2020
Merged

Conversation

pissang
Copy link
Contributor

@pissang pissang commented Jul 5, 2020

[5.0][FEATURE] Label enhancement and state transition

Brief Information

This pull request introduces following main features:

  1. Better displayed labels.
  2. Introduce labelLayout for a more flexible label layout.
  3. Support labelLine on all series.
  4. State transition.

State transition feature is developed in this branch because it's related to label on many codes.

1. Better displayed labels.

It includes several strategies to make sure the labels are displayed nicely and neatly by default in most cases.

1) Smarter color strategy

It has better strategy to calculate the fill and stroke color based on the color of underlying elements, background color and label position.

Text inside the rect

Text outside

2) Smarter label placement in Pie.

Related issues: #6050

An extreme case from Gallery

Case from https://gallery.echartsjs.com/editor.html?c=xSkDzAue4G

Before:

After:

GIF to show how labels will be hidden or truncated when it's overlapped or overflow.

3) Overflow

Add overflow configuration. Which can be truncate, break, breakAll.

label: {
    overflow?: 'truncate' | 'break' | 'breakAll'
}

Truncate

overflow-truncate

Break

overflow-break

The difference between 'break' and 'breakAll' is breakAll will break the word.

2. Introduce labelLayout for a more flexible overall label layout.

labelLayout provides more abilities for developers to tweak the layout of the labels after it's originally positioned by the chart itself.

It's available for all series.

Interface Defninition

{
    /**
     * Overall label layout option in label layout stage.
     * All series can use this config.
     */
    labelLayout?: LabelLayoutOption | LabelLayoutOptionCallback
}

interface LabelLayoutOption {
    /**
     * If move the overlapped label. If label is still overlapped after moved.
     * It will determine if to hide this label with `hideOverlap` policy.
     *
     * shift-x/y will keep the order on x/y
     * shuffle-x/y will move the label around the original position randomly.
     */
    moveOverlap?: 'shift-x'
        | 'shift-y'
        | 'shuffle-x'
        | 'shuffle-y'
    /**
     * If hide the overlapped label. It will be handled after move.
     * @default false
     */
    hideOverlap?: boolean
    /**
     * If label is draggable.
     * @default false
     */
    draggable?: boolean
    /**
     * Can be absolute px number or percent string.
     */
    x?: number | string
    y?: number | string
    /**
     * offset on x based on the original position.
     */
    dx?: number
    /**
     * offset on y based on the original position.
     */
    dy?: number
    /**
     * Rotate degree
     */
    rotate?: number
    align?: ZRTextAlign
    verticalAlign?: ZRTextVerticalAlign

    width?: number
    height?: number

    // Points of label line
    labelLinePoints?: number[][]
}

interface LabelLayoutOptionCallbackParams {
    dataIndex: number,
    dataType: string,
    seriesIndex: number,
    text: string
    align: ZRTextAlign
    verticalAlign: ZRTextVerticalAlign
    /**
     * Rect of related graphic element.
     */
    rect: RectLike
    /**
     * Rect of label.
     */
    labelRect: RectLike
    // The original points of label line. Available in pie/funnel
    labelLinePoints?: number[][]
    // x: number
    // y: number
}

type LabelLayoutOptionCallback = (params: LabelLayoutOptionCallbackParams) => LabelLayoutOption;

Use cases of labelLayout

1) Hide overlapped labels.

Usage
labelLayout: {
    hideOverlap: true
}

That's all.

📈 Line Chart

Related issue #8554

Before

After

📊 Bar

Related issue: #6514

Before

After

Graph

Overlapped labels are overlapped automatically when zooming out. The priority is based on the size of circle.

graph

2) Align labels on top.

align-top

{
    type: 'scatter',
    ...
    labelLayout: {
        // All labels are 20px to the top edge
        y: 20,
        align: 'center',
        // Shift on x axis sequently if labels overlapped.
        moveOverlap: 'shift-x',
        // Hide overlapped label
        hideOverlap: true
    },
    labelLine: {
        // All series supports labelLine now.
        show: true,
        // length2 defines the second segement length of labelLine.
        length2: 5
    }
}

3) Align labels on right.

align-right

{
    type: 'scatter',
    ...
    labelLayout: {
        x: 500,
        // Shift on y axis sequently if labels overlapped.
        moveOverlap: 'shift-y',
        // Hide overlapped label
        hideOverlap: true
    },
    labelLine: {
        show: true,
        length2: 5
    }
}

4) Adjust labelLine

If we wan't to move the labelLine under the text in pie. Like following:

labelLayout: function (params) {
    const isLeft = params.labelRect.x < chart.getWidth() / 2;
    const points = params.labelLinePoints;
    // Update the end point.
    points[2][0] = isLeft
        ? params.labelRect.x
        : params.labelRect.x + params.labelRect.width;

    return {
        labelLinePoints: points
    }
}

Based on this callback, we can tweak the label position in a few lines. For example, add a gap between the line end and label.

labelLayout: function (params) {
    const isLeft = params.labelRect.x < chart.getWidth() / 2;
    const points = params.labelLinePoints;
    const gap = 5;
    // Update the end point.
    points[2][0] = isLeft
        ? params.labelRect.x - gap
        : params.labelRect.x + params.labelRect.width + gap;
    return {
        labelLinePoints: points
    }
}

5) Draggable label.

The labels can be dragged to a different position if users don't satisify the original layout. And the labelLine will be recalculated automatically.

labelLayout: {
    draggable: true
}

3. Support labelLine on all series.

Related issues: #11534

labelLine is very useful for guiding element to related labels when labels are moved far away, like the case of the aligned labels above.

1) Auto calculated labelLine points.

The points of labelLine are calculated based on the position of the label and shape of the element. To make sure the display of labelLine is nice and neat.

2) Support limiting labelLine angle

3) Optimizing smooth algorithm

4. State transition

It provides smoooth state transition when the mouse entered / leaved the element.

Before:

map-state-no-transition

After:

map-state-transition

Interface:

stateAnimation?: {
    duration?: number
    easing?: AnimationEasing
    delay?: number
}

5. Other optimizations

Animation.

1) Pie animation optimization

Related issue: #12553

2) Bar animation optimization

Related issue: #12543

3) Use cubicInOut easing by default

Introduce dark mode.

The dark mode is inferenced from the background automatically by default. We can also specify it manually if we have an image background which echarts can't pick a color from.

option = {
    darkMode: true
}

As described above, the color of labels will be different in dark mode.

6. TODO

  1. More layouting algorithms, like space searching with simulated annealing.
  2. More labelLine calculation restrictions. Like fixed anchor point.
  3. Specify priority when hiding the overlapped labels.
  4. Not show labelLine automatically if label is overlapped with the corresponding element.
  5. Remember the position after dragged in the option. Remembered position can be relative or absolute.

7. PS

Added rich text support in SVG rendering mode.

pissang added 30 commits April 22, 2020 22:52
@echarts-bot
Copy link

echarts-bot bot commented Jul 5, 2020

Thanks for your contribution!
The community will review it ASAP. In the meanwhile, please checkout the coding standard and Wiki about How to make a pull request.

@pissang pissang merged commit 4c3b1b2 into next Jul 6, 2020
@echarts-bot
Copy link

echarts-bot bot commented Jul 6, 2020

Congratulations! Your PR has been merged. Thanks for your contribution! 👍

@zevero
Copy link

zevero commented Oct 2, 2020

The new label feature with hideOverlap is very cool!!!

But in version 5.0.0-alpha.2 my bar chart has a small problem. My tooltip appears only when hovering over the bar itself. It disappears when hovering on the label (which is placed in the center of the bar).

@pissang
Copy link
Contributor Author

pissang commented Oct 26, 2020

@zevero Hi, sorry for the late reply.
We have fixed this issue in the latest version.

@ducthiennguyen
Copy link

Thank you @pissang for this great enhancement. I am afraid that I cannot get the moveOverlap to work for bar chart. Is there any way that I can use this feature with bar chart? Thank you.

@fornasari12
Copy link

Hello, grate feature!!

Its suposed to work on GraphGL with forceAtlas2 too?

I tried but nothing happened!

@pissang
Copy link
Contributor Author

pissang commented Jan 21, 2021

graphGL is not supported yet

@lifront
Copy link

lifront commented Jan 21, 2021

5.0版本,设置labelLayout后,移动地图,label文字位置不动

@pissang
Copy link
Contributor Author

pissang commented Jan 21, 2021

@lifront 怎么设置的?

@lifront
Copy link

lifront commented Jan 21, 2021

@pissang 这是我的设置

option = {
    "dataset": {
		"source": [
			[
				"省份",
				"gdp"
			],
			[
				"上海",
				32679
			],
			[
				"云南",
				5350
			]
		]
	},
	"visualMap": {
		"type": "continuous",
		"itemHeight": 120,
		"dimension": 1,
		"min": 2582,
		"max": 94319,
		"left": 20,
		"bottom": 20,
		"showLabel": true,
		"calculable": true,
		"inRange": {
			"color": [
				"#b7d6f3",
				"#40a9ed",
				"#3598c1",
				"#215096"
			]
		},
		"show": true
	},
	"geo": {
		"scaleLimit": {
			"min": 1,
			"max": 2
		},
		"top": 120
	},
	"series": [
		{
			"type": "map",
			"map": "china",
			"roam": "move",
			"zoom": 1.2,
			"showLegendSymbol": false,
			"tooltip": {},
			"label": {
				"show": true
			},
			"labelLayout": {
				"hideOverlap": true
			},
			"itemStyle": {
				"borderColor": "lightblue",
				"areaColor": "#EBEDF0"
			},
			"emphasis": {},
			"selectedMode": false
		}
	]
};

@pissang
Copy link
Contributor Author

pissang commented Jan 21, 2021

@lifront 谢谢反馈,5.1.0 中修复该问题 #14091

@lifront
Copy link

lifront commented Jan 21, 2021

@pissang 已经确认是问题了吗,目前有没有替代方案

@pissang
Copy link
Contributor Author

pissang commented Jan 21, 2021

@lifront 只能暂时不在地图上使用 labelLayout 配置

@lifront
Copy link

lifront commented Jan 21, 2021

@pissang 好的,明白了,5.1.0大概啥时候上线

@wenrenyixuan
Copy link

@pissang graph 的label支持绑定点击事件了么?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants