preface

Following up on the article “Dry goods” using Vue + Echarts to create your own visual interface (part 1), today we focus on the use of markup to achieve the effect shown below.

The version number of Echarts used is v4.3. Version number of V-Charts is V1.19.0.

There are many ways to use tags. Today’s scenarios are: line chart, bar chart, line chart + bar chart.

Line chart symbol

In the figure above, at the inflection point of the broken line, some “dots” are replaced by small ICONS.

To achieve this, we need to sort out the original requirements:

  • Each tag represents an activity type.
  • Some activities occur at certain points in time, or over a period of time, and you need to indicate the type of activity on the date of the activity.
  • Use composite ICONS when more than one activity is happening on the same day, and when displaying the Tooltip, display information for each activity that day.
  • The tooltip layout displays the current date first, the activity icon and activity name in the middle, and the indicator name and value in the end.
  • No active dates, inflection points and tooltips display the original style as usual. So, the full effect would look something like this:

To achieve this effect, consider the following:

  • How to set the activity icon at the inflection point of the broken line in the form of “dot” through directional matching of dates.
  • How to set the size of the icon? It needs to be marked differently from normal inflection points.
  • How do I style the tooltip? You also need to be compatible with date styles that are not active.

B.

First of all, in order to do directional date matching, the data structure needs to be designed as follows:

data: [
    {
        id: '1, 1, 3, 2',
        date: '2019-10-10',
        name: 'test-name1, test-name2, test-name3, test-name4'},... ]Copy the code

The next core attribute: symbol is the key, it is actually the inflection point on the line.

Symbol supports the following symbol types: Circle, Rect, roundRect, triangle, Diamond, PIN, arrow, and None. The default is emptyCircle, which is a hollow circle.

It also supports the link format: ‘image: / / http://xxx.xxx.xxx/a/b.png. In addition, if you need a different graph for each data, you can set the callback function to the following format:

(value: Array|number, params: Object) => string
Copy the code

The first parameter value is the data value in data. The second parameter, params, is the other data item parameters.

These are just what we need. The above callback function can only be used in the latest version of V4.3, otherwise an error will be reported. This is why, at the outset, I emphasized the issue of Echarts versioning. The concrete implementation is as follows:

<ve-line ... :extend="chartExtend"></ve-line> ... // Mock contains the data structure dataList: [{id:'1, 1, 3, 2',
        date: '2019-10-10',
        name: 'test-name1, test-name2, test-name3, test-name4'},... { id:'1',
        date: '2019-10-17',
        name: 'test-name1'}],...setChartExtend () {
    this.chartExtend = {
        series: (v) => {
            Array.from(v).forEach((e, idx) => {
                e.symbol = (value, params) => {
                    return getSymbolIcon(params.name, dataList);
                };
                e.symbolSize = (value, params) => {
                    return getSymbolSize(params.name, dataList);
                };
            });
            returnv; },... }; }, getSymbolIcon (date, dataList) { const defaultSymbol ='circle';

    if(! dataList || dataList.length === 0) {returndefaultSymbol; Const dataItem = dataList. Find (item => item.date === = date); const dataItem = dataList. const iconUrl = getSymbolUrl(dataItem.id);return iconUrl ? iconUrl : defaultSymbol;
},

getSymbolSize (date, dataList) {
    if(! dataList || dataList.length === 0) {return4. Const dataItem = dataList. Find (item => item.date === = date); const dataItem = dataList.returndataItem ? 15:4; }, getSymbolUrl (id) {// We need to do an extra layer of preparation: we need to name the icon according to the id of the icon, and then upload it to our CDN. Symbol -icon-1. JPG, symbol-icon-2. JPG image://http://xxx.xxx.com/symbol-icon-1.jpg / / in case of multiple ids can add a composite icon to deal with, the id can be classified as 0}Copy the code

One last question, how do I restyle tooltip to make it compatible?

As mentioned earlier, the layout of tooltip is divided into three parts: the date, the tag information, and the specific value. So let’s use this to redraw the Tooltip.

Tooltip supports the formatter callback function, whose return value is of type Sting.

/ / callback function format (params: Object | Array, ticket: string, the callback: (ticket: string, HTML: string)) = > stringCopy the code

The date information can be obtained from params[0].axisValue.

The method of obtaining annotation information is similar to the method of obtaining ICONS above, except that the specific annotation type and name are displayed here.

Specific values can be obtained through marker, seriesName, value and other attributes in Params. The concrete implementation is as follows:

setChartExtend () {
    this.chartExtend = {
        series: (v) => {
            ...
        },
        tooltip: {
            formatter: (params) => {
                returngetTooltipResult(params, dataList); }}}; }, getTooltipResult (params, dataList) { const dateResult = params[0].axisValue; Const originalResultObj = getOriginalTooltipResult(params); const originalResultObj = getOriginalTooltipResult(Params);if(! dataList || dataList.length === 0) {return dateResult + originalResultObj.strResult;
    }

    const dataItem = dataList.find(item => item.date === date);

    if (dataItem) {
        return dateResult +  getSymbolResult(dataItem, originalResultObj.strResult);
    }

    return dateResult + originalResultObj.strResult;
},

getOriginalTooltipResult (params) {
    let result = ' '; Params. forEach((param, idx) => {// value will be of different types depending on seriesTypelet value = Object.prototype.toString.call(param.value) === '[object Array]' ? param.value[1] : param.value;

        const str = `${param.marker}${param.seriesName}: ${ value }<br>`;
        result += str;
    });

    return{ strResult: result }; }, getSymbolResult (dataItem, originalResult) {// Convert the dataItem ID to an array, Const dataIds = dataitem.id.split (const dataIds = dataitem.id.split (', ');
    const dataNames = dataItem.name.split(', '); Dataid.foreach ((id, idx) => {const iconUrl =... ; // Render icon style const STR = '<img SRC ="${iconUrl}" width="11" height="11" style="display: inline-block; margin-right: 4px; margin-left: -1px;">${dataNames[idx]}<br>`;

        result += str;
    });

    return result + originalResult;
}
Copy the code

Some of you might ask why the value returned by getOriginalTooltipResult has only one strResult in it.

It’s really just to make it easier to expand. For example, the date can be followed by the total of the values of the specific data below. Then you need to go through the params loop in the getOriginalTooltipResult method, calculate the total, and then style it to generate a strTotal.

getOriginalTooltipResult (params) {
    ...

    return {
        strTotal: strTotal,
        strResult: result
    };
}
Copy the code

In addition, in real business, there may be some line values that are percentages. We need to extend the getOriginalTooltipResult method by passing in an options object:

getOriginalTooltipResult = (params, options = { isLinePercent: false, isShowTotal: false{...}) }Copy the code

At this point, the rendering of the line chart marker is rendered perfectly.

Bar chart marker — markPoint

Embarrassingly, bar charts do not have a symbol attribute. Which means that the line chart above doesn’t work on the bar chart.

I had no choice but to go back to the files and keep looking. The markPoint attribute was finally discovered after a lot of experimentation. In the markPoint object, you can set the symbol, so that the previous set of work is not in vain. ! ?

Note that the default symbol for markPoint is ‘pin’, which is the icon of a bubble. In addition, to make markPoint’s tag appear, you must set its data property. We need to set the following properties in data:

data: [
    {
        symbol: '... 'Coord: [index, 0] // On the X-axis, mark symbolOffset: [0, 0] // Place marker on x axis},... ]Copy the code

The specific implementation code is as follows:

<ve-histogram :data="chartData" :extend="chartExtend"></ve-histogram> ... // Mock contains the data structure dataList: [{id:'1, 1, 3, 2',
        date: '2019-10-10',
        name: 'test-name1, test-name2, test-name3, test-name4'},... { id:'1',
        date: '2019-10-17',
        name: 'test-name1'}],...setChartExtend() { this.chartExtend = { series: (v) => { Array.from(v).forEach((e, idx) => { e.markPoint = { data: getMarkPointData(this.chartData.rows, dataList) }; }); },... }; }, getMarkPointData (rows, dataList) { const results = []; Rows. ForEach ((row, index) => {const dataItem = dataList. Find (item => item.date === row.date);if(dataItem) { results.push({ symbol: getSymbolUrl(dataItem.id), symbolSize: 15, coord: [index, 0], symbolOffset: [0, 0]}); }});returnresults; }, getSymbolUrl (id) {// We need to do an extra layer of preparation: we need to name the icon according to the id of the icon, and then upload it to our CDN. Symbol -icon-1. JPG, symbol-icon-2. JPG image://http://xxx.xxx.com/symbol-icon-1.jpg / / in case of multiple ids can add a composite icon to deal with, the id can be classified as 0}Copy the code

Since markPoint does not get data for dates when it sets data, it uses rows in chartData.

In the rows loop, if a match is made to that day and needs to be marked, the preset data structure is stored in the result array, and the data returned to markPoint is rendered. The effect is as follows:

The implementation of tooltip is universal, regardless of the type of diagram, so I won’t repeat it here.

In addition, sometimes you encounter the effect of having to deal with whether the bar graph is stacked or not. This affects the location of symbolOffset. For aesthetic purposes, move the item 50% to the right and center the symbol, symbolOffset: [‘50%’, 0].

Line chart + bar chart

Finally, a combination of line and bar chart structure, like the following:

From the point of view of code implementation, of course, we can label every qualified column and inflection point of the polyline. However, for the sake of aesthetics, we chose to mark only the inflection point of the broken line, and ignored the inflection point of the dotted line.

The original intention of this design is: the marking is just to remind people, and it is to tell the checker that some special events happened that day, and the data has changed significantly. So, the conclusion is: one sign a day is enough.

The specific implementation is actually very simple, just need to determine the type of series when rendering:

setChartExtend () {
    this.chartExtend = {
        series: (v) => {
            Array.from(v).forEach((e, idx) => {
                if (e.type === 'bar') {// Set the bar markPoint method... }if (e.type === 'line'// Set the symbol, symbolSize method... }}); },... }; }Copy the code

conclusion

There are many effects that Echarts can achieve, and this article deals with rendering “tags” among them. At the inflection point of the line chart, the matching process is done with symbol. In bar diagrams, since there is no direct symbol, markpoints are used instead, using the practice of locating markers on a dimension. The results are pretty good.

However, in the subsequent use, another embarrassing situation was found: in the line graph, when clicking on an item in the legend to hide its data, and then clicking on re-render, it was found that the custom icon of Symbol was not displayed.

I’ve been doing a lot of digging, but I still haven’t found anything useful. I suspect it is a rendering bug, so I made an issue to Echarts, hope it can be resolved. You are also welcome to discuss relevant questions in the comments section, thank you!

PS: Welcome to follow my public account “Chao Ge Front-end Small stack” to exchange more ideas and technologies.