“This article has participated in the call for good writing activities, click to view: the back end, the big front end double track submission, 20,000 yuan prize pool waiting for you to challenge!”

Demand falls

Guest: I want to add a function to this chart – add a red line when clicked.

I (heart) : this red line is not the configuration of the markLine added to call it a day, easily.

Me (inner – product thinking) : It can’t be that simple! Do you know whether the line is horizontal or vertical? What is the purpose of drawing this line? Beep beep

Demand for communication

Since we are a chart presentation for data analysis, I give the following two solutions:

  • Plan 1: Draw a horizontal line (Y-axis) only once. It is generally used to set the early-warning line and average value, so that customers can check the fluctuation of data on this line.

  • Scheme 2: Draw a vertical line (x axis) and click to update it once. It is generally used to display the data of multiple line segments.

  • Option 3: Can the existing toolTips work

After communication with the customer, we decided to do plan 2

Final effect

The final effect is similar to the fixed tooltips. If you are interested, check out below.

Step 1: Draw a line

Tag: markLine

How it works: The Echarts markLine configuration item is used, and markLine is in a Series configuration

series: [
  {
      name: 'Email marketing'.type: 'line'.stack: 'total'.data: [120.132.101.134.90.230.210].markLine: {          / / line
        symbol: 'none'.// Label types at both ends of the line
        animation: false.// Whether to enable animation
        silent: true.// Whether the graph does not respond to and fire mouse events, the default is false, that is, respond to and fire mouse events.
        data: [{lineStyle: {// Line style
              type:"solid".color:"rgb(203 65 65)".width: 2
            },
            xAxis: 'on Tuesday'   // The x value of the line}]}}]Copy the code

The effect picture of the above configuration is as follows:

Step 2: Click event Handling

Tag: click, getZr

Because the requirement is to update the line dynamically, you need to bind click events to update the values in the configuration.

  • Pothole: The Echarts API uses the click event on a point on the other side of the line.

  • Solution: After some searching, use getZr() to solve the problem.

This method is used to get the pixels at click time. The containPixel method then converts pixels into coordinate system data, so you can click anywhere (including the shaded areas).

Get the axis index code as follows:

chart.getZr().on('click'.async (params) => {
  const pointInPixel = [params.offsetX, params.offsetY]
  if (chart.containPixel('grid', pointInPixel)) {
    let xindex = chart.convertFromPixel({ seriesIndex: 0 }, pointInPixel)[0]
    // xindex is data index
    // todo...}})Copy the code

Complete update of the marking code

const xData = ['Monday'.'on Tuesday'.'on Wednesday'.'on Thursday.'on Friday'.'on Saturday.'Sunday']
chart.getZr().on('click'.async (params) => {
  const pointInPixel = [params.offsetX, params.offsetY]
  if (chart.containPixel('grid', pointInPixel)) {
    const xindex = chart.convertFromPixel({ seriesIndex: 0 }, pointInPixel)[0]
    const chartOption = chart.getOption();         // Get all the current configurations of the chart
    const setSelectSeries = chartOption.series[0]; // Get the graph series configuration
    let markLineArr = setSelectSeries.markLine.data ? setSelectSeries.markLine.data : []

    if (markLineArr.length > 0) {
      // Delete the previous line according to markType
      markLineArr = markLineArr.filter((item) = >item.markType ! = ='select')
    }

    markLineArr.push({
      markType: 'select'.// Use custom markType tags
      silent:false.lineStyle: {type:"solid".color:"rgb(203 65 65)".width: 2
      },
      xAxis: xData[xindex].toString()  // We want to use a string, or an index for a numeric type
    })

    setSelectSeries.markLine.data = markLineArr
    chart.setOption({ series: chartOption.series })
  }
})
Copy the code

The above effects are as follows:

Step 3: Draw the data layer

Tag: label

How it works: Each markLine has a label, and we use the label attribute for personalization

Our goal is to create a middle data layer, so the label must be in the middle

  • Pothole 1: But according to the documentation on the official website, onlyposition: middleYou can center the label, but the text is displayed on the side, and there are no other attributes in the document markLine configuration that can adjust the label to a positive position.

  • Solution 1: After a search, found that someone usedrotateAttribute: markPoint attribute: markPoint attribute: markPoint attribute: markPoint attribute: markPointThe configuration can then be looked up in configuration items with similar functions

After the text is changed to a positive orientation, all it needs is an offset. The offset attribute is found in the markPoint configuration, so the prototype is probably out

Step 4: Data layer style processing

Tag: rich

Principle: you can use various attributes to adjust to the style of the label, including the use of the rich to show the value of the modified to} {| show a value, then in a style that is configured in the rich, rich in concrete can change the style of the reference here.

Configuration is as follows

label: {
    position:'middle'.// Set the position of the label
    formatter: labelStr,                       / / content
    rotate: 0.// The content rotation Angle
    offset: [10.0].// Content offset
    backgroundColor: 'rgba (71, 71, 71, 0.8)'.// Background color
    align: 'left'.// Content versus its way
    color: 'rgba(255, 255, 255, 1)'.padding: 10.borderRadius: 4.borderWidth: 1.rich: {                                    // Rich text style adjustment
      a: {
        fontSize: 14.padding: [10.0.0.0]},b: {
        fontSize: 14,}}}Copy the code

The effect is as follows:

Step 5: Data layer location processing

Tag: offset

Offset: [10, 0] : where the 0th bit of the array is the offset in the x direction and the first bit of the array is the offset in the y direction

  • Purpose: The data layer achieves centralization
  • Problem 1: Center — The height of the data is determined by how many line segments (the length of the series array)
  • Problem 2: If the width of the data layer exceeds the width of the entire container when clicked (part of the data layer will be blocked), it should be displayed on the left

The problem effect is as follows:

The offset value of the data layer is dynamically calculated according to the length of the series array.

const seriesLen = chartOption.series.length; 
const countOffset = [10, seriesLen * 16] // 16 is the height of the text
Copy the code

Figure out the width of the entire data layer according to the maximum length of the text, and then set the corresponding negative offset.

let strLen = 0;    // Maximum length of data
chartOption.series.forEach((item, idx) = > {
  let valueLen = `${item.name}:${item.data[xindex]}`.length;
  if (valueLen > strLen) {
    strLen = valueLen
  }
})

const seriesLen = chartOption.series.length                          // series array length
const chartWidth = chart.getWidth()                                  // Current chart width
const labelWidth = strLen * 12                                       // label width, 12 is the text width
const isLeft = (chartWidth - params.offsetX) < labelWidth ? true : false  // Determine if the data layer is on the left according to the width of the label
const countOffset = [isLeft ? -labelWidth - 30 : 10, seriesLen * 16] // 30 is the gap value, which can be tried by yourself
Copy the code

Key Configuration Items

label: {
    width: labelWidth,
    offset: countOffset
}
Copy the code

The effect is shown below

Final configuration items and code

<script>
option = {
  title: {
      text: 'Line chart stack'
  },
  tooltip: {
      trigger: 'axis'
  },
  legend: {
      data: ['Email marketing'.'Affiliate advertising'.'Video advertising'.'Direct access'.'Search engines']},grid: {
      left: '3%'.right: '4%'.bottom: '3%'.containLabel: true
  },
  toolbox: {
      feature: {
          saveAsImage: {}}}.xAxis: {
      type: 'category'.boundaryGap: false.data: ['Monday'.'on Tuesday'.'on Wednesday'.'on Thursday.'on Friday'.'on Saturday.'Sunday']},yAxis: {
      type: 'value'
  },
  series: [{name: 'Email marketing'.type: 'line'.stack: 'total'.data: [120.132.101.134.90.230.210].markLine: {
            symbol: 'none'.// Label types at both ends of the line
            animation: false.// Whether to enable animation
            silent: true.// Whether the graph does not respond to and fire mouse events, the default is false, that is, respond to and fire mouse events.}}, {name: 'Affiliate advertising'.type: 'line'.stack: 'total'.data: [220.182.191.234.290.330.310] {},name: 'Video advertising'.type: 'line'.stack: 'total'.data: [150.232.201.154.190.330.410] {},name: 'Direct access'.type: 'line'.stack: 'total'.data: [320.332.301.334.390.330.320] {},name: 'Search engines'.type: 'line'.stack: 'total'.data: [820.932.901.934.1290.1330.1320]]}};let chart = echarts.init(document.getElementById('chart'))
chart.setOption(option)

const xData = ['Monday'.'on Tuesday'.'on Wednesday'.'on Thursday.'on Friday'.'on Saturday.'Sunday']
chart.getZr().on('click'.async (params) => {
  const pointInPixel = [params.offsetX, params.offsetY]
  if (chart.containPixel('grid', pointInPixel)) {
    const xindex = chart.convertFromPixel({ seriesIndex: 0 }, pointInPixel)[0]
    const chartOption = chart.getOption();         // Get all the current configurations of the chart
    const setSelectSeries = chartOption.series[0]; // Get the graph series configuration
    let markLineArr = setSelectSeries.markLine.data ? setSelectSeries.markLine.data : []

    if (markLineArr.length > 0) {
      // Delete the previous line according to markType
      markLineArr = markLineArr.filter((item) = >item.markType ! = ='select')}const xVal = xData[xindex].toString()
    let labelStr = `{b|${xVal}}\n`
    let strLen = 0;
    chartOption.series.forEach((item, idx) = > {
      let valueLen = `${item.name}:${item.data[xindex]}`.length;
      if (valueLen > strLen) {
        strLen = valueLen
      }
      labelStr += `{a|${item.name}:${item.data[xindex]}${idx + 1 === chartOption.series.length ? ' ' : '\n'}} `
    })

    const seriesLen = chartOption.series.length                          // series array length
    const chartWidth = chart.getWidth()                                  // Current chart width
    const labelWidth = strLen * 12                                       / / label width
    const isLeft = (chartWidth - params.offsetX) < labelWidth ? true : false  // Determine if the data layer is on the left according to the width of the label
    const countOffset = [isLeft ? -labelWidth - 30 : 10, seriesLen * 16] // 30 is the gap value, which can be tried by yourself

    markLineArr.push({
      markType: 'select'.// Use custom markType tags
      silent:false.lineStyle: {type:"solid".color:"rgb(203 65 65)".width: 2
      },
      label: {
        width: labelWidth,
        position:'middle'.formatter: labelStr,
        rotate: 0.offset: countOffset, 
        backgroundColor: 'rgba (71, 71, 71, 0.8)'.align: 'left'.color: 'rgba(255, 255, 255, 1)'.padding: 10.borderRadius: 4.borderWidth: 1.rich: { // Adjust the style
          a: {
            fontSize: 14.padding: [10.0.0.0]},b: {
            fontSize: 14,}}},xAxis: xVal  // We want to use a string, or an index for a numeric type
    })

    setSelectSeries.markLine.data = markLineArr
    chart.setOption({ series: chartOption.series })
  }
})
</script>
Copy the code

conclusion

This article is short on technical points, but I would like to share with front-end engineers how to handle requirements and solve Echarts problems:

For a new requirement:

  1. First according to the company/department business analysis, the mind probably have the appearance after the realization
  2. Check whether existing features meet requirements
  3. Communicate with customers!!

Echarts problem

  1. Check the configuration items in the official documents
  2. You can view similar configuration items if the configuration is not in the document

The next time you see a need, don’t just jump into it

Thank you for watching, the code involved in the article, there is a better way to implement you can also discuss in the comments section ~