huang-xiao-jian / echarts-ng

angularjs decoration for Baidu echarts.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

echarts-ng新版本实现

huang-xiao-jian opened this issue · comments

前言

个人项目中使用echarts-ng,彼时没有基于echarts3的封装,但使用过程中,感觉不够顺畅,重构早有打算,但始终无法付诸实践。后续接触ES6, rollup, webpack等新东西,于是元旦三天进行较大的重构,意图修复之前存在的问题。

遗留问题

  • 不支持媒体查询: #2
  • 容器resize调用方式诡异: #3
  • 无法顺利迁移Angular2: #4
  • 切换theme不够灵活: #5
  • 不支持事件绑定、主动触发事件
  • 超大配置对象使用双向绑定,性能,用户体验较差

新版本实现

实现Angular2迁移简单化,需要关键实现做到框架无关,剔除框架特异性考量。

  • 数据传递彻底放弃watch
  • 数据更新调整为命令式更新
  • 控制器内暴露echarts instance
  • 使用@bornkiller/echarts-bridge(https://github.com/bornkiller/echarts-ng) 作为中间层,方便迁移

Monkey Patch

创建包装实例,作为echarts实际接口与控制器内部调用的中间层。包装实例在DOM连接之后,完全为echarts instance,在连接之前,使用monkey patch实现部分方法,几乎全部支持链式调用。

monkey patch实现(连接之前即可使用):

  • group分组属性
  • showLoading, hideLoading
  • clear
  • dispose(不支持链式调用,因为实例已销毁)
  • resize
  • on
  • off

实例工具类函数基本不会再连接之前调用,进行延迟挂载即可:

  • getWidth
  • getHeight
  • getDom
  • getOption
  • getDataURL
  • getConnectedDataURL
  • isDisposed
  • dispatchAction
  • convertToPixel
  • convertToPixel
  • containPixel

样例

目前仅提供临时开发样例,后续会通过其他方式,大概理解新版本使用方式即可。

/**
 * @description - develop helper for showcase
 * @author - bornkiller <hjj491229492@hotmail.com>
 */
(function (angular) {
  'use strict';
  
  angular.module('echarts-showcase', ['echarts-ng'])
    .controller('ShowcaseController', ['$echarts', '$timeout', function ($echarts, $timeout) {
      this.themes = ['vintage', 'dark', 'macarons', 'infographic', 'shine', 'roma'];
      this.theme = 'vintage';
      this.show = true;
      this.mediaOptions = [
        {
          option: {
            legend: {
              orient: 'horizontal',
              left: 'center',
              top: 'top'
            },
            grid: {
              right: '10%'
            }
          }
        },
        {
          query: {
            maxWidth: 850
          },
          option: {
            legend: {
              orient: 'vertical',
              right: 10,
              top: '10%'
            },
            grid: {
              right: '15%'
            }
          }
        }
      ];
      this.options = {
        tooltip: {
          trigger: 'axis'
        },
        legend: {
          data: ['邮件营销', '联盟广告', '视频广告', '直接访问', '搜索引擎']
        },
        xAxis: [
          {
            type: 'category',
            boundaryGap: false,
            data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
          }
        ],
        yAxis: [
          {
            type: 'value'
          }
        ],
        series: [
          {
            name: '邮件营销',
            type: 'line',
            stack: '总量',
            areaStyle: { normal: {} },
            data: [120, 132, 101, 134, 90, 230, 210]
          },
          {
            name: '联盟广告',
            type: 'line',
            stack: '总量',
            areaStyle: { normal: {} },
            data: [220, 182, 191, 234, 290, 330, 310]
          },
          {
            name: '视频广告',
            type: 'line',
            stack: '总量',
            areaStyle: { normal: {} },
            data: [150, 232, 201, 154, 190, 330, 410]
          },
          {
            name: '直接访问',
            type: 'line',
            stack: '总量',
            areaStyle: { normal: {} },
            data: [320, 332, 301, 334, 390, 330, 320]
          },
          {
            name: '搜索引擎',
            type: 'line',
            stack: '总量',
            areaStyle: { normal: {} },
            data: [820, 932, 901, 934, 1290, 1330, 1320]
          }
        ]
      };
  
      this.legends = [
        { description: '邮件营销', selected: true },
        { description: '联盟广告', selected: true },
        { description: '视频广告', selected: true },
        { description: '直接访问', selected: true },
        { description: '搜索引擎', selected: true }
      ];
      
      this.instance = $echarts.create(this.theme, {}, this.mediaOptions);
      
      this.instance
        .showLoading()
        .on('click', (event) => {
          console.log(event);
        })
        .on('legendselectchanged', (event) => {
          console.log(event);
        });
      
      $timeout(() => {
        this.instance.setOption(this.options).hideLoading();
      }, 1200);
      
      this.handleThemeChange = () => {
        this.instance = $echarts.create(this.theme).setOption(this.options);
      };
      
      this.handleLegendChange = (name, selected) => {
        this.instance.dispatchAction({
          type: selected ? 'legendSelect' : 'legendUnSelect',
          name: name
        })
      };
      
      this.toggleSwitchStatus = () => {
        this.show = !this.show;
      };

      this.sync = _.debounce(() => {
        this.instance.resize();
      }, 100);

      window.addEventListener('resize', this.sync);
    }]);
  
  angular.bootstrap(document.body, ['echarts-showcase'], { strictDi: true });
})(angular);

录屏地址:https://www.opentest.co/share/56a9c8b0d0be11e688ecd510bcea4514

特别注意

  • 单实例如果需要重复连接,务必注意时间先后顺序。如果连接之后销毁实例,但包装实例未销毁,例如使用ng-if等指令,重连接之前,所有操作全部丢失,所以重连接后方可进一步操作,暨ng-if=true条件先于后续操作;
  • 动态调整DOM尺寸,可以连接后直接调用echarts#resize方法;
  • 瀑布图快捷方式后续以其他方式提供;

tooltip的支持还是使用plotOption来控制吗?在原readme里没能找到plotOption的用法,所以现在还不会调整toolkit。或请详述,有demo更好啦。
ps:readme里本页的链接地址尾部多了个“。”结果访问404了

@moux1024 echarts好像没有plotOption这个选项,现在的实现基本就是把实例的控制器权完全交给开发者,从而进行更精细的纯手动控制的状态,tooltip配置最终通过setOption方法生效。demo的话,正在写,会单独放置另外一个仓库。