ECharts chart linkage

1. Section overview

When multiple charts are displayed together, sometimes the data between the charts are related. In this case, it is necessary to link the charts. For example, there is a map showing the sales of various regions in Henan Province (Eastern Henan, West Henan, South Henan, North Henan, and Central Henan), and there is also a bar chart showing the sales in each urban area. Then click on a certain area on the map. When there is a region, the bar chart also displays the sales data of each urban area under the jurisdiction of the region. In this section, we mainly introduce the use of EventBus in conjunction with the event binding API in the echarts instance in the current Vue project to implement the above chart linkage scenario.

2. Operation steps

2.1. Preparation

1. In the home page HomeView.vue, store the text link (chart linkage), click to jump to the corresponding routing page (chartLinkage), and configure the routing information in the router’s index.js. In the chartLinkage demo page, layout the left and right grids and prepare to store maps (showing regional sales) and bar charts (showing urban sales).

2. Create a new interface in the RAP interface management platform to provide regional sales data and urban sales data under each region.

3. Create CascadeMap.vue under src/components/linkage, introduce Echarts map to display regional sales data, and place it in the left area of the chartLinkage demo page. Reference code:

Note: In the previous section, we obtained the element through document.getElementById. This method actually has an obvious drawback (in the end, multiple components will be put together. If the element id set accidentally is repeated, it will appear. problem), so ref will be used to obtain DOM elements here and later.

<template>
    <div class="mainDiv">
        <div class="titleDiv">Regional sales amount</div>
        <div class="chartDiv" ref="chart"></div>
    </div>
</template>

<script>
import HenanAreaMap from '@/assets/maps/HenanArea.json'
import elementResizeDetectorMaker from 'element-resize-detector'

const erd = elementResizeDetectorMaker()

export default {
    data() {
        return {
            chartData: {},
        }
    },
    name: 'CascadeMap',
    mounted() {
        this.getChartData().then(() => {
            //Render the chart after the data request is received
            this.renderChart()
        })
    },
    beforeDestroy() {
        erd.uninstall(this.$refs.chart)
    },
    methods: {
        //Request data back
        async getChartData() {
            this.chartData = await this.$axios({ url: 'sales/cas' })
        },
        // Render chart
        renderChart() {
            //Use registerMap to register map json data
            this.$echarts.registerMap('HenanArea', HenanAreaMap)
            let myChart = this.$echarts.init(this.$refs.chart)
            let chartData = this.chartData
            var option = {
                tooltip: {},
                visualMap: {
                    min: 3000,
                    max: 30000,
                    left: 'left',
                    top: 'bottom',
                    text: ['High', 'Low'],
                    inRange: {
                        color: ['#e0ffff', '#5470C6'],
                    },
                    show: true,
                },
                series: [
                    {
                        name: 'Sales',
                        type: 'map',
                        map: 'HenanArea',
                        zoom: 1.2,
                        label: {
                            show: true,
                            fontSize: 14,
                            color: '#640000',
                            formatter: '{b}\\
{c}',
                        },
                        data: chartData.data.areaData,
                        select: {
                            itemStyle: {
                                //Set the color after clicking on the map
                                color: null,
                            },
                        },
                    },
                ],
            }
            // Display the chart using the configuration items and data just specified
            myChart.setOption(option)
            // Monitor the container where the chart is located and adapt when the size changes.
            erd.listenTo(this.$refs.chart, function () {
                myChart.resize()
            })
        },
    },
}
</script>

<style scoped>
.titleDiv {
    cursor: pointer;
}
</style>

4. Create CascadeBar.vue under src/components/linkage, introduce Echarts histogram to display urban sales data (all urban data is displayed by default), and place it in the right area of the chartLinkage demo page. Reference code:

<template>
    <div class="mainDiv">
        <div class="titleDiv">{<!-- -->{ title }}</div>
        <div class="chartDiv" ref="chart"></div>
    </div>
</template>

<script>
import elementResizeDetectorMaker from 'element-resize-detector'

const erd = elementResizeDetectorMaker()

export default {
    data() {
        return {
            chartData: {},
            title: 'Sales in cities in Henan Province',
        }
    },
    name: 'CascadeBar',
    mounted() {
        this.getChartData().then(() => {
            //Render the chart after the data request is received
            this.renderChart()
        })
    },
    beforeDestroy() {
        erd.uninstall(this.$refs.chart)
    },
    methods: {
        //Request data back
        async getChartData() {
            this.chartData = await this.$axios({ url: 'sales/cas' })
        },
        // Render chart
        renderChart() {
            let myChart = this.$echarts.init(this.$refs.chart)
            let chartData = this.chartData
            var option = {
                tooltip: {
                    show: true,
                    textStyle: {
                        color: '#000000',
                        fontStyle: 'normal',
                        fontSize: 14,
                        align: 'center',
                    },
                },
                legend: {
                    show: true,
                    data: ['Sales'],
                    icon: 'roundRect',
                    x: 'center',
                    y: 'bottom',
                    padding: [0, 0, 10, 0],
                    textStyle: {
                        fontStyle: 'normal',
                        fontSize: 12,
                    },
                },
                grid: {
                    top: '10%',
                    bottom: '10%',
                    left: '3%',
                    right: '3%',
                    containLabel: true,
                },
                xAxis: {
                    type: 'category',
                    data: chartData.data.allCityData.city,
                    axisTick: {
                        show: true,
                        alignWithLabel: true,
                    },
                    axisLabel: {
                        show: true,
                        interval: 0,
                    },
                    splitLine: {
                        show: false,
                    },
                },
                yAxis: {
                    type: 'value',
                    splitLine: {
                        // Separator settings
                        show: true,
                        lineStyle: {
                            color: ['#E5EAF3'],
                            width: 1,
                            type: 'dashed',
                        },
                    },
                },
                series: [
                    {
                        name: 'Sales',
                        type: 'bar',
                        data: chartData.data.allCityData.amount,
                        label: {
                            show: true,
                            position: 'top',
                            fontSize: 11,
                        },
                        itemStyle: {
                            // Set rounded corners clockwise: upper left, upper right, lower right, lower left
                            borderRadius: [6, 6, 0, 0],
                            // Set different colors according to the value
                            color: function (params) {
                                let colorList = ['#5470C6', '#91CC75', '#FAC858', '#EE6666']
                                let amount = params.data
                                if (amount >= 7000) {
                                    return colorList[0]
                                } else if (amount < 7000 & amp; & amp; amount >= 5000) {
                                    return colorList[1]
                                } else if (amount < 5000 & amp; & amp; amount >= 3000) {
                                    return colorList[2]
                                } else {
                                    return colorList[3]
                                }
                            },
                        },
                    },
                ],
            }
            // Display the chart using the configuration items and data just specified
            myChart.setOption(option)
            // Monitor the container where the chart is located and adapt when the size changes.
            erd.listenTo(this.$refs.chart, function () {
                myChart.resize()
            })
        },
    },
}
</script>

<style scoped>
.titleDiv {
    cursor: pointer;
}
</style>

2.2. Communication between components

1. The two chart components (map and bar chart) have been presented and the data has been rendered. If you want to link, you need to be able to communicate between charts. For example, clicking on the left area can trigger the right chart component method to update data. EventBus is used here for inter-component communication. First, EventBus is globally initialized in main.js, as shown below:

2. Next, EventBus will be used with event binding in the echarts instance to update the histogram data while clicking on the area.

2.3. Implement chart linkage

1. Send events to EventBus in the map component and pass parameters (quick information about the clicked area). Of course, since clicking on the chart area block triggers CascadeBar to display data in the corresponding area, click elsewhere (set here to click the div container of the title) to trigger CascadeBar to return to displaying data in all areas. Therefore, there are two places in the map component where events need to be sent to EventBus. Reference code:

Notes: 1. When binding the echarts click event, because this in echarts is not the vueComponent instance of vue, you need to first define a variable name _this=this to receive it. 2. When the mouse moves across the map, the highlight color is displayed by default. You can set the color to remain unchanged when the mouse moves into the area by binding the mouseover event.

<template>
    <div class="mainDiv">
        <div class="titleDiv" @click="cascadeHenan">Regional sales amount</div>
        <div class="chartDiv" ref="chart"></div>
    </div>
</template>

<script>
import HenanAreaMap from '@/assets/maps/HenanArea.json'
import elementResizeDetectorMaker from 'element-resize-detector'

const erd = elementResizeDetectorMaker()

export default {
    data() {
        return {
            chartData: {},
        }
    },
    name: 'CascadeMap',
    mounted() {
        this.getChartData().then(() => {
            //Render the chart after the data request is received
            this.renderChart()
        })
    },
    beforeDestroy() {
        //Remove element listener
        erd.uninstall(this.$refs.chart)
    },
    methods: {
        //Request data back
        async getChartData() {
            this.chartData = await this.$axios({ url: 'sales/cas' })
        },
        cascadeHenan() {
            this.$EventBus.$emit('casAreaChart', 'Henan Province')
        },
        // Render chart
        renderChart() {
            //Use registerMap to register map json data
            this.$echarts.registerMap('HenanArea', HenanAreaMap)
            let myChart = this.$echarts.init(this.$refs.chart)
            let chartData = this.chartData
            var option = {
                tooltip: {},
                visualMap: {
                    min: 3000,
                    max: 30000,
                    left: 'left',
                    top: 'bottom',
                    text: ['High', 'Low'],
                    inRange: {
                        color: ['#e0ffff', '#5470C6'],
                    },
                    show: true,
                },
                series: [
                    {
                        name: 'Sales',
                        type: 'map',
                        map: 'HenanArea',
                        zoom: 1.2,
                        label: {
                            show: true,
                            fontSize: 14,
                            color: '#640000',
                            formatter: '{b}\\
{c}',
                        },
                        data: chartData.data.areaData,
                        select: {
                            itemStyle: {
                                //Set the color after clicking on the map
                                color: null,
                            },
                        },
                    },
                ],
            }
            // Display the chart using the configuration items and data just specified
            myChart.setOption(option)
            //Set the click event to link the chart with the urban sales data
            // Because this in echarts is not the vueComponent instance of vue, you need to define a variable name to receive it.
            let _this = this
            myChart.on('click', function (params) {
                _this.$EventBus.$emit('casAreaChart', params.name)
            })
            // Set the color of the area where the mouse moves into to remain unchanged
            myChart.on('mouseover', function (params) {
                if (params.data.value != undefined) {
                    myChart.dispatchAction({
                        type: 'downplay',
                    })
                }
            })
            // Monitor the container where the chart is located and adapt when the size changes.
            erd.listenTo(this.$refs.chart, function () {
                myChart.resize()
            })
        },
    },
}
</script>

<style scoped>
.titleDiv {
    cursor: pointer;
}
</style>

2. Perform EventBus monitoring and receiving events in the histogram component, receive the area parameters passed by the map component, and then update the echarts configuration data. Of course, before the histogram component is destroyed, the EventBus listening event needs to be removed to avoid re-creating the listening event next time. Reference Code:

<template>
    <div class="mainDiv">
        <div class="titleDiv">{<!-- -->{ title }}</div>
        <div class="chartDiv" ref="chart"></div>
    </div>
</template>

<script>
import elementResizeDetectorMaker from 'element-resize-detector'

const erd = elementResizeDetectorMaker()

export default {
    data() {
        return {
            chartData: {},
            title: 'Sales in cities in Henan Province',
        }
    },
    name: 'CascadeBar',
    mounted() {
        this.getChartData().then(() => {
            //Render the chart after the data request is received
            this.renderChart()
        })
        //EventBus listens to receive events
        this.$EventBus.$on('casAreaChart', (data) => {
            this.title = data + 'Sales per city'
            let myChart = this.$echarts.getInstanceByDom(this.$refs.chart)
            if (data == 'Henan Province') {
                myChart.setOption({
                    xAxis: {
                        data: this.chartData.data.allCityData.city,
                    },
                    series: [
                        {
                            data: this.chartData.data.allCityData.amount,
                        },
                    ],
                })
            } else {
                myChart.setOption({
                    xAxis: {
                        data: this.chartData.data.areaCityData[data].city,
                    },
                    series: [
                        {
                            data: this.chartData.data.areaCityData[data].amount,
                        },
                    ],
                })
            }
        })
    },
    beforeDestroy() {
        //Remove EventBus listener
        this.$EventBus.$off('casAreaChart')
        //Remove element listener
        erd.uninstall(this.$refs.chart)
    },
    methods: {
        //Request data back
        async getChartData() {
            this.chartData = await this.$axios({ url: 'sales/cas' })
        },
        // Render chart
        renderChart() {
            let myChart = this.$echarts.init(this.$refs.chart)
            let chartData = this.chartData
            var option = {
                tooltip: {
                    show: true,
                    textStyle: {
                        color: '#000000',
                        fontStyle: 'normal',
                        fontSize: 14,
                        align: 'center',
                    },
                },
                legend: {
                    show: true,
                    data: ['Sales'],
                    icon: 'roundRect',
                    x: 'center',
                    y: 'bottom',
                    padding: [0, 0, 10, 0],
                    textStyle: {
                        fontStyle: 'normal',
                        fontSize: 12,
                    },
                },
                grid: {
                    top: '10%',
                    bottom: '10%',
                    left: '3%',
                    right: '3%',
                    containLabel: true,
                },
                xAxis: {
                    type: 'category',
                    data: chartData.data.allCityData.city,
                    axisTick: {
                        show: true,
                        alignWithLabel: true,
                    },
                    axisLabel: {
                        show: true,
                        interval: 0,
                    },
                    splitLine: {
                        show: false,
                    },
                },
                yAxis: {
                    type: 'value',
                    splitLine: {
                        // Separator settings
                        show: true,
                        lineStyle: {
                            color: ['#E5EAF3'],
                            width: 1,
                            type: 'dashed',
                        },
                    },
                },
                series: [
                    {
                        name: 'Sales',
                        type: 'bar',
                        data: chartData.data.allCityData.amount,
                        label: {
                            show: true,
                            position: 'top',
                            fontSize: 11,
                        },
                        itemStyle: {
                            // Set rounded corners clockwise: upper left, upper right, lower right, lower left
                            borderRadius: [6, 6, 0, 0],
                            // Set different colors according to the value
                            color: function (params) {
                                let colorList = ['#5470C6', '#91CC75', '#FAC858', '#EE6666']
                                let amount = params.data
                                if (amount >= 7000) {
                                    return colorList[0]
                                } else if (amount < 7000 & amp; & amp; amount >= 5000) {
                                    return colorList[1]
                                } else if (amount < 5000 & amp; & amp; amount >= 3000) {
                                    return colorList[2]
                                } else {
                                    return colorList[3]
                                }
                            },
                        },
                    },
                ],
            }
            // Display the chart using the configuration items and data just specified
            myChart.setOption(option)
            // Monitor the container where the chart is located and adapt when the size changes.
            erd.listenTo(this.$refs.chart, function () {
                myChart.resize()
            })
        },
    },
}
</script>

<style scoped>
.titleDiv {
    cursor: pointer;
}
</style>

3. At this point, the chart linkage processing is completed. Start the Vue project to check the linkage effect.

3. Section summary

When there is a relationship between the data presented by multiple ECharts charts, it is sometimes necessary to be able to link the charts. In this section, the communication between components is solved by using EventBus, and the linkage between charts is realized by cooperating with the click event binding in the echarts instance.