Highcharts 中文社区

Highcharts 中文社区 门户 HC 学院 查看内容

Homology Event(同源事件)

2016-7-26 16:26| 发布者: 彭芳| 查看: 2089| 评论: 1

摘要: 对HighCharts上的功能进行扩展,拓展是为了能让某一图表的事件相应能传递到其他同种类型不同数据的图表中,下序简称:同源事件。
对HighCharts上的功能进行扩展,拓展是为了能让某一图表的事件相应能传递到其他同种类型不同数据的图表中,下序简称:同源事件
 <!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
    <title>highstock 多曲线同源事件(Homology Event)</title>
    <style type="text/css">
      div.title{width:450px;margin:20px auto;}
      div.container{width:900px;height:400px;margin:20px auto;}
      div.container div.child_container{float:left;width:300px;}
    </style>
    <script src="jquery.min.js"></script>
    <script src="highstock.src.js"></script>
    <script type="text/javascript">
      $(document).ready(function(){
        var names = ['MSFT', 'AAPL', 'GOOG'],
            createChart = function (series) {
              var options = {
                tooltip : {
                  crosshairs:true,
                },
                series: [series]
              };
              options.title = {text : 'chart案例'};
              $('#chart_' + series.name).highcharts('Chart', options);
              options.title = {text : 'stock案例'};
              $('#stock_' + series.name).highcharts('StockChart', options);
            };
        $.each(names, function (i, name) {
          $.getJSON('http://www.hcharts.cn/datas/jsonp.php?filename=' + name.toLowerCase() + '-c.json&callback=?', function (data) {
            createChart({name : name.toLowerCase(), data : data});
          });
        });
        /*
         *  同源事件的关键性代码
         *  window.event是单例,因此需要进行属性拷贝,模拟一个新的鼠标事件
         */
        $('.container .child_container').on('mousemove', function(e){
          var sourceChartContainer = $(this);
          var sourceChart = sourceChartContainer.highcharts();
          var sourceXAxis = sourceChart.xAxis[0];
          var extremes = sourceXAxis.getExtremes();
          var targetChartContainerList = sourceChartContainer.siblings();
          targetChartContainerList.each(function(index, targetChartContainerElement){
            var targetChartContainer = $(targetChartContainerElement);
            var targetChart = targetChartContainer.highcharts();
            var sourceOffset = sourceChartContainer.offset();
            var targetOffset = targetChartContainer.offset();
            var targetE = {};
            for(var i in e){
              targetE[i] = e[i];
            }
            targetE.pageX = e.pageX + targetOffset.left - sourceOffset.left;
            targetE.pageY = e.pageY + targetOffset.top - sourceOffset.top;
            var targetEl =  targetChartContainer.find('rect.highcharts-background')[0] || targetChartContainer.find('path.highcharts-tracker')[0];
            targetE.target = targetE.srcElement = targetE.fromElement = targetE.toElement = targetEl;
            targetE.delegateTarget = targetE.currentTarget = targetChartContainer[0];
            targetE.originalEvent = targetE;
            if(targetChart && targetChart.pointer){
              targetChart.pointer.onContainerMouseMove(targetE);
            }
            if(targetChart && targetChart.scroller){
              targetChart.scroller.mouseMoveHandler(targetE);
            }
            if(sourceChart.mouseIsDown == 'mouseup' || sourceChart.mouseIsDown == 'mousedown'){
              if(targetChart && targetChart.xAxis[0]){
                var targetXAxis = targetChart.xAxis[0];
                targetXAxis.setExtremes(extremes.min, extremes.max);
              }
            }
          });
          return false;
        });
        $('.container .child_container').bind('mouseleave', function(e){
          var sourceChartContainer = $(this);
          var sourceChart = sourceChartContainer.highcharts();
          if(sourceChart && sourceChart.pointer){
            sourceChart.pointer.reset();
            sourceChart.pointer.chartPosition = null;
          }
          var targetChartContainerList = sourceChartContainer.siblings();
          targetChartContainerList.each(function(index, targetChartContainerElement){
            var targetChartContainer = $(targetChartContainerElement);
            var targetChart = targetChartContainer.highcharts();
            if(targetChart && targetChart.pointer){
              targetChart.pointer.reset();
              targetChart.pointer.chartPosition = null;
            }
          });
          return false;
        });
        $('.container .child_container').bind('mouseup', function(e){
          var sourceChartContainer = $(this);
          var sourceChart = sourceChartContainer.highcharts();
          var targetChartContainerList = sourceChartContainer.siblings();
          e.type = 'mouseup';
          if(sourceChart && sourceChart.pointer){
            sourceChart.pointer.drop(e);
            sourceChart.mouseIsDown = 'mouseup';
          }
        });
      });
    </script>
  </head>
  <body>
    <div class="title"><h3>多曲线同源事件案例</h3></div>
    <div class="container" id="chart">
      <div class="child_container" id="chart_msft"></div>
      <div class="child_container" id="chart_aapl"></div>
      <div class="child_container" id="chart_goog"></div>
    </div>
    <div class="container" id="stock">
      <div class="child_container" id="stock_msft"></div>
      <div class="child_container" id="stock_aapl"></div>
      <div class="child_container" id="stock_goog"></div>
    </div>
  </body>
</html>

[/i][/i]


思路分享
    什么叫同源事件,如果有几个一模一样的图表,例如A、B、C、D。四个图表长的一模一样,数据模型也一模一样,除了数据不一样。当看到A表中的某个数据时,也希望看到其他表上的同样的数据点,有的人会把这四个图标合成为一个图表,使用单X轴多曲线即可完成。但如果希望就用四张图表来表示,如果让其中一张图标的行为能触发到其他图表上呢。这就要使用同源事件,顾名思义,就是让所有的图表的事件发生在同一个源头。
    整个HighChart中几乎没有底层绘图的API,而所有的行为都是基于Window.Event来的。简单来看一段源码:
/**
 * Start a drag operation
 */
dragStart: function (e) {
    var chart = this.chart;
    // Record the start position
    chart.mouseIsDown = e.type;
    chart.cancelClick = false;
    chart.mouseDownX = this.mouseDownX = e.chartX;
    chart.mouseDownY = this.mouseDownY = e.chartY;
},
    以上代码,很清楚要做什么,然而绘制的图像并不是基于XY轴,而是基于Window.Event事件,从中获取XY属性。除此以外,还会获取其他很多内容,有些是非Window.Event对象的属性。
    那么基于数学信息(平面坐标)的API绘制被打消了,那么就只能从Window.Event入手,想办法把这个事件传递到其他图标中去。例如,鼠标移动时,会根据Window.Event去标记标记点和绘制数据线:
// The mousemove, touchmove and touchstart event handler
onContainerMouseMove: function (e) {
    var chart = this.chart;
    hoverChartIndex = chart.index;
    e = this.normalize(e);     
    e.returnValue = false; // #2251, #3224
         
    if (chart.mouseIsDown === 'mousedown') {
        this.drag(e);
    }
         
    // Show the tooltip and run mouse over events (#977)
    if ((this.inClass(e.target, 'highcharts-tracker') || chart.isInsidePlot(e.chartX - chart.plotLeft, e.chartY - chart.plotTop)) && !chart.openMenu) {
        this.runPointActions(e);
    }
}
    在A图表进行鼠标移动时,将其事件传递到其他图表中,以下为伪代码:
A.bind('mousemove', function(e){
    B.onContainerMouseMove(e);
    C.onContainerMouseMove(e);
    D.onContainerMouseMove(e);
});
    经过测试,发现根本行不通。打了断点测试发现,B、C、D图表传入的Event所作用对象target全是A。根据这个问题,动态的改变作用对象:
    
A.bind('mousemove', function(e){
    e.currentTarget = B;
    e.target = A.find('.highcharts-background');
    B.onContainerMouseMove(e);
    // C、D同理
});
    经过测试,仍然行不通。一方面我们要有触发对象,另一方面Window.Event是一个单例对象,你所创建的新的Event也只是更新了Window.Event的引用。所以,最后我查看源码发现,他会对Event对象进行自定义的规范化处理:
e = this.normalize(e);
    此方法的源码是获取到传进来的Event对象进行计算,获得与Chart直接相关的数据,例如ChartX、ChartY这些非Event原生属性。而对Event对象,normalize方法中并没有做校验处理。而仅仅只是一句:
// common IE normalizing
e = e || window.event;
// Framework specific normalizing (#1165)
e = washMouseEvent(e);
    既然这样,我想到了通过属性复制的方法,重新构建几个非Event实例的JSON对象,因此就有了以上的实现代码。
   另外官方也有一个类似的例子:http://code.hcharts.cn/hcharts.cn/hhhG87




路过

雷人

握手
1

鲜花

鸡蛋

刚表态过的朋友 (1 人)

发表评论

最新评论

引用 gipal_zheng 2018-7-3 18:01
请问在vue-highchart如何使用同源事件的代码,我试了不行,用了多种方法,求帮助。

查看全部评论(1)

返回顶部