vue monitor window.resize

1. Problem


I encountered a problem in the project today. When using the eCharts chart, when the window changes size, I need to re-render the chart. I encapsulated eCharts into a component and listened to window.onresize in the component, but the effect did not match the expectations. There are multiple eChart components on the page, but only the monitoring of the last component takes effect, the others do not take effect.

But this plug-in was taken directly from the previous project. The only difference is that the previous project introduced jQquary, and the monitoring uses the $ (window) .resize () method. This monitoring is no problem.

 

2. Analysis


window.onresize is directly bound to the onresize property of the window, there can only be one, that is to say, window.onresize can only be mounted once in the project, and only one of them can be mounted on window.onresize window.onresize will work, the front will be covered by the back, the same is true in routing, the back will cover the front, and the parent component will overwrite the child component.

The use of jQuery $ (window) .resize () can write multiple methods. Sometimes we may want to deal with more complex logic, which will have a greater impact on performance, so it is easier to cause the browser to die.

Do an anti-shake process, and delay the execution of the code by adding a timer, so that every time the window changes, we clear the event, and only when he stops, it will continue to execute. Although this method can solve the problem that resize is executed many times, it feels not perfect. For example, in some cases, we need to make some changes on the page immediately after the window changes, this method is not applicable

 

3. Solve


If there are multiple items on a page that need to monitor the window change, put it in the parent component, and then pass the value to the child component. The child component listens to the incoming value and then makes the corresponding operation.

code show as below:

1.vue component

<!--
  /**
     * @author: wendell_chen
     * @createDate: 2019/5/31
     * @weChat: fourteen_clever
     * **/
-->
<template>
  <div :id="`eChart${id}`" class="chartsWrap"></div>
</template>

<script>
  export default {
    name: "EChart",
    props: {
      option: {
        type: Object
      },
      id: {
        type: [Number, String]
      },
      resize: {
        type: Boolean
      }
    },
    data() {
      return {
        eChart: null,
        timer: null
      }
    },
    watch: {
      option: {
        handler(newValue, oldValue){
          if(this.eChart){
            this.eChart.setOption(this.option, {
              notMerge: true
            });
          }else {
            this.initChart();
          }
        },
        deep: true
      },
      resize(newValue, oldValue){
        this.eChart.clear();
        this.eChart.dispose();
        this.eChart = this.$echarts.init(document.getElementById(`eChart${this.id}`));
        this.eChart.setOption(this.option);
      }
    },
    mounted() {
      this.initChart();
      this.initResize();
    },
    beforeDestroy() {
      if(this.eChart){
        this.eChart.clear();
        this.eChart.dispose();
        this.eChart = null;
       }
       this.removeResize();
    },
    methods: {
      // 初始化
      initChart(){
        if(this.eChart){
          this.eChart.clear();
          this.eChart.setOption(this.option);
        }else {
          this.eChart = this.$echarts.init(document.getElementById(`eChart${this.id}`));
          this.eChart.setOption(this.option);
        }
      },

      // 注册resize方法
      initResize(){
        if(this.resize === undefined){
          window.addEventListener('resize', this.resizeFunction, false);
        }
      },

      // 移除resize方法
      removeResize(){
        if(this.resize === undefined){
          window.removeEventListener('resize', this.resizeFunction);
        }
      },

      // resize时需要执行的函数
      resizeFunction(){
        if(this.timer){
         clearTimeout(this.timer);
        }

      this.timer = setTimeout(() => {
        if(this.eChart && document.getElementById(`eChart${this.id}`)){
          this.eChart.clear();
          this.eChart.dispose();
          this.eChart = this.$echarts.init(document.getElementById(`eChart${this.id}`));
          this.eChart.setOption(this.option);
        }
      })
    }
  }
}
</script>

<style lang="scss">
  @import "./eChart";
</style>

2. Use components

<!--
  /**
     * @author: wen_dell
     * @createDate: 2019/12/11
     * @weChat: fourteen_clever
     * **/
-->
<template>
    <div id="home" class="home_wrap">
        <el-row :gutter="6" class="home_row">
            <el-col :span="4" class="home_col">
                <ul class="item_ul">
                    <li class="item_li">
                        <div class="item_li_content">
                            <p class="item_date">2017/7/17 星期三</p>
                            <p class="item_aqi">
                                <span>34</span> 良 AQI<span class="ml">20/cm³</span> 负氧离子
                            </p>
                            <p class="item_weather">
                                <i class="el-icon-sunny weather_img"></i>
                                <span class="item_weather_detail">
                                    26℃~34℃<br />东北风 3-4级
                                </span>
                            </p>
                        </div>
                    </li>
                    <li v-for="item in chartOptions.slice(0, 4)" :key="item.id" class="item_li">
                        <div class="item_li_content">
                            <e-chart :id="item.id" :option="item.option" :resize="item.resize"></e-chart>
                        </div>
                    </li>
                </ul>
            </el-col>
            <el-col :span="16" class="home_col home_col_center">
                <div class="home_col_map">
                    <h5 class="home_col_map_title">经开区精准扶贫看板</h5>
                    <img class="home_map" src="../../assets/image/home-map.png" alt="">

                    <area-info
                        v-model="checkedArea"
                        class="home_address"
                        @check="checkArea"
                    ></area-info>
                </div>
                <div class="home_col_center_bottom">
                    <div class="home_col_chart">
                        <e-chart :id="chartOptionBottom.id" :option="chartOptionBottom.option" :resize="chartOptionBottom.resize"></e-chart>
                    </div>
                </div>
            </el-col>
            <el-col :span="4" class="home_col">
                <ul class="item_ul">
                    <li class="item_li">
                        <div class="item_li_content">
                            <div class="item_li_right_label">
                                设备总数
                                <span>500</span>
                            </div>
                            <div class="item_li_right_label">
                                智能床垫
                                <span>500</span>
                            </div>
                            <div class="item_li_right_label">
                                智能手环
                                <span>500</span>
                            </div>
                            <div class="item_li_right_label">
                                报警终端
                                <span>500</span>
                            </div>
                        </div>
                    </li>
                    <li v-for="item in chartOptions.slice(4, 8)" :key="item.id" class="item_li">
                        <div class="item_li_content">
                            <e-chart :id="item.id" :option="item.option" :resize="item.resize"></e-chart>
                        </div>
                    </li>
                </ul>
            </el-col>
        </el-row>
    </div>
</template>

<script>
    import EChart from '../../components/echart/EChart';
    import Area from '@/components/area/Area';
    import { chartOption } from '../../utils/constant';
    export default {
        name: "Dashboard",
        components: {
            'e-chart': EChart,
            'area-info': Area
        },
        data(){
            return{
                map: null,
                chartOptions: [],

                chartOptionBottom: {
                    id: 9,
                    resize: false,
                    option: {}
                },
                checkedArea: [],
                timer: null,
            }
        },
        mounted() {
            this.initChart();

            this.getHomeData();

            this.initResize();
        },
        beforeRouteLeave(to, from, next){
            Object.assign(this.$data, this.$options.data());
            this.removeResize();
            next();
        },
        methods: {
            initChart(){
                for(let i = 0; i < 8; i++){
                    this.chartOptions[i] = {
                        id: i,
                        resize: false,
                        option: Object.assign({}, JSON.parse(JSON.stringify(chartOption)))
                    }
                }
            },

            checkArea(){
                this.showArea = !this.showArea;
            },

            getHomeData(){
                this.chartOptions[0].option.title.text = '经开区家庭医生12个月数量增长';
                this.chartOptions[0].option.xAxis[0].data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
                this.chartOptions[0].option.series[0].data = [1, 2, 3, 4, 5, 6, 7, 2, 3, 5, 4, 8];

                this.chartOptions[1].option.title.text = '经开区家庭医生12个月帮扶人数增长';
                this.chartOptions[1].option.xAxis[0].data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
                this.chartOptions[1].option.series[0].data = [1, 0, 1, 0, 5, 3, 7, 2, 3, 5, 4, 8];

                this.chartOptions[2].option.title.text = '经开区家庭医生服务覆盖率';
                this.chartOptions[2].option.color = [ 'rgb(0, 165, 255)', 'rgb(173, 226, 255)'];
                this.chartOptions[2].option.series[0] = {
                    name: '访问来源',
                    type: 'pie',
                    selectedOffset: 0,
                    selectedMode: 'single',
                    radius: ['55%', '70%'],
                    center: ['50%', '60%'],
                    label: {
                        normal: {
                            show: true,
                            position: 'center',
                            formatter: (params) => {
                                return (params.dataIndex === 0 ? '{num|'+ params.name + '}' : '');
                            },
                            rich: {
                                num: {
                                    fontSize: 23,
                                    fontWeight: 600,
                                    color: '#1facff',
                                },
                                pre: {
                                    fontSize: 12,
                                    color: '#1facff',
                                }
                            },
                            color: '#1facff',
                            fontWeight: 600,
                            fontSize: 20
                        },
                    },
                    labelLine: {
                        normal: {
                            show: false
                        }
                    },
                    data:[
                        {
                            value: 90,
                            name: '90%',
                            selected: true,
                            itemStyle: {
                                borderWidth: 0,
                                borderColor: '#1facff',
                            }
                        },
                        {value: 10, name: '10%'}
                    ]
                };

                this.chartOptions[3].option.title.text = '大数据分析覆盖率';
                this.chartOptions[3].option.color = ['rgb(255, 221, 26)', 'rgb(255, 240, 153)'];
                this.chartOptions[3].option.series[0] = {
                    name: '访问来源',
                    type: 'pie',
                    selectedOffset: 0,
                    selectedMode: 'single',
                    radius: ['55%', '70%'],
                    center: ['50%', '60%'],
                    label: {
                        normal: {
                            show: true,
                            position: 'center',
                            formatter: (params) => {
                                return (params.dataIndex === 0 ? '{num|'+ params.name + '}' : '');
                            },
                            rich: {
                                num: {
                                    fontSize: 23,
                                    fontWeight: 600,
                                    color: 'rgb(255, 221, 26)',
                                },
                                pre: {
                                    fontSize: 12,
                                    color: 'rgb(255, 221, 26)',
                                }
                            },
                            color: 'rgb(255, 221, 26)',
                            fontWeight: 600,
                            fontSize: 20
                        },
                    },
                    itemStyle: {

                    },
                    labelLine: {
                        normal: {
                            show: false
                        }
                    },
                    data:[
                        {value: 90, name: '90%', selected: true},
                        {value: 10, name: '10%'}
                    ]
                };

                this.chartOptions[4].option.title.text = '远程医疗覆盖率';
                this.chartOptions[4].option.color = ['rgb(0, 165, 255)', 'rgb(173, 226, 255)'];
                this.chartOptions[4].option.series[0] = {
                    name: '访问来源',
                    type: 'pie',
                    selectedOffset: 0,
                    selectedMode: 'single',
                    radius: ['55%', '70%'],
                    center: ['50%', '60%'],
                    label: {
                        normal: {
                            show: true,
                            position: 'center',
                            formatter: (params) => {
                                return (params.dataIndex === 0 ? '{num|'+ params.name + '}' : '');
                            },
                            rich: {
                                num: {
                                    fontSize: 23,
                                    fontWeight: 600,
                                    color: '#1facff',
                                },
                                pre: {
                                    fontSize: 12,
                                    color: '#1facff',
                                }
                            },
                            color: '#1facff',
                            fontWeight: 600,
                            fontSize: 20
                        },
                    },
                    itemStyle: {

                    },
                    labelLine: {
                        normal: {
                            show: false
                        }
                    },
                    data:[
                        {value: 95, name: '95%', selected: true},
                        {value: 5, name: '5%'}
                    ]
                };

                this.chartOptions[5].option.title.text = '设备覆盖率';
                this.chartOptions[5].option.color = ['rgb(0, 255, 217)', 'rgb(179, 255, 244)'];
                this.chartOptions[5].option.series[0] = {
                    name: '访问来源',
                    type: 'pie',
                    selectedOffset: 0,
                    selectedMode: 'single',
                    radius: ['55%', '70%'],
                    center: ['50%', '60%'],
                    label: {
                        normal: {
                            show: true,
                            position: 'center',
                            formatter: (params) => {
                                return (params.dataIndex === 0 ? '{num|'+ params.name + '}' : '');
                            },
                            rich: {
                                num: {
                                    fontSize: 23,
                                    fontWeight: 600,
                                    color: 'rgb(0, 255, 217)',
                                },
                                pre: {
                                    fontSize: 12,
                                    color: 'rgb(0, 255, 217)',
                                }
                            },
                            color: 'rgb(0, 255, 217)',
                            fontWeight: 600,
                            fontSize: 20
                        },
                    },
                    itemStyle: {

                    },
                    labelLine: {
                        normal: {
                            show: false
                        }
                    },
                    data:[
                        {value: 92, name: '92%', selected: true},
                        {value: 8, name: '8%'}
                    ]
                };

                this.chartOptions[6].option.title.text = '推送覆盖率';
                this.chartOptions[6].option.color = ['rgb(129, 61, 255)', 'rgb(211, 189, 255)'];
                this.chartOptions[6].option.series[0] = {
                    name: '访问来源',
                    type: 'pie',
                    selectedOffset: 0,
                    selectedMode: 'single',
                    radius: ['55%', '70%'],
                    center: ['50%', '60%'],
                    label: {
                        normal: {
                            show: true,
                            position: 'center',
                            formatter: (params) => {
                                return (params.dataIndex === 0 ? '{num|'+ params.name + '}' : '');
                            },
                            rich: {
                                num: {
                                    fontSize: 23,
                                    fontWeight: 600,
                                    color: 'rgb(129, 61, 255)',
                                },
                                pre: {
                                    fontSize: 12,
                                    color: 'rgb(129, 61, 255)',
                                }
                            },
                            color: 'rgb(129, 61, 255)',
                            fontWeight: 600,
                            fontSize: 20
                        },
                    },
                    itemStyle: {

                    },
                    labelLine: {
                        normal: {
                            show: false
                        }
                    },
                    data:[
                        {value: 100, name: '100%', selected: true},
                        {value: 0, name: '0%'}
                    ]
                };

                this.chartOptions[7].option.title.text = '大数据分析覆盖率';
                this.chartOptions[7].option.color = ['rgb(255, 221, 26)', 'rgb(255, 240, 153)'];
                this.chartOptions[7].option.series[0] = {
                    name: '访问来源',
                    type: 'pie',
                    selectedOffset: 3,
                    selectedMode: 'single',
                    radius: ['55%', '70%'],
                    center: ['50%', '60%'],
                    label: {
                        normal: {
                            show: true,
                            position: 'center',
                            formatter: (params) => {
                                return (params.dataIndex === 0 ? '{num|'+ params.name + '}' : '');
                            },
                            rich: {
                                num: {
                                    fontSize: 23,
                                    fontWeight: 600,
                                    color: 'rgb(255, 221, 26)',
                                },
                                pre: {
                                    fontSize: 12,
                                    color: 'rgb(255, 221, 26)',
                                }
                            },
                            color: 'rgb(255, 221, 26)',
                            fontWeight: 600,
                            fontSize: 20
                        },
                    },
                    itemStyle: {

                    },
                    labelLine: {
                        normal: {
                            show: false
                        }
                    },
                    data:[
                        {value: 100, name: '100%', selected: true},
                        {value: 0, name: '0%'}
                    ]
                };

                this.chartOptionBottom.option = Object.assign({}, JSON.parse(JSON.stringify(chartOption)));
                this.chartOptionBottom.option.title.text = '经开区医疗扶贫政策12个月帮扶人数';
                this.chartOptionBottom.option.xAxis[0].data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
                this.chartOptionBottom.option.xAxis[0].boundaryGap = ['20%', '20%'];
                this.chartOptionBottom.option.series[0].type = 'bar';
                this.chartOptionBottom.option.series[0].itemStyle = {
                    normal: {
                        color: function (params) {
                            let color;
                            if(params.data > 15){
                                color = '#22ac38'
                            }
                            if(params.data > 10 && params.data < 15){
                                color = '#f7e748'
                            }
                            if(params.data > 5 && params.data < 10){
                                color = '#ff8c00'
                            }
                            if(params.data < 5){
                                color = '#ff0000'
                            }

                            return color;
                        }
                    }
                };
                this.chartOptionBottom.option.series[0].barWidth = 20;
                this.chartOptionBottom.option.series[0].data = [1, 0, 1, 0, 5, 3, 7, 2, 3, 5, 4, 8];
            },

            // 初始化resize
            initResize(){
                window.addEventListener('resize', this.changeWindow, false);
            },

            // 移除resize
            removeResize(){
                window.removeEventListener('resize', this.changeWindow);
            },

            // window改变告诉子组件要重新渲染eChart
            changeWindow(){
                if(this.timer){
                    clearTimeout(this.timer);
                }

                this.timer = setTimeout(() => {
                    this.chartOptionBottom.resize = true;
                    for(let i = 0; i < 8; i++){
                        this.$set(this.chartOptions[i], 'resize', true);
                    }

                    setTimeout(() => {
                        this.chartOptionBottom.resize = false;
                        for(let i = 0; i < 8; i++){
                            this.$set(this.chartOptions[i], 'resize', false);
                        }
                    }, 10);
                }, 100)
            }
        }
    }
</script>

<style lang="scss">
    @import "../../assets/css/views/dashboard/dashboard";
</style>

to sum up


When using the mark state, using num ++ feels easier than using the resize state, but the state is not obvious and not semantic enough, so the state is still used, and then changed back after 100ms.

After the anti-shake process, there will be a noticeable delay in freezing, and it is not good to change it immediately. The timer time can be changed to 10ms.

 

Attach my WeChat public account, you can follow if you like it

Published 5 original articles · Likes0 · Visits 119

Guess you like

Origin blog.csdn.net/forteenBrother/article/details/105625306