美文网首页tool
数据可视化前端项目开发 - 图表

数据可视化前端项目开发 - 图表

作者: VioletJack | 来源:发表于2021-12-15 19:04 被阅读0次

    图表

    highchart vs g2plot

    在 chart 库的选择上,项目先后使用了 highchart 和 g2plot。在我接手项目之前,用的是 highchart 库,但 highchart 有 3 点是让我挺难受的。

    1. 无法直接将数据源转为 chart ,必须进行一轮复杂的数据转换去匹配 highchart 的配置方式。
    2. 由于无法直接将数据源变成 chart,老系统非常粗暴的将整个 highchart 配置都交给后端来编写,前端侧无法控制 chart 呈现,而后端同学几经转手难以理解其中的配置,每每有 chart 改动需求都非常头疼。
    3. 就我个人而言,highchart 的官方文档写的很差劲,看文档很累。

    在我们前后端同学再 highchart 的泥潭中挣扎一段时间后,逐步使用蚂蚁的 g2plot 来替代。收益也很明显。

    1. g2 和 g2plot 的文档和示例非常友好,很容易入手。
    2. g2plot 的 chart 呈现是将数据源和 chart 配置分离的,后端同学只需要提供一串数据数组,chart 配置的事情都交给前端即可。
    3. g2 的功能非常强大,目前项目已经基于 g2 实现了中国地图、折线图、双轴图、饼图、环图、柱状图、条形图以及更加复杂的组合图。

    g2plot 在 Vue 中的封装

    因为 g2plot 并没有特别针对 Vue 的封装,简单写了个组件用于 Vue 项目中。

    <template>
      <div ref="chart" />
    </template>
    
    <script>
    import { Area, Column, Line, Bar, Pie, DualAxes, Gauge } from '@antv/g2plot'
    import { getChartColorsByGameId } from '@/utils/theme.js'
    
    export default {
      name: 'GmChart',
      props: {
        type: String,
        data: Array,
        options: Object
      },
      mounted() {
        this.chartColors = getChartColorsByGameId(this.$store.state.gameId)
        this.chartInstanse = this.getChartInstance()
        this.chartInstanse.update(this.options)
        this.chartInstanse.render()
      },
      beforeDestroy() {
        this.chartInstanse.destroy()
      },
      methods: {
        getChartInstance() {
          const option = {
            data: this.data,
            height: 300,
            color: this.chartColors,
            ...this.options
          }
          switch (this.type) {
            case 'area':
              return new Area(this.$refs.chart, option)
            case 'line':
              return new Line(this.$refs.chart, option)
            case 'column':
              return new Column(this.$refs.chart, option)
            case 'bar':
              return new Bar(this.$refs.chart, option)
            case 'pie':
              return new Pie(this.$refs.chart, option)
            case 'dual-axes':
              return new DualAxes(this.$refs.chart, option)
            case 'gauge':
              return new Gauge(this.$refs.chart, option)
            default:
              return null
          }
        }
      },
      watch: {
        data(val) {
          this.chartInstanse.changeData(val) // 更新 data 数据
        },
        options(val) {
          this.chartInstanse.update(val) // 更新 chart 配置
        }
      }
    }
    </script>
    

    中国地图数据占比图

    在 g2plot 中并没有快捷的地图选项,这就得用到更强大的 g2 的地图了。做地图要注意几点:

    1. 中国地图的 geo 数据 g2 并没有提供,我用的是 github 上的 china-geojson 库,里面包含了中国及各省市的经纬度信息。
    2. 当时后端同学给我的省市名称是简写,如上海、新疆这种,而非全称。这样是无法匹配数据显示到地图上的。后端提供的数据源必须和地图 json 文件中的信息一致。
    <template>
      <div id="china-province-map-container" />
    </template>
    
    <script>
    import DataSet from '@antv/data-set'
    import { Chart } from '@antv/g2'
    
    // Geo JOSN file path
    const GeoPath =
      process.env.NODE_ENV === 'development'
        ? '/china-geo.json'
        : '/spa/china-geo.json'
    
    export default {
      name: 'chinaProvinceMap',
      props: {
        chartData: Array,
      },
      data() {
        return {
          mapData: null,
          chart: null,
        }
      },
      async mounted() {
        await this.getChinaMapGeo()
        this.createMapChartInstance()
        this.renderMap()
      },
      beforeDestroy() {
        if (this.chart) {
          this.chart.destroy()
        }
      },
      methods: {
        createMapChartInstance() {
          this.chart = new Chart({
            container: 'china-province-map-container',
            // autoFit: true,
            width: 500,
            height: 400,
          })
    
          // set tootip
          this.chart.tooltip({
            showTitle: false,
            showMarkers: false,
            shared: true,
            customItems(items) {
              return items.map((item) => ({
                ...item,
                name: item.data.tag_value,
              }))
            },
            domStyles: {
              'g2-tooltip': {
                width: '180px',
              },
            },
          })
          // set scale sync
          this.chart.scale({
            longitude: {
              sync: true,
            },
            latitude: {
              sync: true,
            },
          })
          // close axis
          this.chart.axis(false)
          // close legend
          this.chart.legend(false)
        },
        getChinaMapGeo() {
          return fetch(GeoPath)
            .then((res) => res.json()) // transfer
            .then((mapData) => {
              this.mapData = mapData
            })
        },
        renderMap(isUpdate) {
          this.chart.clear()
          const geoDv = new DataSet.View().source(this.mapData, {
            type: 'GeoJSON',
          })
    
          const geoView = this.chart.createView()
          geoView.data(geoDv.rows)
          geoView.polygon().position('longitude*latitude').color('#ebedf0').style({
            lineWidth: 1,
            stroke: '#fafbfc',
          })
          geoView.tooltip(false)
    
          const userView = this.chart.createView()
          userView.data(this.chartData)
          userView
            .point()
            .position('longitude*latitude')
            .color('#1890ff')
            .shape('circle')
            .size('num', [3, 11])
            .style({
              lineWidth: 1,
              stroke: '#1890ff',
            })
          userView.interaction('element-active')
          this.chart.render(isUpdate)
        },
      },
      watch: {
        chartData() {
          if (this.chart) {
            this.renderMap(true)
          }
        },
      },
    }
    </script>
    
    <style lang="scss" scoped>
    </style>
    

    效果如下


    map

    复杂图表的实现

    另外还有一种比较麻烦的图表是那种用户想要的定制化图表,比如下面两个:


    组合条形图
    组合双轴图

    以上 chart 都可以通过 g2plot 的 多图层图表 Mix 来实现的。想要实现多图层图表其实并不复杂,只要将它拆分为最小单位的一个个图表,就像 PhotoShop 的图层一样堆叠实现就可以了。

    不过多图层图表是有一些坑的:

    • 多图层图表的 legend 图例并无法显示完全,暂时没有找到原因。我是通过异常 g2 的图例自己手写替代来绕过的。
    • 多图层的图标 tooltip 提示信息在某些地方会出现不停闪烁的问题,暂时没有解决。
    • 图表的标注属性 annotations 会出现在整个图表的最上层,而不是当前图表上层,会出现遮挡其他图层的图表的问题,暂时没有解决。

    另外一些需求

    • 在折线图上增加关键点的小旗标识(使用 g2 的绘图功能)。
    G2.registerShape('point', 'flag-point', {
      draw(cfg, container) {
        const point = { x: cfg.x, y: cfg.y }
        const group = container.addGroup()
        if (cfg.data.operation_event && cfg.data.operation_event.length > 0) {
          group.addShape('image', {
            attrs: {
              x: point.x - 4,
              y: point.y - 20,
              img: FlagImg,
              width: 16,
              height: 16,
            },
          })
        }
    
        return group
      },
    })
    
    • 在 tooltip 中增加额外信息(使用 tooltip 的 customContent 方法)。
    • 在折线图中将某一段标为虚线(使用 Line 的 annotations 属性)。

    相关文章

      网友评论

        本文标题:数据可视化前端项目开发 - 图表

        本文链接:https://www.haomeiwen.com/subject/wimpfrtx.html