echarts
echarts copied to clipboard
[Feature] Line style for varied segments and connectNulls
What problem does this feature solve?
Current API lacks the ability to control line style for varied segments. For example, if we want to make different styles for a line series, we have to mock it using many series, which is not an elegant way to do.
What does the proposed API look like?
The new option should give the developer the control over line styles for different line segments. The API could be:
{
data: [{
value: 10,
lineStyle: {...}, // controls the line style after this data
areaStyle: {...}
}]
}
or something similar to visualMap.pieces:
{
linePieces: [{
index: [0, 2, 4], // the index of line segments
lineStyle: {...},
areaStyle: {...}
}]
}
or callback rules:
{
linePieces: [{
rule: function(params) { return params.dataIndex % 4 === 1; },
lineStyle: {...},
areaStyle: {...}
}]
}
We should also provide a way to set the lineStyle for the lines from connectNulls. The API may be
{
nullLineStyle: {...},
nullAreaStyle: {...}
}
or
{
connectNullStyle: {
lineStyle: {...},
areaStyle: {...}
}
}
or
{
linePieces: [{
rule: function(params) { return params.isConnectNulls },
lineStyle: {...},
areaStyle: {...}
}]
}
This issue is a task for OSPP so it will probably be fixed by a student from the program. Discussion about the design and your own requirement is always welcomed.
See also: #14239 #10233
What about api like this? This is my initial idea. @Ovilia 😊
series: [
{
data: [150, 230, 224, 218, 135, 147, 260],
type: 'line',
// itemStyle and areastyle is also like this
lineStyle:{
type: "dotted", // global style
styles:{ // part style
type:'solid',
scope:[0,1],
},
}
}
]
sorry ,styles should be an array 😅
@pe-2 Thanks for your suggestion. Although we do not have something like this for other options, theoretically speeking, this could also be a solution. I would suggest comparing each of these solutions and getting to a conclusion which one is the best. The thinking behind the choice of API design is the most important thing for this task.
P.S. We don't have any application on this task yet. Please make sure you submit the application on time.
Thanks!
You’re welcome, It’s my pleasure. I will submit the application after I complete it. 😁
We should also provide a way to set the lineStyle for the lines from
connectNulls. The API may be{ nullLineStyle: {...}, nullAreaStyle: {...} }or
{ connectNullStyle: { lineStyle: {...}, areaStyle: {...} } }
If we use these two APIs for setting lineStyle for null item, how could we set lineStyle for each null item individually?
@Ovilia I'd like to add following properties to option in LineSeries.ts#L72 for implementing the second proposal:
export interface LinePieceData {
index: number
leftValue: number
rightValue: number
lineStyle: LineStyleOption
isConnectNulls: boolean
}
export interface LinePiecesOption {
indexes?: number[]
rules: (piece: LinePieceData) => boolean
lineStyle?: LineStyleOption
}
export interface LineSeriesOption {
// ...
linePieces?: LinePiecesOption[]
}
The new API can be call by providing the linePieces option as you proposed:
or something similar to visualMap.pieces:
{ linePieces: [{ index: [0, 2, 4], // the index of line segments lineStyle: {...}, areaStyle: {...} }] }or callback rules:
{ linePieces: [{ rule: function(params) { return params.dataIndex % 4 === 1; }, lineStyle: {...}, areaStyle: {...} }] }
When using property rules to indicate which pieces should this style applied to, the callback function will receive the following arguments as a piece object:
export interface LinePieceData {
// index of the line piece
index: number
// left endpoint value of the line piece
leftValue: number
// right endpoint value of the line piece
rightValue: number
// line style will be applied to the line piece
lineStyle: LineStyleOption
// indicates if the piece is used to connect null data
isConnectNulls: boolean
}
What do you think of this implement?
@zz5840 Thanks for the update. I want to clearify that if linePieces.index is the index of the data? I'm not sure what does index: [0, 2, 4] mean? Does this mean the lines between (data[0], data[1]), (data[2], data[3]), (data[3], data[4]) appy this style?
We should better use names like startValue and endValue rather than leftValue and rightValue because if the axis is inversed, the order of data is not the same as in the chart.
areaStyle should also be provided in the API.
@Ovilia Thanks for reply.
I'd rather make linePieces.index be the index of the line segment, which is showed in the image below. The red number is line index and the blue is data index.

Line index will be different from data index only if a null data exist. So if we use line index, all index will be valid, and things like index pointing to a null data will not happen.
But there still exist disadvantage in line index. For example, it's hard to control the style of data segment (eg. to set the style for all data whose x-axis data is small than a certain value) when using dynamic data and number of nulls is unknown.
So which index should we provide, or should we provide both?
If the index means the ordinal number of the lines, then you must be careful because when the data is dense enough with line series, the data item (empty circles) is not always visible. Do you wish to have
or
?
Also, I feel it strange to exclude null data when making this index. It would mean the nth non-null line. I'm not sure if this is helpful for developers. Can you think of an example when this way is more convenient?
it's hard to control the style of data segment (eg. to set the style for all data whose x-axis data is small than a certain value) when using dynamic data and number of nulls is unknown.
This is a common situation so I think we shouldn't exclude null data in the index unless we have some solid reasons.
Also, I feel it strange to exclude null data when making this index. It would mean the nth non-null line. I'm not sure if this is helpful for developers. Can you think of an example when this way is more convenient?
I didn't mean to exclude null data, I mean this is what will happen if we use the index of the left endpoint as the index of line segment. But as you say, it's very common to set the style for all data whose x-axis data is small than a certain value, so we must accept a inconsecutive index if we use choose this solution.
Also, if we choose this solution, only the following image will show the correct index.

@zz5840 Thanks for the clarification. Just a few updates on the API design:
export interface LineSeriesOption {
lineSegments?: LineSegmentsOption[],
nullItemStyle?: {...}, // item style for null data
nullLineStyle?: {...},
nullAreaStyle?: {...},
// ...
}
export interface LineSegmentsOption {
dataIndexRange: number[][]
includeEndItem: boolean = true
itemStyle?: ItemStyleOption
lineStyle?: LineStyleOption
areaStyle?: AreaStyleOption
}
- I don't think we should provide
isConnectNullsinLinePieceData(or at least you can make an PR without this first and later add this feature when you have time). For now,isConnectNullsis a series-level option and it doesn't seem very necessary to provide a segment-level option to me. - Please note that the type of data values of line series is not
numberonly. It could benumbers like123, orstrings like'123'or arrays like['2020-10-10 13:00:00', 123]. UseLineDataValueif you are referencing to input data types. dataIndexis a better name thanindexbecause the latter one can also mean series index or others.dataIndexRangeis an array containing an array of length two. It works likeArray.slice, where the first number means the starting index and the second number means the length. So[[2, 1], [7, 2]]means data with index 2, 7, and 8.- For
itemStyle, ifincludeEndIndexis set to be true, the data at the end of the segment will be applied theitemStyle. For example, ifdataIndexRangeis[[2, 1], [7, 2]]andincludeEndIndexis false, then data item 2, 7, and 8 will apply theitemStyle. IfincludeEndIndexis true, then data item 2, 3, 7, 8 and 9 will apply theitemStyle. - I removed the callback form in the API because it seems to complicate the situation. Developers have such situations can loop through the data and generate an array of
lineSegmentsto implement the requirement. So I think we can go without it and see if it's necessary in the future. - I added
nullItemStyle,nullLineStyle, andnullAreaStylein the API. When data contains null, we should automatically create a segment from the null data and use corresponding styles.
Maybe how PlottableJs handles property updates can be of help for you to decide which API is best. For each property that you can set in PlottableJs, you have the choice of either give the value statically or give an accessor function:
https://github.com/palantir/plottable/blob/develop/src/core/interfaces.ts#L13-L19
/**
* Accesses a specific datum property. Users supply Accessors to their
* plots' .x, .y, .attr, etc. functions.
*/
export interface IAccessor<T> {
(datum: any, index: number, dataset: Dataset): T; // Inputs should be replaced by charts specific dataset data structures
}
Then when creating a Plot in Plottable:
const stackedBarPlot = new Plottable.Plots.StackedBar()
.datasets(datasets)
.attr('fill', (d: any, I: number, dataset: Dataset) => dataset.metadata().color);
How it would look like in echarts:
export interface LineSeriesOption .... {
lineStyle: LineStyleOption | IAccessor<LineStyleOption>;
...
}
Then in user-land:
const baseLineStyle = {...};
...
lineStyle: (datum: any, index: number, dataset: Dataset) => {
return {... baseLineStyle, dashOffset: index > 10 ? 0 : 4 };
}
...
That way a user of the library can very easily create its own logic, that even reusable because it's a simple function.
What about api like this? This is my initial idea. @Ovilia 😊
series: [ { data: [150, 230, 224, 218, 135, 147, 260], type: 'line', // itemStyle and areastyle is also like this lineStyle:{ type: "dotted", // global style styles:{ // part style type:'solid', scope:[0,1], }, } } ]
there is no any styles in lineStyle , and did you submit application ?
Will this feature be implemented in the next version?
A workaround with renderItem - Demo Code

I really need lineStyle!!!
{ data: [{ lineStyle: {...}, }] }
Is there a way to simply make lineStyle.color and lineStyle.type accept a function. itemStyle.color already accepts a function and can give you the ability to change color based on dataPoint and index.
您好,最终能实现到可以根据第三维信息,通过visualmap获取映射对应的颜色;然后两点之间根据首尾节点的颜色做渐变的效果吗? 或者是输入和数据等长的信息,描述每个节点的颜色,然后折线段根据2个节点的颜色做渐变呢?
thank you, i'll give it a try