IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> vue3 Echarts绘图遇到问题与解决 -> 正文阅读

[JavaScript知识库]vue3 Echarts绘图遇到问题与解决

1. 版本介绍

前端框架:vue("v 3.2.38”),绘图:echarts("v 5.3.3”)
vue2和vue3语法上会有些不同,Echarts版本影响不大

2. 遇到问题

2.1、页面大小变化时,画布大小未随页面大小变化

2.2、配合tab页使用时,切换tab,数据改变后,重新切换到图形tab会报获取不到dom宽高如下警告:

在这里插入图片描述

3. 问题解决(实测有效)

3.1、解决页面大小变化时,画布大小未随页面大小变化问题

主要解决思路:
当window变化resize时,画布大小也跟随变化resize:
vue执行mounted生命周期时,为window绑定resize事件,vue执行beforeUnmount生命周期时window解绑resize事件。代码如下:
util.js文件

/**
 * @description 绑定事件 on(element, event, handler)
 */
 export const on = (function () {
  if (document.addEventListener) {
    return function (element, event, handler) {
      if (element && event && handler) {
        element.addEventListener(event, handler, false)
      }
    }
  } else {
    return function (element, event, handler) {
      if (element && event && handler) {
        element.attachEvent('on' + event, handler)
      }
    }
  }
})()

/**
 * @description 解绑事件 off(element, event, handler)
 */
export const off = (function () {
  if (document.removeEventListener) {
    return function (element, event, handler) {
      if (element && event) {
        element.removeEventListener(event, handler, false)
      }
    }
  } else {
    return function (element, event, handler) {
      if (element && event) {
        element.detachEvent('on' + event, handler)
      }
    }
  }
})()

chart.vue

<template>
  <div ref="dom" style="height:600px;width:600px;"></div>
</template>

<script>
import * as echarts from "echarts";
/*引入绑定和解绑事件,引入路径自定义*/
import { on, off } from "../../../../libs/util.js"; 
export default {
  name: "LineChart",
  data() {
    return {
      dom: null,
    };
  },
  methods: {
  /*画布resize事件*/
    resize() {
      this.dom.resize();
    },
    drawLine() {
      if(this.dom){
        // 如果已存在echart实例,在重新渲染前先销毁当前echart实例
        this.dom.dispose()
      }
      /*初始化dom*/
      this.dom = echarts.init(this.$refs.dom);
      let option = {
      	option = {
  			xAxis: {
    			type: 'category',
    			data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
  			},
 			yAxis: {
    			type: 'value'
  			},
  			series: [
   				{
      				data: [150, 230, 224, 218, 135, 147, 260],
      				type: 'line'
    			}
  			]
		};
      } 
      /*第一个参数为图形的相关配置,第二个参数设为true,每次数据变化都是重新渲染的,而非和之前数据合并绘制*/
      this.dom.setOption(option, true);
    },
  },
  mounted() {
      this.drawLine();
      /*为window绑定resize事件,window resize时,图形绑定的dom也会resize*/
      on(window, "resize", this.resize);
  },
  beforeUnmount() {
  	/*vue组件卸载前,window解除绑定的resize事件*/
    off(window, "resize", this.resize);
  },
};
</script>
<style scoped>
</style>

3.2、解决tab页切换时,vue警告不能获取绘图dom宽高问题

我是把绘图部分抽出来了,作为一个单独的组件 (如果只放在一个vue组件里,应该不会存在这个问题),父组件调用接口获取数据,将数据传给子组件,子组件监听数据发生变化时执行绘图函数,因为tab页是点击当前tab才会加载dom,所以在另一个tab页数据发生变化时,绘图组件监听到数据变化会执行绘图函数,但此时dom还未加载,所以获取不到宽高,所以会报相关警告,我们会看到一个空页面。
主要解决思路:
除了监听数据变化重新绘制图形外,监听tab页更改并当前tab页为绘制图形tab页时,dom已经加载,此时执行绘制图形函数就能正常绘制出图形。相关代码如下:
parent.vue(一个查询框和一个tab页,查询框更新数据,tab页展示数据)

<template>
  <div style="height:100%;width:100%;">
    <Card dis-hover>
      <Form
        ref="searchForm"
        :model="searchForm"
        label-position="right"
        :label-width="60"
      >
        <Row :gutter="24">
          <Col span="8">
            <FormItem label="name" prop="name">
              <Input
                v-model="searchForm.name"
                placeholder="请输入name"
                clearable
              />
            </FormItem>
          </Col>
          <Col span="8">
            <FormItem>
              <div>
                <Button type="primary" @click="search">查询</Button>
                <Button class="ml15" @click="clear" style="marginLeft: 10px"
                  >清空</Button
                >
              </div>
            </FormItem>
          </Col>
        </Row>
      </Form>
    </Card>
    <Tabs :animated="false" style="marginTop:20px;" v-model="tab"  @on-click="changeTabs">
      <TabPane name="chart" label="图形" icon="md-analytics">
          <Tree class="chart" :value="chartData" :name="searchForm.name" :tab="tab"/>
        </TabPane>
        <TabPane name="table" label="表格" icon="md-document">
          <Table class="table" :tableData="tableData" :name="searchForm.name" :loading="loading"/>
        </TabPane>
    </Tabs>
  </div>
</template>
<script>
import Table from './table.vue'
import Tree from './components/charts/tree.vue'
/*这个是我封装好的axios调用接口获取数据的方法*/
import {searchData} from '@/api/searchData.js'
export default {
  name: 'Parent',
  components: {
    Table,
    Chart
  },
  data(){
    return {
      searchForm: {
        name: "",
      },
      loading: false, // 表格是否加载中
      tab: 'chart',
      chartData: {}, // 调用链数据
      tableData: [], // 表格数据
    }
  },
  mounted(){

    this.getData()
  },
  methods: {
    search() {
      this.getData();
    },
    clear() {
      this.$refs.searchForm.resetFields();
    },
    changeTabs(tab){
      this.tab = tab
    },
    async getData() {    
      try{
        const params = {
          nmae: this.searchForm.name
        }
        // this.$Loading.start()
        this.loading = true
        const {data:json}= await searchData(params)
        // 初始化
        this.chartData = {}
        this.tableData = []
        if(json.success){
        	/*这块主要看接口怎么返回的,不同的接口定义返回的字段可能不一样,我这里json.data为一个对象,所以表格数据要push*/
            this.chartData = json.data
            this.tableData.push(json.data)
            this.$Message.success("成功")
          	this.loading = false
          // this.$Loading.finish()
        }else{
          this.chartData = {}
          this.tableData = []
          this.$Message.error("失败")
        }
      }catch(error){
        console.log(error)
      }
    },
  }
}
</script>

<style scoped>
  .chart{
    height: 680px;
    /* width:100%; */
    overflow: auto;
  }
  .table{
    height: 650px;
    /* width:100%; */
    overflow: auto;
  }
</style>

tree.vue(table.vue就是普通的表格组件,这里不再放出代码,主要看下Echarts绘图部分代码)

<template>
  <div style="height:100%;width:100%">
    <div ref="dom" style="height: 680px"></div>
  </div>
</template>

<script>
import * as echarts from "echarts";
import { on, off } from "../../../../libs/util.js";
export default {
  name: "Tree",
  components: {
  },
  props: {
    value: Object,
    name: String,
    tab: String,
  },
  data() {
    return {
	showDetail: false,
      dom: null,
      detailData: {},
      treeData: [], // 树图绑定的数据
    };
  },
  methods: {
    resize() {
      this.dom.resize();
    },
    renderChart() {
      if (this.dom) {
        // 如果已存在echart实例,在重新渲染前先销毁当前echart实例
        this.dom.dispose();
      }
      var option;
      this.dom = echarts.init(this.$refs.dom);
      option = {
        tooltip: {
          trigger: "item",
          triggerOn: "mousemove",
          formatter: (data) => {
            return `name: ${data.data.name} <br/>  age: ${data.data.age}`;
          },
        },
        series: [
          {
            type: "tree",
            // expandAndCollapse: false,
            id: 0,
            zoom: 0.8, // 默认视图缩放0.8
            roam: true, // 是否支持缩放和平移,scale--缩放,move--平移  true--都允许
            name: "tree1",
            data: [this.value],
            top: "20px",
            left: "180px",
            bottom: "20px",
            right: "200px",
            symbol: "emptyCircle", // 节点类型
            symbolSize: 10, // 节点大小
            edgeShape: "polyline", // 节点间连线类型--polyline折线curve曲线
            edgeForkPosition: "40%", // 子节点从哪个位置分叉
            initialTreeDepth: 3, // 默认展开层级数,根节点层级为0
            label: {
              position: "left",
              verticalAlign: "middle",
              align: "right",
              color: "#293c55",
              margin: [4, 4],
              borderWidth: 1,
              borderColor: "#aaa",
              backgroundColor: "white",
              borderRadius: 2,
              padding: [5, 4],
              formatter: (data) => {
              /*格式化label展示支持富文本*/
                if (data.data) {
                      return (
                        [
                          `{name|${data.data.name}}`,
                          `{age|${
                            data.data.age.}}`,
                        ].join("\n") + `    {favor|${data.data.favor}}`
                      );
              },
              rich: {
                name: {
                  color: "#5E6472",
                  fontSize: "12",
                  lineHeight: 20,
                  align: "left",
                },
                age: {
                  color: "#5E6472",
                  fontSize: "12",
                  lineHeight: 20,
                  align: "left",
                },
                favor: {
                  color: "white",
                  backgroundColor: "#DD1C1A",
                  padding: 5,
                  margin: 5,
                  borderRadius: 3,
                  align: "right",
                },
              },
            },
            leaves: {
              label: {
                position: "right",
                verticalAlign: "middle",
                align: "left",
              },
            },
            emphasis: {
              focus: "relative", // 高亮时聚焦所有的祖先和子孙节点
            },
            expandAndCollapse: true,
            animationDuration: 550,
            animationDurationUpdate: 750,
          },
        ],
      };
      this.dom.setOption(option, true);
      /*if框里的内容为我项目需要,实现功能主要为点击树图label执行其他操作(比如打开一个drawer,并传递节点相关数据给drawer等)不展开或折叠节点,点击节点展开折叠节点,和本篇文章介绍的两个问题没有关联,可以忽略不看。至于分本地环境和生产环境是我本地运行和生产使用的echarts版本不一,也可忽略*/
      if (option && typeof option === "object") {
        // myChart.setOption(option, true);
        this.dom.setOption(option, true);
        this.dom.on("mousedown", (e) => {
          const flag = e.event.target.__proto__.constructor.name;
          // 本地环境,点击label框,flag为Rect或TSpan,点击节点,flag为Sub
          if (flag == "Rect" || flag == "TSpan") {
            // 点击label矩形框展示详情
            this.showDetail = true;
            this.detailData = e.data;
          } else if (flag == "Sub") {
            // 点击节点圆展开或折叠节点
          }
          // 生产环境点击label矩形框值为e,点击节点,flag值为n
          // if (flag == 'e') {
          //   // 点击label矩形框展示详情
          //   this.showDetail = true;
          //   this.detailData = e.data;
          // } else if (flag == "n") {
          //   // 点击节点圆展开或折叠节点
          // }
        });
      }
    },
  },
  mounted() {
    this.$nextTick(() => {
      this.renderChart();
      /*问题2.1相关*/
      on(window, "resize", this.resize);
    });
  },
  beforeUnmount() {
    off(window, "resize", this.resize);
  },
  watch: {
  /*监听value是为了在当前绘图tab页点击查询数据改变重新画图*/
    value: {
      handler() {
      /*nextTick可以获取数据更新后最新DOM的变化,主要在数据更新后立即操作dom的时候使用*/
        this.$nextTick(() => {
          this.renderChart();
        });
      },
      deep: true,
    },
    tab: {
      handler(){
        if(this.tab == 'chart'){
          this.$nextTick(() => {
            this.renderChart()
          })
        }
      }
    }
  },
};
</script>

<style scoped></style>

4、主要代码截图(代码太长可以跳过只看关键代码截图,有疑惑再看3主要解决方案完整代码)

4.1、画布不随页面变化问题解决关键代码

util.js文件里绑定事件和解绑事件
组件相关代码截图:
在这里插入图片描述

4.2、tab页echarts绘图获取不到dom宽高问题解决关键代码

在这里插入图片描述

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2022-09-24 20:47:38  更:2022-09-24 20:49:00 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年1日历 -2025/1/11 15:52:25-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码