closertb / closertb.github.io

浏览issue 或 我的网站,即可查看我的所有博客
https://closertb.site
32 stars 0 forks source link

从0开始撸一个支持单轴轮播的雷达图之未篇(解决Echarts雷达图单轴轮播) #14

Open closertb opened 5 years ago

closertb commented 5 years ago

写于:2017-11-09

今天要用到的理论在前两篇都讲过,如果你错过了前两篇,你应该先看看。 从0开始写一个支持单轴轮播的雷达图之首篇 从0开始写一个支持单轴轮播的雷达图之中篇

前言

通过前面我们自己实现了一个Radar图,并对其实现了单轴轮播和hover,我们已经明白了其中的坐标变换,在工作中,我们用自己写的雷达图来做产品的可能性不大,毕竟精力有限,而且和Echarts的图表相比,展示效果和鲁棒性都没法比。但是我们可否用Echarts生成一个radar图,自己为其写一个tooltip呢,答案是肯定的。
但一切的一切前提是,要支持单轴hover轮播,这个Radar图只有一个系列。其实现的核心思想:通过实例化Echarts对象的option,我们可以获取画布大小,获取标签和值,生成坐标系,算出要hover点的坐标,然后添加上面说的hover事件和自动轮播事件,直接上源码:

第一步绘制对应坐标系

/*target 添加画布的容器,option即为setOption中的Option,autoOption用于设定是否自动轮播,是否hover触发,*/  
function addHover(target,option,autoOption={autoShow:false,hoverEnable:true,time:1000}){
  const m =Math;
  var center ={  //获取画布的中心偏离比,因为echarts支持百分数控制radar图在画布中的位置,所以我们需要计算这个点,当然我们也可以为了方便,不在option中设置,这样我们就可以直接用画布的中心点,即(0.5,0.5);
    pointx:(option.radar.center&&Number(option.radar.center[0].substr(0,option.radar.center[0].length-1))/100)||0.5,  //计算X轴的偏离比例
    pointy:(option.radar.center&&Number(option.radar.center[1].substr(0,option.radar.center[1].length-1))/100)||0.5, //计算y轴的偏离比例
  };
  var x=target.offsetWidth*center.pointx,y=target.offsetHeight*center.pointy; //计算radar中心点x的值,计算radar中心点x的值
  var indicator = option.radar.indicator; //获取option中radar的标签
  var data = option.series[0].data[0].value;//获取option中radar的值,我们在这里只去第一个series的值;
  var length = indicator.length; //获取标签的长度,即雷达的拐点数,这个很重要
  var step =-1; //自动轮播要用到的参数
  var hovering =false ; //这个参数是控制轮播与hover同时触发,显示hover值,暂停轮播显示
  var radius=option.radar.radius,pointData=[]; //获取radar的半径
  var style ={  //hover显示的样式
    color:'#fff',
    border:'1px solid rgb(51,51,51)',
    borderRadius:'4px',
    backgroundColor:'rgba(50,50,50,0.7)'
  };

  const single = 2*m.PI /length*(-1);
  for(let i = 0;i<length;i++){
    var ratio = data[i]/indicator[i].max;                   
    pointData.push([radius*m.sin(i*single)*ratio,radius*m.cos(i*single)*ratio]);
  }
}    

/*获取鼠标在canvas画布上的位置(**不是浏览器窗口的鼠标位置)
* clientX获取的相对浏览器窗口左上角的位置,适用于所有浏览器
* 在chrome浏览器中,有一个zrX属性,是相对于元素本身的相对位置
* getBoundingClientRect()函数是获取元素边框相对于浏览器窗口的位置
* */
function getMousePos(canvas, event) {
    var rect = canvas.getBoundingClientRect();
    return {
        x: event.clientX - rect.left ,
        y: event.clientY - rect.top
    }
}

创建hover事件和自动轮播显示标签

创建标签

let label =document.createElement('label');
label.style.position='absolute';
label.style.display='none';
target.appendChild(label);
function hoverLabel(label,point,text,style){
  label.style.display ='none';
  label.style.top=point.y+'px';
  label.style.left=point.x+'px';
  label.style.border=style.border;
  label.style.color =style.color;
  label.style.borderRadius=style.borderRadius;
  label.style.backgroundColor = style.backgroundColor;
  label.style.transition ='left 0.4s cubic-bezier(0.23,1,0.32,1),top 0.4s cubic-bezier(0.23,1,0.32,1)';
  label.style.zIndex = 999;
  label.innerHTML =text;
  label.style.display ='inline';
}
function removeLabel(dom) {
  dom.style.display ='none';
}

添加轮播事件

  autoOption.autoShow&&(setInterval(function () { //控制轮播
      step = (step+1)%length;
      var showPoint={
          x:pointData[step][0]+x,
          y:y-pointData[step][2]
      }
      var tag =indicator[step];
      var text = tag.name+':'+m.round(data[step]*100/tag.max)+"%";
      (!hovering)&&hoverLabel(label,showPoint,text,style);
  },autoOption.time||1000));
  target.addEventListener('click',function(event){
      console.log(event);
  });

添加hover事件

autoOption.hoverEnable&&(target.addEventListener('mousemove',function(event){  //控制hover
  const canvas= target.querySelector('canvas');
  const mouse = getMousePos(canvas, event);
  let point={};
  let index =-1;
  const r =5;
  point.x=mouse.x-x;
  point.y=y-mouse.y;
  for(let i=0;i<pointData.length;i++) {
    let item = pointData[i];
    if (point.x > (item[0] - r) && point.x < (item[0] + r) && point.y > (item[1] - r) && point.y < (item[1] + r)) {
        index = i;
        break;
    }
  }
  if(index!==-1){
    var tag =indicator[index]
    var text = tag.name+':'+m.round(data[index]*100/tag.max)+"%";
    hovering =true;
    hoverLabel(label,mouse,text,style);
  }else{
    hovering =false;
    removeLabel(label);
  }
}))
}

至于调用,那就简单了

     // 第一步:
     var ele = document.getElementById('chart');
     var draw = echarts.init(ele);
     // 第二步:配置你的option
     var option ={};
     // 第三步画出你的雷达图
     draw.setOption(option);
     // 第四步:绑定事件:
     addHover(ele,option,{autoShow:true,hoverEnable:true});

上面,其实只是一个简易的版本,在实际应用中,我们需要考虑很多问题。

  1. 图表局部刷新带来的数据重载,就上面的代码,完全不行,我们没有清除定时器,这样会找出多个hover重叠
  2. 用过Echarts的人,我们都知道,tooltip有个formatter函数,用于显示数据的格式化,以便展示出我们想要的效果。
  3. 性能问题,每次Hover生成一个labe元素,是否太消耗性能,作为产品,我们应该怎样优化
  4. 怎样才能更通用,更方便的让人使用 基于上述问题,我对这个轮播函数做了改进和封装,使用方法是这样的:
    /*Echarts图表的正常实例化*/ 
    var target = document.getElementById('highOpinionChart');
    myChart = echarts.init(target);
    myChart.setOption(option);
    /*为图表绑定轮播事件和Hover事件*/
    var radarAutoInfo = {hoverEnable: true, autoShow: true, time: 2000, formatter:function(v){
    return v.text + ':' + (v.value*100/v.max).toFixed(2)+'%';
    }};        
    RadarAutoTip(myChart,target, option, radarAutoInfo);  //绑定
    //测试停止,开始和重置方法
    document.querySelector('#reset').addEventListener('click',function (e) {
    console.log(e);
    if(e.target.innerText === '停止'){
    e.target.innerText ='开始'
    myChart.radarAutoTip.stop();
    }else{
    e.target.innerText  = '停止';
    myChart.radarAutoTip.start();
    }
    });
    document.querySelector('#rereset').addEventListener('click',function (e) {
    myChart.radarAutoTip.reset();
    });

    至此,我们就完全解决了Echarts单轴轮播问题。 如果你嫌上面讲的太琐碎,不直观,可以直接取github看我的试验源码,文件是radarAutoTip.html,欢迎star

本文首发于:http://closertb.site ,转载请注明