text: /*
   * uCharts v1.9.4.20200331
   * uni-app平台高性能跨全端图表，支持H5、APP、小程序（微信/支付宝/百度/头条/QQ/360）
   * Copyright (c) 2019 QIUN秋云 https://www.ucharts.cn All rights reserved.
   * Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
   * 
   * uCharts官方网站
   * https://www.uCharts.cn
   * 
   * 开源地址:
   * https://gitee.com/uCharts/uCharts
   * 
   * uni-app插件市场地址：
   * http://ext.dcloud.net.cn/plugin?id=271
   * 
   */
  
  'use strict';
  
  var config = {
    yAxisWidth: 15,
    yAxisSplit: 5,
    xAxisHeight: 15,
    xAxisLineHeight: 15,
    legendHeight: 15,
    yAxisTitleWidth: 15,
    padding: [10, 10, 10, 10],
    pixelRatio: 1,
    rotate: false,
    columePadding: 3,
    fontSize: 13,
    //dataPointShape: ['diamond', 'circle', 'triangle', 'rect'],
    dataPointShape: ['circle', 'circle', 'circle', 'circle'],
    colors: ['#1890ff', '#2fc25b', '#facc14', '#f04864', '#8543e0', '#90ed7d'],
    pieChartLinePadding: 15,
    pieChartTextPadding: 5,
    xAxisTextPadding: 3,
    titleColor: '#333333',
    titleFontSize: 20,
    subtitleColor: '#999999',
    subtitleFontSize: 15,
    toolTipPadding: 3,
    toolTipBackground: '#000000',
    toolTipOpacity: 0.7,
    toolTipLineHeight: 20,
    radarLabelTextMargin: 15,
    gaugeLabelTextMargin: 15
  };
  
  let assign = function (target, ...varArgs) {
      if (target == null) {
          throw new TypeError('Cannot convert undefined or null to object');
      }
      if (!varArgs || varArgs.length <= 0) {
          return target;
      }
      // 深度合并对象
      function deepAssign(obj1, obj2) {
          for (let key in obj2) {
              obj1[key] = obj1[key] && obj1[key].toString() === "[object Object]" ?
                  deepAssign(obj1[key], obj2[key]) : obj1[key] = obj2[key];
          }
          return obj1;
      }
  
      varArgs.forEach(val => {
          target = deepAssign(target, val);
      });
      return target;
  };
  
  var util = {
    toFixed: function toFixed(num, limit) {
      limit = limit || 2;
      if (this.isFloat(num)) {
        num = num.toFixed(limit);
      }
      return num;
    },
    isFloat: function isFloat(num) {
      return num % 1 !== 0;
    },
    approximatelyEqual: function approximatelyEqual(num1, num2) {
      return Math.abs(num1 - num2) < 1e-10;
    },
    isSameSign: function isSameSign(num1, num2) {
      return Math.abs(num1) === num1 && Math.abs(num2) === num2 || Math.abs(num1) !== num1 && Math.abs(num2) !== num2;
    },
    isSameXCoordinateArea: function isSameXCoordinateArea(p1, p2) {
      return this.isSameSign(p1.x, p2.x);
    },
    isCollision: function isCollision(obj1, obj2) {
      obj1.end = {};
      obj1.end.x = obj1.start.x + obj1.width;
      obj1.end.y = obj1.start.y - obj1.height;
      obj2.end = {};
      obj2.end.x = obj2.start.x + obj2.width;
      obj2.end.y = obj2.start.y - obj2.height;
      var flag = obj2.start.x > obj1.end.x || obj2.end.x < obj1.start.x || obj2.end.y > obj1.start.y || obj2.start.y < obj1.end.y;
      return !flag;
    }
  };
  
  //兼容H5点击事件
  function getH5Offset(e) {
    e.mp = {
      changedTouches: []
    };
    e.mp.changedTouches.push({
      x: e.offsetX,
      y: e.offsetY
    });
    return e;
  }
  
  // hex 转 rgba
  function hexToRgb(hexValue, opc) {
    var rgx = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
    var hex = hexValue.replace(rgx, function(m, r, g, b) {
      return r + r + g + g + b + b;
    });
    var rgb = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
    var r = parseInt(rgb[1], 16);
    var g = parseInt(rgb[2], 16);
    var b = parseInt(rgb[3], 16);
    return 'rgba(' + r + ',' + g + ',' + b + ',' + opc + ')';
  }
  
  function findRange(num, type, limit) {
    if (isNaN(num)) {
      throw new Error('[uCharts] unvalid series data!');
    }
    limit = limit || 10;
    type = type ? type : 'upper';
    var multiple = 1;
    while (limit < 1) {
      limit *= 10;
      multiple *= 10;
    }
    if (type === 'upper') {
      num = Math.ceil(num * multiple);
    } else {
      num = Math.floor(num * multiple);
    }
    while (num % limit !== 0) {
      if (type === 'upper') {
        num++;
      } else {
        num--;
      }
    }
    return num / multiple;
  }
  
  function calCandleMA(dayArr, nameArr, colorArr, kdata) {
    let seriesTemp = [];
    for (let k = 0; k < dayArr.length; k++) {
      let seriesItem = {
        data: [],
        name: nameArr[k],
        color: colorArr[k]
      };
      for (let i = 0, len = kdata.length; i < len; i++) {
        if (i < dayArr[k]) {
          seriesItem.data.push(null);
          continue;
        }
        let sum = 0;
        for (let j = 0; j < dayArr[k]; j++) {
          sum += kdata[i - j][1];
        }
        seriesItem.data.push(+(sum / dayArr[k]).toFixed(3));
      }
      seriesTemp.push(seriesItem);
    }
    return seriesTemp;
  }
  
  function calValidDistance(self,distance, chartData, config, opts) {
    var dataChartAreaWidth = opts.width - opts.area[1] - opts.area[3];
    var dataChartWidth = chartData.eachSpacing * (opts.chartData.xAxisData.xAxisPoints.length-1);
    var validDistance = distance;
    if (distance >= 0) {
      validDistance = 0;
  		self.event.trigger('scrollLeft');
    } else if (Math.abs(distance) >= dataChartWidth - dataChartAreaWidth) {
      validDistance = dataChartAreaWidth - dataChartWidth;
  		self.event.trigger('scrollRight');
    }
    return validDistance;
  }
  
  function isInAngleRange(angle, startAngle, endAngle) {
    function adjust(angle) {
      while (angle < 0) {
        angle += 2 * Math.PI;
      }
      while (angle > 2 * Math.PI) {
        angle -= 2 * Math.PI;
      }
      return angle;
    }
    angle = adjust(angle);
    startAngle = adjust(startAngle);
    endAngle = adjust(endAngle);
    if (startAngle > endAngle) {
      endAngle += 2 * Math.PI;
      if (angle < startAngle) {
        angle += 2 * Math.PI;
      }
    }
    return angle >= startAngle && angle <= endAngle;
  }
  
  function calRotateTranslate(x, y, h) {
    var xv = x;
    var yv = h - y;
    var transX = xv + (h - yv - xv) / Math.sqrt(2);
    transX *= -1;
    var transY = (h - yv) * (Math.sqrt(2) - 1) - (h - yv - xv) / Math.sqrt(2);
    return {
      transX: transX,
      transY: transY
    };
  }
  
  function createCurveControlPoints(points, i) {
  
    function isNotMiddlePoint(points, i) {
      if (points[i - 1] && points[i + 1]) {
        return points[i].y >= Math.max(points[i - 1].y, points[i + 1].y) || points[i].y <= Math.min(points[i - 1].y,points[i + 1].y);
      } else {
        return false;
      }
    }
  	function isNotMiddlePointX(points, i) {
  	  if (points[i - 1] && points[i + 1]) {
  	    return points[i].x >= Math.max(points[i - 1].x, points[i + 1].x) || points[i].x <= Math.min(points[i - 1].x,points[i + 1].x);
  	  } else {
  	    return false;
  	  }
  	}
    var a = 0.2;
    var b = 0.2;
    var pAx = null;
    var pAy = null;
    var pBx = null;
    var pBy = null;
    if (i < 1) {
      pAx = points[0].x + (points[1].x - points[0].x) * a;
      pAy = points[0].y + (points[1].y - points[0].y) * a;
    } else {
      pAx = points[i].x + (points[i + 1].x - points[i - 1].x) * a;
      pAy = points[i].y + (points[i + 1].y - points[i - 1].y) * a;
    }
  
    if (i > points.length - 3) {
      var last = points.length - 1;
      pBx = points[last].x - (points[last].x - points[last - 1].x) * b;
      pBy = points[last].y - (points[last].y - points[last - 1].y) * b;
    } else {
      pBx = points[i + 1].x - (points[i + 2].x - points[i].x) * b;
      pBy = points[i + 1].y - (points[i + 2].y - points[i].y) * b;
    }
    if (isNotMiddlePoint(points, i + 1)) {
      pBy = points[i + 1].y;
    }
    if (isNotMiddlePoint(points, i)) {
      pAy = points[i].y;
    }
  	if (isNotMiddlePointX(points, i + 1)) {
  	  pBx = points[i + 1].x;
  	}
  	if (isNotMiddlePointX(points, i)) {
  	  pAx = points[i].x;
  	}
  	if (pAy >= Math.max(points[i].y, points[i + 1].y) || pAy <= Math.min(points[i].y, points[i + 1].y)) {
  	pAy = points[i].y;
  	}
  	if (pBy >= Math.max(points[i].y, points[i + 1].y) || pBy <= Math.min(points[i].y, points[i + 1].y)) {
  	pBy = points[i + 1].y;
  	}
  	if (pAx >= Math.max(points[i].x, points[i + 1].x) || pAx <= Math.min(points[i].x, points[i + 1].x)) {
  	pAx = points[i].x;
  	}
  	if (pBx >= Math.max(points[i].x, points[i + 1].x) || pBx <= Math.min(points[i].x, points[i + 1].x)) {
  	pBx = points[i + 1].x;
  	}
    return {
      ctrA: {
        x: pAx,
        y: pAy
      },
      ctrB: {
        x: pBx,
        y: pBy
      }
    };
  }
  
  function convertCoordinateOrigin(x, y, center) {
    return {
      x: center.x + x,
      y: center.y - y
    };
  }
  
  function avoidCollision(obj, target) {
    if (target) {
      // is collision test
      while (util.isCollision(obj, target)) {
        if (obj.start.x > 0) {
          obj.start.y--;
        } else if (obj.start.x < 0) {
          obj.start.y++;
        } else {
          if (obj.start.y > 0) {
            obj.start.y++;
          } else {
            obj.start.y--;
          }
        }
      }
    }
    return obj;
  }
  
  function fillSeries(series, opts, config) {
    var index = 0;
    return series.map(function(item) {
      if (!item.color) {
        item.color = config.colors[index];
        index = (index + 1) % config.colors.length;
      }
      if (!item.index) {
        item.index = 0;
      }
      if (!item.type) {
        item.type = opts.type;
      }
      if (typeof item.show == "undefined") {
        item.show = true;
      }
      if (!item.type) {
        item.type = opts.type;
      }
      if (!item.pointShape) {
        item.pointShape = "circle";
      }
      if (!item.legendShape) {
        switch (item.type) {
          case 'line':
            item.legendShape = "line";
            break;
          case 'column':
            item.legendShape = "rect";
            break;
          case 'area':
            item.legendShape = "triangle";
            break;
          default:
            item.legendShape = "circle";
        }
      }
      return item;
    });
  }
  
  function getDataRange(minData, maxData) {
    var limit = 0;
    var range = maxData - minData;
    if (range >= 10000) {
      limit = 1000;
    } else if (range >= 1000) {
      limit = 100;
    } else if (range >= 100) {
      limit = 10;
    } else if (range >= 10) {
      limit = 5;
    } else if (range >= 1) {
      limit = 1;
    } else if (range >= 0.1) {
      limit = 0.1;
    } else if (range >= 0.01) {
      limit = 0.01;
    } else if (range >= 0.001) {
      limit = 0.001;
    } else if (range >= 0.0001) {
      limit = 0.0001;
    } else if (range >= 0.00001) {
      limit = 0.00001;
    } else {
      limit = 0.000001;
    }
    return {
      minRange: findRange(minData, 'lower', limit),
      maxRange: findRange(maxData, 'upper', limit)
    };
  }
  
  function measureText(text) {
    var fontSize = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : config.fontSize;
    text = String(text);
    var text = text.split('');
    var width = 0;
    for (let i = 0; i < text.length; i++) {
      let item = text[i];
      if (/[a-zA-Z]/.test(item)) {
        width += 7;
      } else if (/[0-9]/.test(item)) {
        width += 5.5;
      } else if (/\./.test(item)) {
        width += 2.7;
      } else if (/-/.test(item)) {
        width += 3.25;
      } else if (/[\u4e00-\u9fa5]/.test(item)) {
        width += 10;
      } else if (/\(|\)/.test(item)) {
        width += 3.73;
      } else if (/\s/.test(item)) {
        width += 2.5;
      } else if (/%/.test(item)) {
        width += 8;
      } else {
        width += 10;
      }
    }
    return width * fontSize / 10;
  }
  
  function dataCombine(series) {
    return series.reduce(function(a, b) {
      return (a.data ? a.data : a).concat(b.data);
    }, []);
  }
  
  function dataCombineStack(series, len) {
    var sum = new Array(len);
    for (var j = 0; j < sum.length; j++) {
      sum[j] = 0;
    }
    for (var i = 0; i < series.length; i++) {
      for (var j = 0; j < sum.length; j++) {
        sum[j] += series[i].data[j];
      }
    }
    return series.reduce(function(a, b) {
      return (a.data ? a.data : a).concat(b.data).concat(sum);
    }, []);
  }
  
  function getTouches(touches, opts, e) {
    let x, y;
    if (touches.clientX) {
      if (opts.rotate) {
        y = opts.height - touches.clientX * opts.pixelRatio;
        x = (touches.pageY - e.currentTarget.offsetTop - (opts.height / opts.pixelRatio / 2) * (opts.pixelRatio - 1)) *
          opts.pixelRatio;
      } else {
        x = touches.clientX * opts.pixelRatio;
        y = (touches.pageY - e.currentTarget.offsetTop - (opts.height / opts.pixelRatio / 2) * (opts.pixelRatio - 1)) *
          opts.pixelRatio;
      }
    } else {
      if (opts.rotate) {
        y = opts.height - touches.x * opts.pixelRatio;
        x = touches.y * opts.pixelRatio;
      } else {
        x = touches.x * opts.pixelRatio;
        y = touches.y * opts.pixelRatio;
      }
    }
    return {
      x: x,
      y: y
    }
  }
  
  function getSeriesDataItem(series, index) {
    var data = [];
    for (let i = 0; i < series.length; i++) {
      let item = series[i];
      if (item.data[index] !== null && typeof item.data[index] !== 'undefined' && item.show) {
        let seriesItem = {};
        seriesItem.color = item.color;
        seriesItem.type = item.type;
        seriesItem.style = item.style;
        seriesItem.pointShape = item.pointShape;
        seriesItem.disableLegend = item.disableLegend;
        seriesItem.name = item.name;
        seriesItem.show = item.show;
        seriesItem.data = item.format ? item.format(item.data[index]) : item.data[index];
        data.push(seriesItem);
      }
    }
    return data;
  }
  
  function getMaxTextListLength(list) {
    var lengthList = list.map(function(item) {
      return measureText(item);
    });
    return Math.max.apply(null, lengthList);
  }
  
  function getRadarCoordinateSeries(length) {
    var eachAngle = 2 * Math.PI / length;
    var CoordinateSeries = [];
    for (var i = 0; i < length; i++) {
      CoordinateSeries.push(eachAngle * i);
    }
  
    return CoordinateSeries.map(function(item) {
      return -1 * item + Math.PI / 2;
    });
  }
  
  function getToolTipData(seriesData, calPoints, index, categories) {
    var option = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {};
  
    var textList = seriesData.map(function(item) {
  		let titleText=[];
  		if(categories){
  			titleText=categories;
  		}else{
  			titleText=item.data;
  		}
      return {
        text: option.format ? option.format(item, titleText[index]) : item.name + ': ' + item.data,
        color: item.color
      };
    });
    var validCalPoints = [];
    var offset = {
      x: 0,
      y: 0
    };
    for (let i = 0; i < calPoints.length; i++) {
      let points = calPoints[i];
      if (typeof points[index] !== 'undefined' && points[index] !== null) {
        validCalPoints.push(points[index]);
      }
    }
    for (let i = 0; i < validCalPoints.length; i++) {
      let item = validCalPoints[i];
      offset.x = Math.round(item.x);
      offset.y += item.y;
    }
    offset.y /= validCalPoints.length;
    return {
      textList: textList,
      offset: offset
    };
  }
  
  function getMixToolTipData(seriesData, calPoints, index, categories) {
    var option = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {};
    var textList = seriesData.map(function(item) {
      return {
        text: option.format ? option.format(item, categories[index]) : item.name + ': ' + item.data,
        color: item.color,
        disableLegend: item.disableLegend ? true : false
      };
    });
    textList = textList.filter(function(item) {
      if (item.disableLegend !== true) {
        return item;
      }
    });
    var validCalPoints = [];
    var offset = {
      x: 0,
      y: 0
    };
    for (let i = 0; i < calPoints.length; i++) {
      let points = calPoints[i];
      if (typeof points[index] !== 'undefined' && points[index] !== null) {
        validCalPoints.push(points[index]);
      }
    }
    for (let i = 0; i < validCalPoints.length; i++) {
      let item = validCalPoints[i];
      offset.x = Math.round(item.x);
      offset.y += item.y;
    }
    offset.y /= validCalPoints.length;
    return {
      textList: textList,
      offset: offset
    };
  }
  
  function getCandleToolTipData(series, seriesData, calPoints, index, categories, extra) {
    var option = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : {};
    let upColor = extra.color.upFill;
    let downColor = extra.color.downFill;
    //颜色顺序为开盘，收盘，最低，最高
    let color = [upColor, upColor, downColor, upColor];
    var textList = [];
    let text0 = {
      text: categories[index],
      color: null
    };
    textList.push(text0);
    seriesData.map(function(item) {
      if (index == 0) {
        if(item.data[1] - item.data[0] < 0){
        	color[1] = downColor;
        }else{
        	color[1] = upColor;
        }
      } else {
        if (item.data[0] < series[index - 1][1]) {
          color[0] = downColor;
        }
        if (item.data[1] < item.data[0]) {
          color[1] = downColor;
        }
        if (item.data[2] > series[index - 1][1]) {
          color[2] = upColor;
        }
        if (item.data[3] < series[index - 1][1]) {
          color[3] = downColor;
        }
      }
      let text1 = {
        text: '开盘：' + item.data[0],
        color: color[0]
      };
      let text2 = {
        text: '收盘：' + item.data[1],
        color: color[1]
      };
      let text3 = {
        text: '最低：' + item.data[2],
        color: color[2]
      };
      let text4 = {
        text: '最高：' + item.data[3],
        color: color[3]
      };
      textList.push(text1, text2, text3, text4);
    });
    var validCalPoints = [];
    var offset = {
      x: 0,
      y: 0
    };
    for (let i = 0; i < calPoints.length; i++) {
      let points = calPoints[i];
      if (typeof points[index] !== 'undefined' && points[index] !== null) {
        validCalPoints.push(points[index]);
      }
    }
    offset.x = Math.round(validCalPoints[0][0].x);
    return {
      textList: textList,
      offset: offset
    };
  }
  
  function filterSeries(series) {
    let tempSeries = [];
    for (let i = 0; i < series.length; i++) {
      if (series[i].show == true) {
        tempSeries.push(series[i])
      }
    }
    return tempSeries;
  }
  
  function findCurrentIndex(currentPoints, calPoints, opts, config) {
    var offset = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 0;
    var currentIndex = -1;
    var spacing = opts.chartData.eachSpacing/2;
  	let xAxisPoints=[];
  	if(calPoints.length>0){
  		if(opts.type=='candle'){
  			for(let i=0;i<calPoints[0].length;i++){
  				xAxisPoints.push(calPoints[0][i][0].x)
  			}
  		}else{
  			for(let i=0;i<calPoints[0].length;i++){
  				xAxisPoints.push(calPoints[0][i].x)
  			}
  		}
  		if((opts.type=='line' || opts.type=='area') && opts.xAxis.boundaryGap=='justify'){
  		  spacing = opts.chartData.eachSpacing/2;
  		}
  		if(!opts.categories){
  			spacing=0
  		}
  		if (isInExactChartArea(currentPoints, opts, config)) {
  		  xAxisPoints.forEach(function(item, index) {
  		    if (currentPoints.x + offset + spacing > item) {
  		      currentIndex = index;
  		    }
  		  });
  		}
  	}
    return currentIndex;
  }
  
  function findLegendIndex(currentPoints, legendData, opts) {
    let currentIndex = -1;
    if (isInExactLegendArea(currentPoints, legendData.area)) {
      let points = legendData.points;
      let index = -1;
      for (let i = 0, len = points.length; i < len; i++) {
        let item = points[i];
        for (let j = 0; j < item.length; j++) {
          index += 1;
          let area = item[j]['area'];
          if (currentPoints.x > area[0] && currentPoints.x < area[2] && currentPoints.y > area[1] && currentPoints.y < area[3]) {
            currentIndex = index;
            break;
          }
        }
      }
      return currentIndex;
    }
    return currentIndex;
  }
  
  function isInExactLegendArea(currentPoints, area) {
    return currentPoints.x > area.start.x && currentPoints.x < area.end.x && currentPoints.y > area.start.y &&
      currentPoints.y < area.end.y;
  }
  
  function isInExactChartArea(currentPoints, opts, config) {
    return currentPoints.x <= opts.width - opts.area[1] + 10 && currentPoints.x >= opts.area[3] -10 && currentPoints.y >= opts.area[0] && currentPoints.y <= opts.height - opts.area[2];
  }
  
  function findRadarChartCurrentIndex(currentPoints, radarData, count) {
    var eachAngleArea = 2 * Math.PI / count;
    var currentIndex = -1;
    if (isInExactPieChartArea(currentPoints, radarData.center, radarData.radius)) {
      var fixAngle = function fixAngle(angle) {
        if (angle < 0) {
          angle += 2 * Math.PI;
        }
        if (angle > 2 * Math.PI) {
          angle -= 2 * Math.PI;
        }
        return angle;
      };
  
      var angle = Math.atan2(radarData.center.y - currentPoints.y, currentPoints.x - radarData.center.x);
      angle = -1 * angle;
      if (angle < 0) {
        angle += 2 * Math.PI;
      }
  
      var angleList = radarData.angleList.map(function(item) {
        item = fixAngle(-1 * item);
  
        return item;
      });
  
      angleList.forEach(function(item, index) {
        var rangeStart = fixAngle(item - eachAngleArea / 2);
        var rangeEnd = fixAngle(item + eachAngleArea / 2);
        if (rangeEnd < rangeStart) {
          rangeEnd += 2 * Math.PI;
        }
        if (angle >= rangeStart && angle <= rangeEnd || angle + 2 * Math.PI >= rangeStart && angle + 2 * Math.PI <=
          rangeEnd) {
          currentIndex = index;
        }
      });
    }
  
    return currentIndex;
  }
  
  function findFunnelChartCurrentIndex(currentPoints, funnelData) {
    var currentIndex = -1;
    for (var i = 0, len = funnelData.series.length; i < len; i++) {
      var item = funnelData.series[i];
      if (currentPoints.x > item.funnelArea[0] && currentPoints.x < item.funnelArea[2] && currentPoints.y > item.funnelArea[1] && currentPoints.y < item.funnelArea[3]) {
        currentIndex = i;
        break;
      }
    }
    return currentIndex;
  }
  
  function findWordChartCurrentIndex(currentPoints, wordData) {
    var currentIndex = -1;
    for (var i = 0, len = wordData.length; i < len; i++) {
      var item = wordData[i];
      if (currentPoints.x > item.area[0] && currentPoints.x < item.area[2] && currentPoints.y > item.area[1] && currentPoints.y < item.area[3]) {
        currentIndex = i;
        break;
      }
    }
    return currentIndex;
  }
  
  function findMapChartCurrentIndex(currentPoints, opts) {
    var currentIndex = -1;
    var cData=opts.chartData.mapData;
    var data=opts.series;
    var tmp=pointToCoordinate(currentPoints.y, currentPoints.x,cData.bounds,cData.scale,cData.xoffset,cData.yoffset);
    var poi=[tmp.x, tmp.y];
    for (var i = 0, len = data.length; i < len; i++) {
      var item = data[i].geometry.coordinates;
      if(isPoiWithinPoly(poi,item)){
        currentIndex = i;
        break;
      }
    }
    return currentIndex;
  }
  
  function findPieChartCurrentIndex(currentPoints, pieData) {
    var currentIndex = -1;
    if (isInExactPieChartArea(currentPoints, pieData.center, pieData.radius)) {
      var angle = Math.atan2(pieData.center.y - currentPoints.y, currentPoints.x - pieData.center.x);
      angle = -angle;
      for (var i = 0, len = pieData.series.length; i < len; i++) {
        var item = pieData.series[i];
        if (isInAngleRange(angle, item._start_, item._start_ + item._proportion_ * 2 * Math.PI)) {
          currentIndex = i;
          break;
        }
      }
    }
  
    return currentIndex;
  }
  
  function isInExactPieChartArea(currentPoints, center, radius) {
    return Math.pow(currentPoints.x - center.x, 2) + Math.pow(currentPoints.y - center.y, 2) <= Math.pow(radius, 2);
  }
  
  function splitPoints(points) {
    var newPoints = [];
    var items = [];
    points.forEach(function(item, index) {
      if (item !== null) {
        items.push(item);
      } else {
        if (items.length) {
          newPoints.push(items);
        }
        items = [];
      }
    });
    if (items.length) {
      newPoints.push(items);
    }
  
    return newPoints;
  }
  
  function calLegendData(series, opts, config, chartData) {
    let legendData = {
      area: {
        start: {
          x: 0,
          y: 0
        },
        end: {
          x: 0,
          y: 0
        },
        width: 0,
        height: 0,
        wholeWidth: 0,
        wholeHeight: 0
      },
      points: [],
      widthArr: [],
      heightArr: []
    };
    if (opts.legend.show === false) {
      chartData.legendData = legendData;
      return legendData;
    }
  
    let padding = opts.legend.padding;
    let margin = opts.legend.margin;
    let fontSize = opts.legend.fontSize;
    let shapeWidth = 15 * opts.pixelRatio;
    let shapeRight = 5 * opts.pixelRatio;
    let lineHeight = Math.max(opts.legend.lineHeight * opts.pixelRatio, fontSize);
    if (opts.legend.position == 'top' || opts.legend.position == 'bottom') {
      let legendList = [];
      let widthCount = 0;
      let widthCountArr = [];
      let currentRow = [];
      for (let i = 0; i < series.length; i++) {
        let item = series[i];
        let itemWidth = shapeWidth + shapeRight + measureText(item.name || 'undefined', fontSize) + opts.legend.itemGap;
        if (widthCount + itemWidth > opts.width - opts.padding[1] - opts.padding[3]) {
          legendList.push(currentRow);
          widthCountArr.push(widthCount - opts.legend.itemGap);
          widthCount = itemWidth;
          currentRow = [item];
        } else {
          widthCount += itemWidth;
          currentRow.push(item);
        }
      }
      if (currentRow.length) {
        legendList.push(currentRow);
        widthCountArr.push(widthCount - opts.legend.itemGap);
        legendData.widthArr = widthCountArr;
        let legendWidth = Math.max.apply(null, widthCountArr);
        switch (opts.legend.float) {
          case 'left':
            legendData.area.start.x = opts.padding[3];
            legendData.area.end.x = opts.padding[3] + 2 * padding;
            break;
          case 'right':
            legendData.area.start.x = opts.width - opts.padding[1] - legendWidth - 2 * padding;
            legendData.area.end.x = opts.width - opts.padding[1];
            break;
          default:
            legendData.area.start.x = (opts.width - legendWidth) / 2 - padding;
            legendData.area.end.x = (opts.width + legendWidth) / 2 + padding;
        }
        legendData.area.width = legendWidth + 2 * padding;
        legendData.area.wholeWidth = legendWidth + 2 * padding;
        legendData.area.height = legendList.length * lineHeight + 2 * padding;
        legendData.area.wholeHeight = legendList.length * lineHeight + 2 * padding + 2 * margin;
        legendData.points = legendList;
      }
    } else {
      let len = series.length;
      let maxHeight = opts.height - opts.padding[0] - opts.padding[2] - 2 * margin - 2 * padding;
      let maxLength = Math.min(Math.floor(maxHeight / lineHeight), len);
      legendData.area.height = maxLength * lineHeight + padding * 2;
      legendData.area.wholeHeight = maxLength * lineHeight + padding * 2;
      switch (opts.legend.float) {
        case 'top':
          legendData.area.start.y = opts.padding[0] + margin;
          legendData.area.end.y = opts.padding[0] + margin + legendData.area.height;
          break;
        case 'bottom':
          legendData.area.start.y = opts.height - opts.padding[2] - margin - legendData.area.height;
          legendData.area.end.y = opts.height - opts.padding[2] - margin;
          break;
        default:
          legendData.area.start.y = (opts.height - legendData.area.height) / 2;
          legendData.area.end.y = (opts.height + legendData.area.height) / 2;
      }
      let lineNum = len % maxLength === 0 ? len / maxLength : Math.floor((len / maxLength) + 1);
      let currentRow = [];
      for (let i = 0; i < lineNum; i++) {
        let temp = series.slice(i * maxLength, i * maxLength + maxLength);
        currentRow.push(temp);
      }
  
      legendData.points = currentRow;
  
      if (currentRow.length) {
        for (let i = 0; i < currentRow.length; i++) {
          let item = currentRow[i];
          let maxWidth = 0;
          for (let j = 0; j < item.length; j++) {
            let itemWidth = shapeWidth + shapeRight + measureText(item[j].name || 'undefined', fontSize) + opts.legend.itemGap;
            if (itemWidth > maxWidth) {
              maxWidth = itemWidth;
            }
          }
          legendData.widthArr.push(maxWidth);
          legendData.heightArr.push(item.length * lineHeight + padding * 2);
        }
        let legendWidth = 0
        for (let i = 0; i < legendData.widthArr.length; i++) {
          legendWidth += legendData.widthArr[i];
        }
        legendData.area.width = legendWidth - opts.legend.itemGap + 2 * padding;
        legendData.area.wholeWidth = legendData.area.width + padding;
      }
    }
  
    switch (opts.legend.position) {
      case 'top':
        legendData.area.start.y = opts.padding[0] + margin;
        legendData.area.end.y = opts.padding[0] + margin + legendData.area.height;
        break;
      case 'bottom':
        legendData.area.start.y = opts.height - opts.padding[2] - legendData.area.height - margin;
        legendData.area.end.y = opts.height - opts.padding[2] - margin;
        break;
      case 'left':
        legendData.area.start.x = opts.padding[3];
        legendData.area.end.x = opts.padding[3] + legendData.area.width;
        break;
      case 'right':
        legendData.area.start.x = opts.width - opts.padding[1] - legendData.area.width;
        legendData.area.end.x = opts.width - opts.padding[1];
        break;
    }
    chartData.legendData = legendData;
    return legendData;
  }
  
  function calCategoriesData(categories, opts, config, eachSpacing) {
    var result = {
      angle: 0,
      xAxisHeight: config.xAxisHeight
    };
    var categoriesTextLenth = categories.map(function(item) {
      return measureText(item,opts.xAxis.fontSize||config.fontSize);
    });
    var maxTextLength = Math.max.apply(this, categoriesTextLenth);
  
    if (opts.xAxis.rotateLabel == true && maxTextLength + 2 * config.xAxisTextPadding > eachSpacing) {
      result.angle = 45 * Math.PI / 180;
      result.xAxisHeight = 2 * config.xAxisTextPadding + maxTextLength * Math.sin(result.angle);
    }
    return result;
  }
  
  function getXAxisTextList(series, opts, config) {
    var index = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : -1;
    var data = dataCombine(series);
    var sorted = [];
    // remove null from data
    data = data.filter(function(item) {
      //return item !== null;
      if (typeof item === 'object' && item !== null) {
        if (item.constructor.toString().indexOf('Array')>-1) {
          return item !== null;
        } else {
          return item.value !== null;
        }
      } else {
        return item !== null;
      }
    });
    data.map(function(item) {
      if (typeof item === 'object') {
        if (item.constructor.toString().indexOf('Array')>-1) {
  				if(opts.type=='candle'){
  					item.map(function(subitem) {
  					  sorted.push(subitem);
  					})
  				}else{
  					sorted.push(item[0]);
  				}
        } else {
          sorted.push(item.value);
        }
      } else {
        sorted.push(item);
      }
    })
  	
    var minData = 0;
    var maxData = 0;
    if (sorted.length > 0) {
      minData = Math.min.apply(this, sorted);
      maxData = Math.max.apply(this, sorted);
    }
    //为了兼容v1.9.0之前的项目
    if(index>-1){
      if (typeof opts.xAxis.data[index].min === 'number') {
        minData = Math.min(opts.xAxis.data[index].min, minData);
      }
      if (typeof opts.xAxis.data[index].max === 'number') {
        maxData = Math.max(opts.xAxis.data[index].max, maxData);
      }
    }else{
      if (typeof opts.xAxis.min === 'number') {
        minData = Math.min(opts.xAxis.min, minData);
      }
      if (typeof opts.xAxis.max === 'number') {
        maxData = Math.max(opts.xAxis.max, maxData);
      }
    }
    
  
    if (minData === maxData) {
      var rangeSpan = maxData || 10;
      maxData += rangeSpan;
    }
  
    //var dataRange = getDataRange(minData, maxData);
    var minRange = minData;
    var maxRange = maxData;
  
    var range = [];
    var eachRange = (maxRange - minRange) / opts.xAxis.splitNumber;
  
    for (var i = 0; i <= opts.xAxis.splitNumber; i++) {
      range.push(minRange + eachRange * i);
    }
    return range;
  }
  
  function calXAxisData(series, opts, config){
      var result = {
          angle: 0,
          xAxisHeight: config.xAxisHeight
      };
  
      result.ranges = getXAxisTextList(series, opts, config);
      result.rangesFormat = result.ranges.map(function(item){
          item = opts.xAxis.format? opts.xAxis.format(item):util.toFixed(item, 2);
          return item;
      });
  		
      var xAxisScaleValues = result.ranges.map(function (item) {
          // 如果刻度值是浮点数,则保留两位小数
          item = util.toFixed(item, 2);
          // 若有自定义格式则调用自定义的格式化函数
          item = opts.xAxis.format ? opts.xAxis.format(Number(item)) : item;
          return item;
      });
  
      result = Object.assign(result,getXAxisPoints(xAxisScaleValues, opts, config));
      // 计算X轴刻度的属性譬如每个刻度的间隔,刻度的起始点\结束点以及总长
      var eachSpacing = result.eachSpacing;
  
      var textLength = xAxisScaleValues.map(function (item) {
          return measureText(item);
      });
      
      // get max length of categories text
      var maxTextLength = Math.max.apply(this, textLength);
  
      // 如果刻度值文本内容过长,则将其逆时针旋转45°
      if (maxTextLength + 2 * config.xAxisTextPadding > eachSpacing) {
          result.angle = 45 * Math.PI / 180;
          result.xAxisHeight = 2 * config.xAxisTextPadding + maxTextLength * Math.sin(result.angle);
      }
  
      if (opts.xAxis.disabled === true) {
          result.xAxisHeight = 0;
      }
  
      return result;
  }
  
  function getRadarDataPoints(angleList, center, radius, series, opts) {
    var process = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 1;
  
    var radarOption = opts.extra.radar || {};
    radarOption.max = radarOption.max || 0;
    var maxData = Math.max(radarOption.max, Math.max.apply(null, dataCombine(series)));
  
    var data = [];
    for (let i = 0; i < series.length; i++) {
      let each = series[i];
      let listItem = {};
      listItem.color = each.color;
  		listItem.legendShape = each.legendShape;
  		listItem.pointShape = each.pointShape;
      listItem.data = [];
      each.data.forEach(function(item, index) {
        let tmp = {};
        tmp.angle = angleList[index];
  
        tmp.proportion = item / maxData;
        tmp.position = convertCoordinateOrigin(radius * tmp.proportion * process * Math.cos(tmp.angle), radius * tmp.proportion *
          process * Math.sin(tmp.angle), center);
        listItem.data.push(tmp);
      });
  
      data.push(listItem);
    }
  
    return data;
  }
  
  function getPieDataPoints(series, radius) {
    var process = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1;
  
    var count = 0;
    var _start_ = 0;
    for (let i = 0; i < series.length; i++) {
      let item = series[i];
      item.data = item.data === null ? 0 : item.data;
      count += item.data;
    }
    for (let i = 0; i < series.length; i++) {
      let item = series[i];
      item.data = item.data === null ? 0 : item.data;
      if (count === 0) {
        item._proportion_ = 1 / series.length * process;
      } else {
        item._proportion_ = item.data / count * process;
      }
      item._radius_ = radius;
    }
    for (let i = 0; i < series.length; i++) {
      let item = series[i];
      item._start_ = _start_;
      _start_ += 2 * item._proportion_ * Math.PI;
    }
  
    return series;
  }
  
  function getFunnelDataPoints(series, radius) {
    var process = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1;
    series = series.sort(function(a,b){return parseInt(b.data)-parseInt(a.data);});
    for (let i = 0; i < series.length; i++) {
      series[i].radius = series[i].data/series[0].data*radius*process;
      series[i]._proportion_ = series[i].data/series[0].data;
    }
    return series.reverse();
  }
  
  function getRoseDataPoints(series, type, minRadius, radius) {
    var process = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 1;
    var count = 0;
    var _start_ = 0;
  
    var dataArr = [];
    for (let i = 0; i < series.length; i++) {
      let item = series[i];
      item.data = item.data === null ? 0 : item.data;
      count += item.data;
      dataArr.push(item.data);
    }
    
    var minData = Math.min.apply(null, dataArr);
    var maxData = Math.max.apply(null, dataArr);
    var radiusLength = radius - minRadius;
    
    for (let i = 0; i < series.length; i++) {
      let item = series[i];
      item.data = item.data === null ? 0 : item.data;
      if (count === 0 || type == 'area') {
        item._proportion_ = item.data / count * process;
        item._rose_proportion_ = 1 / series.length * process;
      } else {
        item._proportion_ = item.data / count * process;
        item._rose_proportion_ = item.data / count * process;
      }
      item._radius_ = minRadius + radiusLength * ((item.data - minData) / (maxData - minData));
    }
    for (let i = 0; i < series.length; i++) {
      let item = series[i];
      item._start_ = _start_;
      _start_ += 2 * item._rose_proportion_ * Math.PI;
    }
  
    return series;
  }
  
  function getArcbarDataPoints(series, arcbarOption) {
    var process = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1;
    if (process == 1) {
      process = 0.999999;
    }
    for (let i = 0; i < series.length; i++) {
      let item = series[i];
      item.data = item.data === null ? 0 : item.data;
      let totalAngle;
      if (arcbarOption.type == 'circle') {
        totalAngle = 2;
      } else {
  			if (arcbarOption.endAngle < arcbarOption.startAngle) {
  			  totalAngle = 2 + arcbarOption.endAngle - arcbarOption.startAngle;
  			} else{
  			  totalAngle = arcbarOption.startAngle - arcbarOption.endAngle;
  			}
      }
      item._proportion_ = totalAngle * item.data * process + arcbarOption.startAngle;
      if (item._proportion_ >= 2) {
        item._proportion_ = item._proportion_ % 2;
      }
    }
    return series;
  }
  
  function getGaugeAxisPoints(categories, startAngle, endAngle) {
    let totalAngle = startAngle - endAngle + 1;
    let tempStartAngle = startAngle;
    for (let i = 0; i < categories.length; i++) {
      categories[i].value = categories[i].value === null ? 0 : categories[i].value;
      categories[i]._startAngle_ = tempStartAngle;
      categories[i]._endAngle_ = totalAngle * categories[i].value + startAngle;
      if (categories[i]._endAngle_ >= 2) {
        categories[i]._endAngle_ = categories[i]._endAngle_ % 2;
      }
      tempStartAngle = categories[i]._endAngle_;
    }
    return categories;
  }
  
  function getGaugeDataPoints(series, categories, gaugeOption) {
    let process = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 1;
    for (let i = 0; i < series.length; i++) {
      let item = series[i];
      item.data = item.data === null ? 0 : item.data;
      if (gaugeOption.pointer.color == 'auto') {
        for (let i = 0; i < categories.length; i++) {
          if (item.data <= categories[i].value) {
            item.color = categories[i].color;
            break;
          }
        }
      } else {
        item.color = gaugeOption.pointer.color;
      }
      let totalAngle = gaugeOption.startAngle - gaugeOption.endAngle + 1;
      item._endAngle_ = totalAngle * item.data + gaugeOption.startAngle;
      item._oldAngle_ = gaugeOption.oldAngle;
      if (gaugeOption.oldAngle < gaugeOption.endAngle) {
        item._oldAngle_ += 2;
      }
      if (item.data >= gaugeOption.oldData) {
        item._proportion_ = (item._endAngle_ - item._oldAngle_) * process + gaugeOption.oldAngle;
      } else {
        item._proportion_ = item._oldAngle_ - (item._oldAngle_ - item._endAngle_) * process;
      }
      if (item._proportion_ >= 2) {
        item._proportion_ = item._proportion_ % 2;
      }
    }
    return series;
  }
  
  function getPieTextMaxLength(series) {
    series = getPieDataPoints(series);
    let maxLength = 0;
    for (let i = 0; i < series.length; i++) {
      let item = series[i];
      let text = item.format ? item.format(+item._proportion_.toFixed(2)) : util.toFixed(item._proportion_ * 100) + '%';
      maxLength = Math.max(maxLength, measureText(text));
    }
  
    return maxLength;
  }
  
  function fixColumeData(points, eachSpacing, columnLen, index, config, opts) {
    return points.map(function(item) {
      if (item === null) {
        return null;
      }
      item.width = Math.ceil((eachSpacing - 2 * config.columePadding) / columnLen);
  
      if (opts.extra.column && opts.extra.column.width && +opts.extra.column.width > 0) {
        item.width = Math.min(item.width, +opts.extra.column.width);
      }
      if (item.width <= 0) {
        item.width = 1;
      }
      item.x += (index + 0.5 - columnLen / 2) * item.width;
      return item;
    });
  }
  
  function fixColumeMeterData(points, eachSpacing, columnLen, index, config, opts, border) {
    return points.map(function(item) {
      if (item === null) {
        return null;
      }
      item.width = Math.ceil((eachSpacing - 2 * config.columePadding) / 2);
  
      if (opts.extra.column && opts.extra.column.width && +opts.extra.column.width > 0) {
        item.width = Math.min(item.width, +opts.extra.column.width);
      }
  
      if (index > 0) {
        item.width -= 2 * border;
      }
      return item;
    });
  }
  
  function fixColumeStackData(points, eachSpacing, columnLen, index, config, opts, series) {
  
    return points.map(function(item, indexn) {
  
      if (item === null) {
        return null;
      }
      item.width = Math.ceil((eachSpacing - 2 * config.columePadding) / 2);
  
      if (opts.extra.column && opts.extra.column.width && +opts.extra.column.width > 0) {
        item.width = Math.min(item.width, +opts.extra.column.width);
      }
      return item;
    });
  }
  
  function getXAxisPoints(categories, opts, config) {
    var spacingValid = opts.width - opts.area[1] - opts.area[3];
    var dataCount = opts.enableScroll ? Math.min(opts.xAxis.itemCount, categories.length) : categories.length;
    if((opts.type=='line' || opts.type=='area') && dataCount>1 && opts.xAxis.boundaryGap=='justify'){
      dataCount -=1;
    }
    var eachSpacing = spacingValid / dataCount;
  
    var xAxisPoints = [];
    var startX = opts.area[3];
    var endX = opts.width - opts.area[1];
    categories.forEach(function(item, index) {
      xAxisPoints.push(startX + index * eachSpacing);
    });
    if(opts.xAxis.boundaryGap !=='justify'){
      if (opts.enableScroll === true) {
        xAxisPoints.push(startX + categories.length * eachSpacing);
      } else {
        xAxisPoints.push(endX);
      }
    }
    return {
      xAxisPoints: xAxisPoints,
      startX: startX,
      endX: endX,
      eachSpacing: eachSpacing
    };
  }
  
  function getCandleDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config) {
    var process = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : 1;
    var points = [];
    var validHeight = opts.height - opts.area[0] - opts.area[2];
    data.forEach(function(item, index) {
      if (item === null) {
        points.push(null);
      } else {
        var cPoints = [];
        item.forEach(function(items, indexs) {
          var point = {};
          point.x = xAxisPoints[index] + Math.round(eachSpacing / 2);
          var value = items.value || items;
          var height = validHeight * (value - minRange) / (maxRange - minRange);
          height *= process;
          point.y = opts.height - Math.round(height) - opts.area[2];
          cPoints.push(point);
        });
        points.push(cPoints);
      }
    });
  
    return points;
  }
  
  function getDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config) {
    var process = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : 1;
    var boundaryGap='center';
    if (opts.type == 'line'||opts.type == 'area'){
      boundaryGap=opts.xAxis.boundaryGap;
    }
    var points = [];
    var validHeight = opts.height - opts.area[0] - opts.area[2];
  	var validWidth = opts.width - opts.area[1] - opts.area[3];
    data.forEach(function(item, index) {
      if (item === null) {
        points.push(null);
      } else {
        var point = {};
        point.color = item.color;
        point.x = xAxisPoints[index];
        var value = item;
        if (typeof item === 'object' && item !== null) {
  				if (item.constructor.toString().indexOf('Array')>-1) {
  					let xranges,xminRange,xmaxRange;
  					xranges = [].concat(opts.chartData.xAxisData.ranges);
  					xminRange = xranges.shift();
  					xmaxRange = xranges.pop();
  				  value = item[1];
  					point.x = opts.area[3]+ validWidth * (item[0] - xminRange) / (xmaxRange - xminRange);
  				} else {
  				  value = item.value;
  				}
        }
  			if(boundaryGap=='center'){
  			  point.x += Math.round(eachSpacing / 2);
  			}
        var height = validHeight * (value - minRange) / (maxRange - minRange);
        height *= process;
        point.y = opts.height - Math.round(height) - opts.area[2];
        points.push(point);
      }
    });
  
    return points;
  }
  
  function getStackDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, seriesIndex, stackSeries) {
    var process = arguments.length > 9 && arguments[9] !== undefined ? arguments[9] : 1;
    var points = [];
    var validHeight = opts.height - opts.area[0] - opts.area[2];
  
    data.forEach(function(item, index) {
      if (item === null) {
        points.push(null);
      } else {
        var point = {};
        point.color = item.color;
        point.x = xAxisPoints[index] + Math.round(eachSpacing / 2);
  
        if (seriesIndex > 0) {
          var value = 0;
          for (let i = 0; i <= seriesIndex; i++) {
            value += stackSeries[i].data[index];
          }
          var value0 = value - item;
          var height = validHeight * (value - minRange) / (maxRange - minRange);
          var height0 = validHeight * (value0 - minRange) / (maxRange - minRange);
        } else {
          var value = item;
          var height = validHeight * (value - minRange) / (maxRange - minRange);
          var height0 = 0;
        }
        var heightc = height0;
        height *= process;
        heightc *= process;
        point.y = opts.height - Math.round(height) - opts.area[2];
        point.y0 = opts.height - Math.round(heightc) - opts.area[2];
        points.push(point);
      }
    });
  
    return points;
  }
  
  function getYAxisTextList(series, opts, config, stack) {
    var index = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : -1;
    var data;
    if (stack == 'stack') {
      data = dataCombineStack(series, opts.categories.length);
    } else {
      data = dataCombine(series);
    }
    var sorted = [];
    // remove null from data
    data = data.filter(function(item) {
      //return item !== null;
      if (typeof item === 'object' && item !== null) {
        if (item.constructor.toString().indexOf('Array')>-1) {
          return item !== null;
        } else {
          return item.value !== null;
        }
      } else {
        return item !== null;
      }
    });
    data.map(function(item) {
      if (typeof item === 'object') {
        if (item.constructor.toString().indexOf('Array')>-1) {
  				if(opts.type=='candle'){
  					item.map(function(subitem) {
  					  sorted.push(subitem);
  					})
  				}else{
  					sorted.push(item[1]);
  				}
        } else {
          sorted.push(item.value);
        }
      } else {
        sorted.push(item);
      }
    })
  	
    var minData = 0;
    var maxData = 0;
    if (sorted.length > 0) {
      minData = Math.min.apply(this, sorted);
      maxData = Math.max.apply(this, sorted);
    }
    //为了兼容v1.9.0之前的项目
    if(index>-1){
      if (typeof opts.yAxis.data[index].min === 'number') {
        minData = Math.min(opts.yAxis.data[index].min, minData);
      }
      if (typeof opts.yAxis.data[index].max === 'number') {
        maxData = Math.max(opts.yAxis.data[index].max, maxData);
      }
    }else{
      if (typeof opts.yAxis.min === 'number') {
        minData = Math.min(opts.yAxis.min, minData);
      }
      if (typeof opts.yAxis.max === 'number') {
        maxData = Math.max(opts.yAxis.max, maxData);
      }
    }
    
  
    if (minData === maxData) {
      var rangeSpan = maxData || 10;
      maxData += rangeSpan;
    }
  
    var dataRange = getDataRange(minData, maxData);
    var minRange = dataRange.minRange;
    var maxRange = dataRange.maxRange;
  
    var range = [];
    var eachRange = (maxRange - minRange) / opts.yAxis.splitNumber;
  
    for (var i = 0; i <= opts.yAxis.splitNumber; i++) {
      range.push(minRange + eachRange * i);
    }
    return range.reverse();
  }
  
  function calYAxisData(series, opts, config) {
    //堆叠图重算Y轴
    var columnstyle = assign({}, {
      type: ""
    }, opts.extra.column);
    //如果是多Y轴，重新计算
    var YLength = opts.yAxis.data.length;
    var newSeries=new Array(YLength);
    if(YLength>0){
      for(let i=0;i<YLength;i++){
        newSeries[i]=[];
        for(let j=0;j<series.length;j++){
          if(series[j].index == i){
            newSeries[i].push(series[j]);
          }
        }
      }
      var rangesArr =new Array(YLength);
      var rangesFormatArr = new Array(YLength);
      var yAxisWidthArr =new Array(YLength);
  		
      for(let i=0;i<YLength;i++){
        let yData = opts.yAxis.data[i];
  			//如果总开关不显示，强制每个Y轴为不显示
  			if(opts.yAxis.disabled == true){
  				yData.disabled = true;
  			}
  			rangesArr[i]=getYAxisTextList(newSeries[i], opts, config, columnstyle.type,i);
  			let yAxisFontSizes = yData.fontSize || config.fontSize;
  			yAxisWidthArr[i] = {position:yData.position?yData.position:'left',width:0};
  			rangesFormatArr[i]= rangesArr[i].map(function(items) {
  				items = util.toFixed(items, 6);
  				items = yData.format ? yData.format(Number(items)) : items;
  				yAxisWidthArr[i].width = Math.max(yAxisWidthArr[i].width, measureText(items, yAxisFontSizes) + 5);
  				return items;
  			});
  			let calibration= yData.calibration? 4*opts.pixelRatio : 0 ;
  			yAxisWidthArr[i].width += calibration +3*opts.pixelRatio;
        if (yData.disabled === true) {
          yAxisWidthArr[i].width=0;
        }
      }
      
    }else{
      var rangesArr =new Array(1);
      var rangesFormatArr = new Array(1);
      var yAxisWidthArr =new Array(1);
  		rangesArr[0] = getYAxisTextList(series, opts, config, columnstyle.type);
  		yAxisWidthArr[0] = {position:'left',width:0};
  		var yAxisFontSize = opts.yAxis.fontSize || config.fontSize;
  		rangesFormatArr[0] = rangesArr[0].map(function(item) {
  			item = util.toFixed(item, 6);
  			item = opts.yAxis.format ? opts.yAxis.format(Number(item)) : item;
  			yAxisWidthArr[0].width = Math.max(yAxisWidthArr[0].width, measureText(item, yAxisFontSize) + 5);
  			return item;
  		});
  		yAxisWidthArr[0].width += 3*opts.pixelRatio;
  		if (opts.yAxis.disabled === true) {
  		  yAxisWidthArr[0] = {position:'left',width:0};
  		  opts.yAxis.data[0]={disabled:true};
  		}else{
  			opts.yAxis.data[0]={disabled:false,position:'left',max:opts.yAxis.max,min:opts.yAxis.min,format:opts.yAxis.format};
  		}
      
    }
  
    return {
      rangesFormat: rangesFormatArr,
      ranges: rangesArr,
      yAxisWidth: yAxisWidthArr
    };
    
  }
  
  function calTooltipYAxisData(point, series, opts, config, eachSpacing) {
    let ranges = [].concat(opts.chartData.yAxisData.ranges);
    let spacingValid = opts.height - opts.area[0] - opts.area[2];
    let minAxis = opts.area[0];
    let items=[];
    for(let i=0;i<ranges.length;i++){
      let maxVal = ranges[i].shift();
      let minVal = ranges[i].pop();
      let item = maxVal - (maxVal - minVal) * (point - minAxis) / spacingValid;
      item = opts.yAxis.data[i].format ? opts.yAxis.data[i].format(Number(item)) : item.toFixed(0);
      items.push(String(item))
    }
    return items;
  }
  
  function calMarkLineData(points, opts) {
    let minRange, maxRange;
    let spacingValid = opts.height - opts.area[0] - opts.area[2];
    for (let i = 0; i < points.length; i++) {
      points[i].yAxisIndex = points[i].yAxisIndex ? points[i].yAxisIndex:0;
      let range = [].concat(opts.chartData.yAxisData.ranges[points[i].yAxisIndex]);
      minRange = range.pop();
      maxRange = range.shift();
      let height = spacingValid * (points[i].value - minRange) / (maxRange - minRange);
      points[i].y = opts.height - Math.round(height) - opts.area[2];
    }
    return points;
  }
  
  function contextRotate(context, opts) {
    if (opts.rotateLock !== true) {
      context.translate(opts.height, 0);
      context.rotate(90 * Math.PI / 180);
    } else if (opts._rotate_ !== true) {
      context.translate(opts.height, 0);
      context.rotate(90 * Math.PI / 180);
      opts._rotate_ = true;
    }
  }
  
  function drawPointShape(points, color, shape, context, opts) {
    context.beginPath();
  	if(opts.dataPointShapeType == 'hollow'){
  		context.setStrokeStyle(color);
  		context.setFillStyle(opts.background);
  		context.setLineWidth(2 * opts.pixelRatio);
  	}else{
  		context.setStrokeStyle("#ffffff");
  		context.setFillStyle(color);
  		context.setLineWidth(1 * opts.pixelRatio);
  	}
    if (shape === 'diamond') {
      points.forEach(function(item, index) {
        if (item !== null) {
          context.moveTo(item.x, item.y - 4.5);
          context.lineTo(item.x - 4.5, item.y);
          context.lineTo(item.x, item.y + 4.5);
          context.lineTo(item.x + 4.5, item.y);
          context.lineTo(item.x, item.y - 4.5);
        }
      });
    } else if (shape === 'circle') {
      points.forEach(function(item, index) {
        if (item !== null) {
          context.moveTo(item.x + 2.5 * opts.pixelRatio, item.y);
          context.arc(item.x, item.y, 3 * opts.pixelRatio, 0, 2 * Math.PI, false);
        }
      });
    } else if (shape === 'rect') {
      points.forEach(function(item, index) {
        if (item !== null) {
          context.moveTo(item.x - 3.5, item.y - 3.5);
          context.rect(item.x - 3.5, item.y - 3.5, 7, 7);
        }
      });
    } else if (shape === 'triangle') {
      points.forEach(function(item, index) {
        if (item !== null) {
          context.moveTo(item.x, item.y - 4.5);
          context.lineTo(item.x - 4.5, item.y + 4.5);
          context.lineTo(item.x + 4.5, item.y + 4.5);
          context.lineTo(item.x, item.y - 4.5);
        }
      });
    }
    context.closePath();
    context.fill();
    context.stroke();
  }
  
  function drawRingTitle(opts, config, context, center) {
    var titlefontSize = opts.title.fontSize || config.titleFontSize;
    var subtitlefontSize = opts.subtitle.fontSize || config.subtitleFontSize;
    var title = opts.title.name || '';
    var subtitle = opts.subtitle.name || '';
    var titleFontColor = opts.title.color || config.titleColor;
    var subtitleFontColor = opts.subtitle.color || config.subtitleColor;
    var titleHeight = title ? titlefontSize : 0;
    var subtitleHeight = subtitle ? subtitlefontSize : 0;
    var margin = 5;
  
    if (subtitle) {
      var textWidth = measureText(subtitle, subtitlefontSize);
      var startX = center.x - textWidth / 2 + (opts.subtitle.offsetX || 0);
      var startY = center.y + subtitlefontSize / 2 + (opts.subtitle.offsetY || 0);
      if (title) {
        startY += (titleHeight + margin) / 2;
      }
      context.beginPath();
      context.setFontSize(subtitlefontSize);
      context.setFillStyle(subtitleFontColor);
      context.fillText(subtitle, startX, startY);
      context.closePath();
      context.stroke();
    }
    if (title) {
      var _textWidth = measureText(title, titlefontSize);
      var _startX = center.x - _textWidth / 2 + (opts.title.offsetX || 0);
      var _startY = center.y + titlefontSize / 2 + (opts.title.offsetY || 0);
      if (subtitle) {
        _startY -= (subtitleHeight + margin) / 2;
      }
      context.beginPath();
      context.setFontSize(titlefontSize);
      context.setFillStyle(titleFontColor);
      context.fillText(title, _startX, _startY);
      context.closePath();
      context.stroke();
    }
  }
  
  function drawPointText(points, series, config, context) {
    // 绘制数据文案
    var data = series.data;
    points.forEach(function(item, index) {
      if (item !== null) {
        //var formatVal = series.format ? series.format(data[index]) : data[index];
        context.beginPath();
        context.setFontSize(series.textSize || config.fontSize);
        context.setFillStyle(series.textColor || '#666666');
        var value = data[index]
        if (typeof data[index] === 'object' && data[index] !== null) {
  				if (data[index].constructor == Array) {
  					value = data[index][1];
  				}else{
  					value = data[index].value
  				}
        }
        var formatVal = series.format ? series.format(value) : value;
        context.fillText(String(formatVal), item.x - measureText(formatVal, series.textSize || config.fontSize) / 2, item.y -4);
        context.closePath();
        context.stroke();
      }
    });
  
  }
  
  function drawGaugeLabel(gaugeOption, radius, centerPosition, opts, config, context) {
    radius -= gaugeOption.width / 2 + config.gaugeLabelTextMargin;
  
    let totalAngle = gaugeOption.startAngle - gaugeOption.endAngle + 1;
    let splitAngle = totalAngle / gaugeOption.splitLine.splitNumber;
    let totalNumber = gaugeOption.endNumber - gaugeOption.startNumber;
    let splitNumber = totalNumber / gaugeOption.splitLine.splitNumber;
    let nowAngle = gaugeOption.startAngle;
    let nowNumber = gaugeOption.startNumber;
    for (let i = 0; i < gaugeOption.splitLine.splitNumber + 1; i++) {
      var pos = {
        x: radius * Math.cos(nowAngle * Math.PI),
        y: radius * Math.sin(nowAngle * Math.PI)
      };
      var labelText = gaugeOption.labelFormat ? gaugeOption.labelFormat(nowNumber) : nowNumber;
      pos.x += centerPosition.x - measureText(labelText) / 2;
      pos.y += centerPosition.y;
      var startX = pos.x;
      var startY = pos.y;
      context.beginPath();
      context.setFontSize(config.fontSize);
      context.setFillStyle(gaugeOption.labelColor || '#666666');
      context.fillText(labelText, startX, startY + config.fontSize / 2);
      context.closePath();
      context.stroke();
  
      nowAngle += splitAngle;
      if (nowAngle >= 2) {
        nowAngle = nowAngle % 2;
      }
      nowNumber += splitNumber;
    }
  
  }
  
  function drawRadarLabel(angleList, radius, centerPosition, opts, config, context) {
    var radarOption = opts.extra.radar || {};
    radius += config.radarLabelTextMargin;
  
    angleList.forEach(function(angle, index) {
      var pos = {
        x: radius * Math.cos(angle),
        y: radius * Math.sin(angle)
      };
      var posRelativeCanvas = convertCoordinateOrigin(pos.x, pos.y, centerPosition);
      var startX = posRelativeCanvas.x;
      var startY = posRelativeCanvas.y;
      if (util.approximatelyEqual(pos.x, 0)) {
        startX -= measureText(opts.categories[index] || '') / 2;
      } else if (pos.x < 0) {
        startX -= measureText(opts.categories[index] || '');
      }
      context.beginPath();
      context.setFontSize(config.fontSize);
      context.setFillStyle(radarOption.labelColor || '#666666');
      context.fillText(opts.categories[index] || '', startX, startY + config.fontSize / 2);
      context.closePath();
      context.stroke();
    });
  
  }
  
  function drawPieText(series, opts, config, context, radius, center) {
    var lineRadius = config.pieChartLinePadding;
    var textObjectCollection = [];
    var lastTextObject = null;
  
    var seriesConvert = series.map(function(item) {
      var text = item.format ? item.format(+item._proportion_.toFixed(2)) : util.toFixed(item._proportion_.toFixed(4) * 100) +'%';
      if(item._rose_proportion_) item._proportion_=item._rose_proportion_;
      var arc = 2 * Math.PI - (item._start_ + 2 * Math.PI * item._proportion_ / 2);
      var color = item.color;
      var radius = item._radius_;
      return {
        arc: arc,
        text: text,
        color: color,
        radius: radius,
        textColor: item.textColor,
        textSize: item.textSize,
      };
    });
    for (let i = 0; i < seriesConvert.length; i++) {
      let item = seriesConvert[i];
      // line end
      let orginX1 = Math.cos(item.arc) * (item.radius + lineRadius);
      let orginY1 = Math.sin(item.arc) * (item.radius + lineRadius);
  
      // line start
      let orginX2 = Math.cos(item.arc) * item.radius;
      let orginY2 = Math.sin(item.arc) * item.radius;
  
      // text start
      let orginX3 = orginX1 >= 0 ? orginX1 + config.pieChartTextPadding : orginX1 - config.pieChartTextPadding;
      let orginY3 = orginY1;
      let textWidth = measureText(item.text,item.textSize||config.fontSize);
      let startY = orginY3;
  
      if (lastTextObject && util.isSameXCoordinateArea(lastTextObject.start, {
          x: orginX3
        })) {
        if (orginX3 > 0) {
          startY = Math.min(orginY3, lastTextObject.start.y);
        } else if (orginX1 < 0) {
          startY = Math.max(orginY3, lastTextObject.start.y);
        } else {
          if (orginY3 > 0) {
            startY = Math.max(orginY3, lastTextObject.start.y);
          } else {
            startY = Math.min(orginY3, lastTextObject.start.y);
          }
        }
      }
      if (orginX3 < 0) {
        orginX3 -= textWidth;
      }
  
      let textObject = {
        lineStart: {
          x: orginX2,
          y: orginY2
        },
        lineEnd: {
          x: orginX1,
          y: orginY1
        },
        start: {
          x: orginX3,
          y: startY
        },
        width: textWidth,
        height: config.fontSize,
        text: item.text,
        color: item.color,
        textColor: item.textColor,
        textSize: item.textSize
      };
      lastTextObject = avoidCollision(textObject, lastTextObject);
      textObjectCollection.push(lastTextObject);
    }
  
    for (let i = 0; i < textObjectCollection.length; i++) {
      let item = textObjectCollection[i];
      let lineStartPoistion = convertCoordinateOrigin(item.lineStart.x, item.lineStart.y, center);
      let lineEndPoistion = convertCoordinateOrigin(item.lineEnd.x, item.lineEnd.y, center);
      let textPosition = convertCoordinateOrigin(item.start.x, item.start.y, center);
      context.setLineWidth(1 * opts.pixelRatio);
      context.setFontSize(config.fontSize);
      context.beginPath();
      context.setStrokeStyle(item.color);
      context.setFillStyle(item.color);
      context.moveTo(lineStartPoistion.x, lineStartPoistion.y);
      let curveStartX = item.start.x < 0 ? textPosition.x + item.width : textPosition.x;
      let textStartX = item.start.x < 0 ? textPosition.x - 5 : textPosition.x + 5;
      context.quadraticCurveTo(lineEndPoistion.x, lineEndPoistion.y, curveStartX, textPosition.y);
      context.moveTo(lineStartPoistion.x, lineStartPoistion.y);
      context.stroke();
      context.closePath();
      context.beginPath();
      context.moveTo(textPosition.x + item.width, textPosition.y);
      context.arc(curveStartX, textPosition.y, 2, 0, 2 * Math.PI);
      context.closePath();
      context.fill();
      context.beginPath();
      context.setFontSize(item.textSize || config.fontSize);
      context.setFillStyle(item.textColor || '#666666');
      context.fillText(item.text, textStartX, textPosition.y + 3);
      context.closePath();
      context.stroke();
      context.closePath();
    }
  }
  
  function drawToolTipSplitLine(offsetX, opts, config, context) {
    var toolTipOption = opts.extra.tooltip || {};
    toolTipOption.gridType = toolTipOption.gridType == undefined ? 'solid' : toolTipOption.gridType;
    toolTipOption.dashLength = toolTipOption.dashLength == undefined ? 4 : toolTipOption.dashLength;
    var startY = opts.area[0];
    var endY = opts.height - opts.area[2];
  
    if (toolTipOption.gridType == 'dash') {
      context.setLineDash([toolTipOption.dashLength, toolTipOption.dashLength]);
    }
    context.setStrokeStyle(toolTipOption.gridColor || '#cccccc');
    context.setLineWidth(1 * opts.pixelRatio);
    context.beginPath();
    context.moveTo(offsetX, startY);
    context.lineTo(offsetX, endY);
    context.stroke();
    context.setLineDash([]);
  
    if (toolTipOption.xAxisLabel) {
      let labelText = opts.categories[opts.tooltip.index];
      context.setFontSize(config.fontSize);
      let textWidth = measureText(labelText, config.fontSize);
  
      let textX = offsetX - 0.5 * textWidth;
      let textY = endY;
      context.beginPath();
      context.setFillStyle(hexToRgb(toolTipOption.labelBgColor || config.toolTipBackground, toolTipOption.labelBgOpacity || config.toolTipOpacity));
      context.setStrokeStyle(toolTipOption.labelBgColor || config.toolTipBackground);
      context.setLineWidth(1 * opts.pixelRatio);
      context.rect(textX - config.toolTipPadding, textY, textWidth + 2 * config.toolTipPadding, config.fontSize + 2 * config.toolTipPadding);
      context.closePath();
      context.stroke();
      context.fill();
  
      context.beginPath();
      context.setFontSize(config.fontSize);
      context.setFillStyle(toolTipOption.labelFontColor || config.fontColor);
      context.fillText(String(labelText), textX, textY + config.toolTipPadding + config.fontSize);
      context.closePath();
      context.stroke();
    }
  }
  
  function drawMarkLine(opts, config, context) {
    let markLineOption = assign({}, {
      type: 'solid',
      dashLength: 4,
      data: []
    }, opts.extra.markLine);
    let startX = opts.area[3];
    let endX = opts.width - opts.area[1];
    let points = calMarkLineData(markLineOption.data, opts);
  
    for (let i = 0; i < points.length; i++) {
      let item = assign({}, {
        lineColor: '#DE4A42',
        showLabel: false,
        labelFontColor: '#666666',
        labelBgColor: '#DFE8FF',
        labelBgOpacity: 0.8,
        yAxisIndex: 0
      }, points[i]);
  
      if (markLineOption.type == 'dash') {
        context.setLineDash([markLineOption.dashLength, markLineOption.dashLength]);
      }
      context.setStrokeStyle(item.lineColor);
      context.setLineWidth(1 * opts.pixelRatio);
      context.beginPath();
      context.moveTo(startX, item.y);
      context.lineTo(endX, item.y);
      context.stroke();
      context.setLineDash([]);
      if (item.showLabel) {
        let labelText = opts.yAxis.format ? opts.yAxis.format(Number(item.value)) : item.value;
        context.setFontSize(config.fontSize);
        let textWidth = measureText(labelText, config.fontSize);
        let bgStartX = opts.padding[3] + config.yAxisTitleWidth - config.toolTipPadding;
        let bgEndX = Math.max(opts.area[3], textWidth + config.toolTipPadding * 2);
        let bgWidth = bgEndX - bgStartX;
  
        let textX = bgStartX + (bgWidth - textWidth) / 2;
        let textY = item.y;
        context.setFillStyle(hexToRgb(item.labelBgColor, item.labelBgOpacity));
        context.setStrokeStyle(item.labelBgColor);
        context.setLineWidth(1 * opts.pixelRatio);
        context.beginPath();
        context.rect(bgStartX, textY - 0.5 * config.fontSize - config.toolTipPadding, bgWidth, config.fontSize + 2 * config.toolTipPadding);
        context.closePath();
        context.stroke();
        context.fill();
  
        context.beginPath();
        context.setFontSize(config.fontSize);
        context.setFillStyle(item.labelFontColor);
        context.fillText(String(labelText), textX, textY + 0.5 * config.fontSize);
        context.stroke();
      }
    }
  }
  
  function drawToolTipHorizentalLine(opts, config, context, eachSpacing, xAxisPoints) {
    var toolTipOption = assign({}, {
      gridType: 'solid',
      dashLength: 4
    }, opts.extra.tooltip);
  
    var startX = opts.area[3];
    var endX = opts.width - opts.area[1];
  
    if (toolTipOption.gridType == 'dash') {
      context.setLineDash([toolTipOption.dashLength, toolTipOption.dashLength]);
    }
    context.setStrokeStyle(toolTipOption.gridColor || '#cccccc');
    context.setLineWidth(1 * opts.pixelRatio);
    context.beginPath();
    context.moveTo(startX, opts.tooltip.offset.y);
    context.lineTo(endX, opts.tooltip.offset.y);
    context.stroke();
    context.setLineDash([]);
  
    if (toolTipOption.yAxisLabel) {
      let labelText = calTooltipYAxisData(opts.tooltip.offset.y, opts.series, opts, config, eachSpacing);
      let widthArr = opts.chartData.yAxisData.yAxisWidth;
      let tStartLeft=opts.area[3];
      let tStartRight=opts.width-opts.area[1];
      for(let i=0;i<labelText.length;i++){
        context.setFontSize(config.fontSize);
        let textWidth = measureText(labelText[i], config.fontSize);
        let bgStartX,bgEndX,bgWidth;
        if(widthArr[i].position == 'left'){
          bgStartX = tStartLeft - widthArr[i].width;
          bgEndX = Math.max(bgStartX, bgStartX + textWidth + config.toolTipPadding * 2);
        }else{
          bgStartX = tStartRight;
          bgEndX = Math.max(bgStartX + widthArr[i].width, bgStartX + textWidth + config.toolTipPadding * 2);
        }
        bgWidth = bgEndX - bgStartX;
        
        let textX = bgStartX + (bgWidth - textWidth) / 2;
        let textY = opts.tooltip.offset.y;
        context.beginPath();
        context.setFillStyle(hexToRgb(toolTipOption.labelBgColor || config.toolTipBackground, toolTipOption.labelBgOpacity || config.toolTipOpacity));
        context.setStrokeStyle(toolTipOption.labelBgColor || config.toolTipBackground);
        context.setLineWidth(1 * opts.pixelRatio);
        context.rect(bgStartX, textY - 0.5 * config.fontSize - config.toolTipPadding, bgWidth, config.fontSize + 2 * config.toolTipPadding);
        context.closePath();
        context.stroke();
        context.fill();
        
        context.beginPath();
        context.setFontSize(config.fontSize);
        context.setFillStyle(toolTipOption.labelFontColor || config.fontColor);
        context.fillText(labelText[i], textX, textY + 0.5 * config.fontSize);
        context.closePath();
        context.stroke();
        if(widthArr[i].position == 'left'){
          tStartLeft -=(widthArr[i].width + opts.yAxis.padding);
        }else{
          tStartRight +=widthArr[i].width+ opts.yAxis.padding;
        }
      }
    }
  }
  
  function drawToolTipSplitArea(offsetX, opts, config, context, eachSpacing) {
    var toolTipOption = assign({}, {
      activeBgColor: '#000000',
      activeBgOpacity: 0.08
    }, opts.extra.tooltip);
    var startY = opts.area[0];
    var endY = opts.height - opts.area[2];
    context.beginPath();
    context.setFillStyle(hexToRgb(toolTipOption.activeBgColor, toolTipOption.activeBgOpacity));
    context.rect(offsetX - eachSpacing / 2, startY, eachSpacing, endY - startY);
    context.closePath();
    context.fill();
  }
  
  function drawToolTip(textList, offset, opts, config, context, eachSpacing, xAxisPoints) {
    var toolTipOption = assign({}, {
  		showBox:true,
      bgColor: '#000000',
      bgOpacity: 0.7,
      fontColor: '#FFFFFF'
    }, opts.extra.tooltip);
    var legendWidth = 4 * opts.pixelRatio;
    var legendMarginRight = 5 * opts.pixelRatio;
    var arrowWidth = 8 * opts.pixelRatio;
    var isOverRightBorder = false;
    if (opts.type == 'line' || opts.type == 'area' || opts.type == 'candle' || opts.type == 'mix') {
      drawToolTipSplitLine(opts.tooltip.offset.x, opts, config, context);
    }
  
    offset = assign({
      x: 0,
      y: 0
    }, offset);
    offset.y -= 8 * opts.pixelRatio;
    var textWidth = textList.map(function(item) {
      return measureText(item.text, config.fontSize);
    });
    var toolTipWidth = legendWidth + legendMarginRight + 4 * config.toolTipPadding + Math.max.apply(null, textWidth);
    var toolTipHeight = 2 * config.toolTipPadding + textList.length * config.toolTipLineHeight;
  
  	if(toolTipOption.showBox == false){ return }
    // if beyond the right border
    if (offset.x - Math.abs(opts._scrollDistance_) + arrowWidth + toolTipWidth > opts.width) {
      isOverRightBorder = true;
    }
    if (toolTipHeight + offset.y > opts.height) {
      offset.y = opts.height - toolTipHeight;
    }
    // draw background rect
    context.beginPath();
    context.setFillStyle(hexToRgb(toolTipOption.bgColor || config.toolTipBackground, toolTipOption.bgOpacity || config.toolTipOpacity));
    if (isOverRightBorder) {
      context.moveTo(offset.x, offset.y + 10 * opts.pixelRatio);
      context.lineTo(offset.x - arrowWidth, offset.y + 10 * opts.pixelRatio - 5 * opts.pixelRatio);
      context.lineTo(offset.x - arrowWidth, offset.y);
      context.lineTo(offset.x - arrowWidth - Math.round(toolTipWidth), offset.y);
      context.lineTo(offset.x - arrowWidth - Math.round(toolTipWidth), offset.y + toolTipHeight);
      context.lineTo(offset.x - arrowWidth, offset.y + toolTipHeight);
      context.lineTo(offset.x - arrowWidth, offset.y + 10 * opts.pixelRatio + 5 * opts.pixelRatio);
      context.lineTo(offset.x, offset.y + 10 * opts.pixelRatio);
    } else {
      context.moveTo(offset.x, offset.y + 10 * opts.pixelRatio);
      context.lineTo(offset.x + arrowWidth, offset.y + 10 * opts.pixelRatio - 5 * opts.pixelRatio);
      context.lineTo(offset.x + arrowWidth, offset.y);
      context.lineTo(offset.x + arrowWidth + Math.round(toolTipWidth), offset.y);
      context.lineTo(offset.x + arrowWidth + Math.round(toolTipWidth), offset.y + toolTipHeight);
      context.lineTo(offset.x + arrowWidth, offset.y + toolTipHeight);
      context.lineTo(offset.x + arrowWidth, offset.y + 10 * opts.pixelRatio + 5 * opts.pixelRatio);
      context.lineTo(offset.x, offset.y + 10 * opts.pixelRatio);
    }
  
    context.closePath();
    context.fill();
  
    // draw legend
    textList.forEach(function(item, index) {
      if (item.color !== null) {
        context.beginPath();
        context.setFillStyle(item.color);
        var startX = offset.x + arrowWidth + 2 * config.toolTipPadding;
        var startY = offset.y + (config.toolTipLineHeight - config.fontSize) / 2 + config.toolTipLineHeight * index +
          config.toolTipPadding + 1;
        if (isOverRightBorder) {
          startX = offset.x - toolTipWidth - arrowWidth + 2 * config.toolTipPadding;
        }
        context.fillRect(startX, startY, legendWidth, config.fontSize);
        context.closePath();
      }
    });
  
    // draw text list
  
    textList.forEach(function(item, index) {
      var startX = offset.x + arrowWidth + 2 * config.toolTipPadding + legendWidth + legendMarginRight;
      if (isOverRightBorder) {
        startX = offset.x - toolTipWidth - arrowWidth + 2 * config.toolTipPadding + +legendWidth + legendMarginRight;
      }
      var startY = offset.y + (config.toolTipLineHeight - config.fontSize) / 2 + config.toolTipLineHeight * index +
        config.toolTipPadding;
      context.beginPath();
      context.setFontSize(config.fontSize);
      context.setFillStyle(toolTipOption.fontColor);
      context.fillText(item.text, startX, startY + config.fontSize);
      context.closePath();
      context.stroke();
    });
  }
  
  function drawYAxisTitle(title, opts, config, context) {
    var startX = config.xAxisHeight + (opts.height - config.xAxisHeight - measureText(title)) / 2;
    context.save();
    context.beginPath();
    context.setFontSize(config.fontSize);
    context.setFillStyle(opts.yAxis.titleFontColor || '#333333');
    context.translate(0, opts.height);
    context.rotate(-90 * Math.PI / 180);
    context.fillText(title, startX, opts.padding[3] + 0.5 * config.fontSize);
    context.closePath();
    context.stroke();
    context.restore();
  }
  
  function drawColumnDataPoints(series, opts, config, context) {
    let process = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 1;
    let xAxisData = opts.chartData.xAxisData,
      xAxisPoints = xAxisData.xAxisPoints,
      eachSpacing = xAxisData.eachSpacing;
    let columnOption = assign({}, {
      type: 'group',
      width: eachSpacing / 2,
      meter: {
        border: 4,
        fillColor: '#FFFFFF'
      }
    }, opts.extra.column);
    
    let calPoints = [];
    context.save();
  	
  	let leftNum=-2;
  	let rightNum=xAxisPoints.length+2;
  	
    if (opts._scrollDistance_ && opts._scrollDistance_ !== 0 && opts.enableScroll === true) {
      context.translate(opts._scrollDistance_, 0);
  		leftNum=Math.floor(-opts._scrollDistance_/eachSpacing)-2;
  		rightNum=leftNum+opts.xAxis.itemCount+4;
    }
    if (opts.tooltip && opts.tooltip.textList && opts.tooltip.textList.length && process === 1) {
      drawToolTipSplitArea(opts.tooltip.offset.x, opts, config, context, eachSpacing);
    }
  	
    series.forEach(function(eachSeries, seriesIndex) {
      let ranges,minRange,maxRange;
      ranges = [].concat(opts.chartData.yAxisData.ranges[eachSeries.index]);
      minRange = ranges.pop();
      maxRange = ranges.shift();
      
      var data = eachSeries.data;
      switch (columnOption.type) {
        case 'group':
          var points = getDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, process);
          var tooltipPoints = getStackDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, seriesIndex, series, process);
          calPoints.push(tooltipPoints);
          points = fixColumeData(points, eachSpacing, series.length, seriesIndex, config, opts);
  				for(let i=0;i<points.length;i++){
  					let item=points[i];
            if (item !== null && i>leftNum && i<rightNum) {
              context.beginPath();
              context.setStrokeStyle(item.color || eachSeries.color);
              context.setLineWidth(1)
              context.setFillStyle(item.color || eachSeries.color);
              var startX = item.x - item.width / 2;
              var height = opts.height - item.y - opts.area[2];
              context.moveTo(startX, item.y);
              context.lineTo(startX+item.width-2,item.y);
              context.lineTo(startX+item.width-2,opts.height - opts.area[2]);
              context.lineTo(startX,opts.height - opts.area[2]);
              context.lineTo(startX,item.y);
              context.closePath();
              context.stroke();
              context.fill();
            }
          };
          break;
        case 'stack':
          // 绘制堆叠数据图
          var points = getStackDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, seriesIndex, series, process);
          calPoints.push(points);
          points = fixColumeStackData(points, eachSpacing, series.length, seriesIndex, config, opts, series);
  
          for(let i=0;i<points.length;i++){
          	let item=points[i];
            if (item !== null && i>leftNum && i<rightNum) {
              context.beginPath();
              context.setFillStyle(item.color || eachSeries.color);
              var startX = item.x - item.width / 2 + 1;
              var height = opts.height - item.y - opts.area[2];
              var height0 = opts.height - item.y0 - opts.area[2];
              if (seriesIndex > 0) {
                height -= height0;
              }
              context.moveTo(startX, item.y);
              context.fillRect(startX, item.y, item.width - 2, height);
              context.closePath();
              context.fill();
            }
          };
          break;
        case 'meter':
          // 绘制温度计数据图
          var points = getDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, process);
          calPoints.push(points);
          points = fixColumeMeterData(points, eachSpacing, series.length, seriesIndex, config, opts, columnOption.meter.border);
          if (seriesIndex == 0) {
            for(let i=0;i<points.length;i++){
            	let item=points[i];
              if (item !== null && i>leftNum && i<rightNum) {
                //画背景颜色
                context.beginPath();
                context.setFillStyle(columnOption.meter.fillColor);
                var startX = item.x - item.width / 2;
                var height = opts.height - item.y - opts.area[2];
                context.moveTo(startX, item.y);
                context.fillRect(startX, item.y, item.width, height);
                context.closePath();
                context.fill();
                //画边框线
                if (columnOption.meter.border > 0) {
                  context.beginPath();
                  context.setStrokeStyle(eachSeries.color);
                  context.setLineWidth(columnOption.meter.border * opts.pixelRatio);
                  context.moveTo(startX + columnOption.meter.border * 0.5, item.y + height);
                  context.lineTo(startX + columnOption.meter.border * 0.5, item.y + columnOption.meter.border * 0.5);
                  context.lineTo(startX + item.width - columnOption.meter.border * 0.5, item.y + columnOption.meter.border * 0.5);
                  context.lineTo(startX + item.width - columnOption.meter.border * 0.5, item.y + height);
                  context.stroke();
                }
              }
            };
          } else {
            for(let i=0;i<points.length;i++){
            	let item=points[i];
              if (item !== null && i>leftNum && i<rightNum) {
                context.beginPath();
                context.setFillStyle(item.color || eachSeries.color);
                var startX = item.x - item.width / 2;
                var height = opts.height - item.y - opts.area[2];
                context.moveTo(startX, item.y);
                context.fillRect(startX, item.y, item.width, height);
                context.closePath();
                context.fill();
              }
            };
          }
          break;
      }
    });
  
    if (opts.dataLabel !== false && process === 1) {
      series.forEach(function(eachSeries, seriesIndex) {
        let ranges,minRange,maxRange;
          ranges = [].concat(opts.chartData.yAxisData.ranges[eachSeries.index]);
          minRange = ranges.pop();
          maxRange = ranges.shift();
        var data = eachSeries.data;
        switch (columnOption.type) {
          case 'group':
            var points = getDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, process);
            points = fixColumeData(points, eachSpacing, series.length, seriesIndex, config, opts);
            drawPointText(points, eachSeries, config, context);
            break;
          case 'stack':
            var points = getStackDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, seriesIndex, series, process);
            drawPointText(points, eachSeries, config, context);
            break;
          case 'meter':
            var points = getDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, process);
            drawPointText(points, eachSeries, config, context);
            break;
        }
      });
    }
  
    context.restore();
  
    return {
      xAxisPoints: xAxisPoints,
      calPoints: calPoints,
      eachSpacing: eachSpacing
    };
  }
  
  function drawCandleDataPoints(series, seriesMA, opts, config, context) {
    var process = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 1;
    var candleOption = assign({}, {
      color: {},
      average: {}
    }, opts.extra.candle);
    candleOption.color = assign({}, {
      upLine: '#f04864',
      upFill: '#f04864',
      downLine: '#2fc25b',
      downFill: '#2fc25b'
    }, candleOption.color);
    candleOption.average = assign({}, {
      show: false,
      name: [],
      day: [],
      color: config.colors
    }, candleOption.average);
    opts.extra.candle = candleOption;
  
    let xAxisData = opts.chartData.xAxisData,
      xAxisPoints = xAxisData.xAxisPoints,
      eachSpacing = xAxisData.eachSpacing;
  
    let calPoints = [];
  
    context.save();
  	
  	let leftNum=-2;
  	let rightNum=xAxisPoints.length+2;
  	let leftSpace=0;
  	let rightSpace=opts.width+eachSpacing;
  	
    if (opts._scrollDistance_ && opts._scrollDistance_ !== 0 && opts.enableScroll === true) {
      context.translate(opts._scrollDistance_, 0);
  		leftNum=Math.floor(-opts._scrollDistance_/eachSpacing)-2;
  		rightNum=leftNum+opts.xAxis.itemCount+4;
  		leftSpace=-opts._scrollDistance_-eachSpacing+opts.area[3];
  		rightSpace=leftSpace+(opts.xAxis.itemCount+4)*eachSpacing;
    }
  
    //画均线
    if (candleOption.average.show) {
      seriesMA.forEach(function(eachSeries, seriesIndex) {
        let ranges,minRange,maxRange;
        ranges = [].concat(opts.chartData.yAxisData.ranges[eachSeries.index]);
        minRange = ranges.pop();
        maxRange = ranges.shift();
  
        var data = eachSeries.data;
        var points = getDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, process);
        var splitPointList = splitPoints(points);
  			
  			for(let i=0;i<splitPointList.length;i++){
  				let points=splitPointList[i];
  				context.beginPath();
  				context.setStrokeStyle(eachSeries.color);
  				context.setLineWidth(1);
  				if (points.length === 1) {
  					context.moveTo(points[0].x, points[0].y);
  					context.arc(points[0].x, points[0].y, 1, 0, 2 * Math.PI);
  				} else {
  					context.moveTo(points[0].x, points[0].y);
  					let startPoint=0;
  					for(let j=0;j<points.length;j++){
  						let item=points[j];
  						if(startPoint==0 && item.x > leftSpace){
  							context.moveTo(item.x, item.y);
  							startPoint=1;
  						}
  						if (j > 0 && item.x > leftSpace && item.x < rightSpace) {
  							var ctrlPoint = createCurveControlPoints(points, j - 1);
  							context.bezierCurveTo(ctrlPoint.ctrA.x, ctrlPoint.ctrA.y, ctrlPoint.ctrB.x, ctrlPoint.ctrB.y,item.x, item.y);
  						}
  					}
  					context.moveTo(points[0].x, points[0].y);
  				}
  				context.closePath();
  				context.stroke();
        }
      });
    }
    //画K线
    series.forEach(function(eachSeries, seriesIndex) {
      let ranges,minRange,maxRange;
      ranges = [].concat(opts.chartData.yAxisData.ranges[eachSeries.index]);
      minRange = ranges.pop();
      maxRange = ranges.shift();
      var data = eachSeries.data;
      var points = getCandleDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, process);
      calPoints.push(points);
      var splitPointList = splitPoints(points);
  
  		for(let i=0;i<splitPointList[0].length;i++){
  			if(i>leftNum && i<rightNum){
  				let item=splitPointList[0][i];
  				context.beginPath();
  				//如果上涨
  				if (data[i][1] - data[i][0] > 0) {
  					context.setStrokeStyle(candleOption.color.upLine);
  					context.setFillStyle(candleOption.color.upFill);
  					context.setLineWidth(1 * opts.pixelRatio);
  					context.moveTo(item[3].x, item[3].y); //顶点
  					context.lineTo(item[1].x, item[1].y); //收盘中间点
  					context.lineTo(item[1].x - eachSpacing / 4, item[1].y); //收盘左侧点
  					context.lineTo(item[0].x - eachSpacing / 4, item[0].y); //开盘左侧点
  					context.lineTo(item[0].x, item[0].y); //开盘中间点
  					context.lineTo(item[2].x, item[2].y); //底点
  					context.lineTo(item[0].x, item[0].y); //开盘中间点
  					context.lineTo(item[0].x + eachSpacing / 4, item[0].y); //开盘右侧点
  					context.lineTo(item[1].x + eachSpacing / 4, item[1].y); //收盘右侧点
  					context.lineTo(item[1].x, item[1].y); //收盘中间点
  					context.moveTo(item[3].x, item[3].y); //顶点
  				} else {
  					context.setStrokeStyle(candleOption.color.downLine);
  					context.setFillStyle(candleOption.color.downFill);
  					context.setLineWidth(1 * opts.pixelRatio);
  					context.moveTo(item[3].x, item[3].y); //顶点
  					context.lineTo(item[0].x, item[0].y); //开盘中间点
  					context.lineTo(item[0].x - eachSpacing / 4, item[0].y); //开盘左侧点
  					context.lineTo(item[1].x - eachSpacing / 4, item[1].y); //收盘左侧点
  					context.lineTo(item[1].x, item[1].y); //收盘中间点
  					context.lineTo(item[2].x, item[2].y); //底点
  					context.lineTo(item[1].x, item[1].y); //收盘中间点
  					context.lineTo(item[1].x + eachSpacing / 4, item[1].y); //收盘右侧点
  					context.lineTo(item[0].x + eachSpacing / 4, item[0].y); //开盘右侧点
  					context.lineTo(item[0].x, item[0].y); //开盘中间点
  					context.moveTo(item[3].x, item[3].y); //顶点
  				}
  				context.closePath();
  				context.fill();
  				context.stroke();
  			}
      }
    });
  
    context.restore();
  
    return {
      xAxisPoints: xAxisPoints,
      calPoints: calPoints,
      eachSpacing: eachSpacing
    };
  }
  
  function drawAreaDataPoints(series, opts, config, context) {
    var process = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 1;
    var areaOption = assign({},{
      type: 'straight',
      opacity: 0.2,
      addLine: false,
      width: 2,
  		gradient:false
    },opts.extra.area);
  
    let xAxisData = opts.chartData.xAxisData,
      xAxisPoints = xAxisData.xAxisPoints,
      eachSpacing = xAxisData.eachSpacing;
  
    let endY = opts.height - opts.area[2];
    let calPoints = [];
  
    context.save();
  	let leftSpace=0;
  	let rightSpace=opts.width+eachSpacing;
    if (opts._scrollDistance_ && opts._scrollDistance_ !== 0 && opts.enableScroll === true) {
      context.translate(opts._scrollDistance_, 0);
  		leftSpace=-opts._scrollDistance_-eachSpacing+opts.area[3];
  		rightSpace=leftSpace+(opts.xAxis.itemCount+4)*eachSpacing;
    }
  
    series.forEach(function(eachSeries, seriesIndex) {
      let ranges,minRange,maxRange;
      ranges = [].concat(opts.chartData.yAxisData.ranges[eachSeries.index]);
      minRange = ranges.pop();
      maxRange = ranges.shift();
      let data = eachSeries.data;
      let points = getDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, process);
      calPoints.push(points);
  
      let splitPointList = splitPoints(points);
      for (let i = 0; i < splitPointList.length; i++) {
        let points = splitPointList[i];
        // 绘制区域数
        context.beginPath();
        context.setStrokeStyle(hexToRgb(eachSeries.color, areaOption.opacity));
  			if(areaOption.gradient){
  				let gradient = context.createLinearGradient(0, opts.area[0], 0, opts.height-opts.area[2]);
  				gradient.addColorStop('0', hexToRgb(eachSeries.color, areaOption.opacity));
  				gradient.addColorStop('1.0',hexToRgb("#FFFFFF", 0.1));
  				context.setFillStyle(gradient);
  			}else{
  				context.setFillStyle(hexToRgb(eachSeries.color, areaOption.opacity));
  			}
        context.setLineWidth(areaOption.width * opts.pixelRatio);
        if (points.length > 1) {
          let firstPoint = points[0];
          let lastPoint = points[points.length - 1];
          context.moveTo(firstPoint.x, firstPoint.y);
  				let startPoint=0;
          if (areaOption.type === 'curve') {
  					for(let j=0;j<points.length;j++){
  						let item=points[j];
  						if(startPoint==0 && item.x > leftSpace){
  							context.moveTo(item.x, item.y);
  							startPoint=1;
  						}
              if (j > 0 && item.x > leftSpace && item.x < rightSpace) {
                let ctrlPoint = createCurveControlPoints(points, j - 1);
                context.bezierCurveTo(ctrlPoint.ctrA.x, ctrlPoint.ctrA.y, ctrlPoint.ctrB.x, ctrlPoint.ctrB.y,item.x, item.y);
              }
            };
          } else {
  					for(let j=0;j<points.length;j++){
  						let item=points[j];
  						if(startPoint==0 && item.x > leftSpace){
  							context.moveTo(item.x, item.y);
  							startPoint=1;
  						}
  					  if (j > 0 && item.x > leftSpace && item.x < rightSpace) {
                context.lineTo(item.x, item.y);
              }
            };
          }
  
          context.lineTo(lastPoint.x, endY);
          context.lineTo(firstPoint.x, endY);
          context.lineTo(firstPoint.x, firstPoint.y);
        } else {
          let item = points[0];
          context.moveTo(item.x - eachSpacing / 2, item.y);
          context.lineTo(item.x + eachSpacing / 2, item.y);
          context.lineTo(item.x + eachSpacing / 2, endY);
          context.lineTo(item.x - eachSpacing / 2, endY);
          context.moveTo(item.x - eachSpacing / 2, item.y);
        }
        context.closePath();
        context.fill();
  
        //画连线
        if (areaOption.addLine) {
  				if (eachSeries.lineType == 'dash') {
  					let dashLength = eachSeries.dashLength?eachSeries.dashLength:8;
  					dashLength *= opts.pixelRatio;
  				  context.setLineDash([dashLength, dashLength]);
  				}
          context.beginPath();
          context.setStrokeStyle(eachSeries.color);
          context.setLineWidth(areaOption.width * opts.pixelRatio);
          if (points.length === 1) {
            context.moveTo(points[0].x, points[0].y);
            context.arc(points[0].x, points[0].y, 1, 0, 2 * Math.PI);
          } else {
            context.moveTo(points[0].x, points[0].y);
  					let startPoint=0;
            if (areaOption.type === 'curve') {
              for(let j=0;j<points.length;j++){
              	let item=points[j];
              	if(startPoint==0 && item.x > leftSpace){
              		context.moveTo(item.x, item.y);
              		startPoint=1;
              	}
                if (j > 0 && item.x > leftSpace && item.x < rightSpace) {
                  let ctrlPoint = createCurveControlPoints(points, j - 1);
                  context.bezierCurveTo(ctrlPoint.ctrA.x, ctrlPoint.ctrA.y, ctrlPoint.ctrB.x, ctrlPoint.ctrB.y,item.x,item.y);
                }
              };
            } else {
              for(let j=0;j<points.length;j++){
              	let item=points[j];
              	if(startPoint==0 && item.x > leftSpace){
              		context.moveTo(item.x, item.y);
              		startPoint=1;
              	}
                if (j > 0 && item.x > leftSpace && item.x < rightSpace) {
                  context.lineTo(item.x, item.y);
                }
              };
            }
            context.moveTo(points[0].x, points[0].y);
          }
          context.stroke();
  				context.setLineDash([]);
        }
      }
  
      //画点
      if (opts.dataPointShape !== false) {
        drawPointShape(points, eachSeries.color, eachSeries.pointShape, context, opts);
      }
  
    });
  
    if (opts.dataLabel !== false && process === 1) {
      series.forEach(function(eachSeries, seriesIndex) {
        let ranges,minRange,maxRange;
        ranges = [].concat(opts.chartData.yAxisData.ranges[eachSeries.index]);
        minRange = ranges.pop();
        maxRange = ranges.shift();
        var data = eachSeries.data;
        var points = getDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, process);
        drawPointText(points, eachSeries, config, context);
      });
    }
  
    context.restore();
  
    return {
      xAxisPoints: xAxisPoints,
      calPoints: calPoints,
      eachSpacing: eachSpacing
    };
  }
  
  function drawLineDataPoints(series, opts, config, context) {
    var process = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 1;
    var lineOption = assign({},{
  		type: 'straight',
  		width: 2
  	},opts.extra.line);
  	lineOption.width *=opts.pixelRatio;
  	
    let xAxisData = opts.chartData.xAxisData,
      xAxisPoints = xAxisData.xAxisPoints,
      eachSpacing = xAxisData.eachSpacing;
    var calPoints = [];
  
    context.save();
  	let leftSpace=0;
  	let rightSpace=opts.width+eachSpacing;
    if (opts._scrollDistance_ && opts._scrollDistance_ !== 0 && opts.enableScroll === true) {
      context.translate(opts._scrollDistance_, 0);
  		leftSpace=-opts._scrollDistance_-eachSpacing+opts.area[3];
  		rightSpace=leftSpace+(opts.xAxis.itemCount+4)*eachSpacing;
    }
  
    series.forEach(function(eachSeries, seriesIndex) {
      let ranges,minRange,maxRange;
      ranges = [].concat(opts.chartData.yAxisData.ranges[eachSeries.index]);
      minRange = ranges.pop();
      maxRange = ranges.shift();
      var data = eachSeries.data;
      var points = getDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, process);
      calPoints.push(points);
      var splitPointList = splitPoints(points);
  		
  		if (eachSeries.lineType == 'dash') {
  			let dashLength = eachSeries.dashLength?eachSeries.dashLength:8;
  			dashLength *= opts.pixelRatio;
  		  context.setLineDash([dashLength, dashLength]);
  		}
  		context.beginPath();
  		context.setStrokeStyle(eachSeries.color);
  		context.setLineWidth(lineOption.width);
  		
      splitPointList.forEach(function(points, index) {
  			
        if (points.length === 1) {
          context.moveTo(points[0].x, points[0].y);
          context.arc(points[0].x, points[0].y, 1, 0, 2 * Math.PI);
        } else {
          context.moveTo(points[0].x, points[0].y);
  				let startPoint=0;
          if (lineOption.type === 'curve') {
            for(let j=0;j<points.length;j++){
            	let item=points[j];
            	if(startPoint==0 && item.x > leftSpace){
            		context.moveTo(item.x, item.y);
            		startPoint=1;
            	}
              if (j > 0 && item.x > leftSpace && item.x < rightSpace) {
                var ctrlPoint = createCurveControlPoints(points, j - 1);
                context.bezierCurveTo(ctrlPoint.ctrA.x, ctrlPoint.ctrA.y, ctrlPoint.ctrB.x, ctrlPoint.ctrB.y,item.x, item.y);
              }
            };
          } else {
            for(let j=0;j<points.length;j++){
            	let item=points[j];
            	if(startPoint==0 && item.x > leftSpace){
            		context.moveTo(item.x, item.y);
            		startPoint=1;
            	}
              if (j > 0 && item.x > leftSpace && item.x < rightSpace) {
                context.lineTo(item.x, item.y);
              }
            };
          }
          context.moveTo(points[0].x, points[0].y);
        }
        
      });
  		
  		context.stroke();
  		context.setLineDash([]);
  		
      if (opts.dataPointShape !== false) {
        drawPointShape(points, eachSeries.color, eachSeries.pointShape, context, opts);
      }
    });
  
    if (opts.dataLabel !== false && process === 1) {
      series.forEach(function(eachSeries, seriesIndex) {
        let ranges,minRange,maxRange;
        ranges = [].concat(opts.chartData.yAxisData.ranges[eachSeries.index]);
        minRange = ranges.pop();
        maxRange = ranges.shift();
        var data = eachSeries.data;
        var points = getDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, process);
        drawPointText(points, eachSeries, config, context);
      });
    }
  
    context.restore();
  
    return {
      xAxisPoints: xAxisPoints,
      calPoints: calPoints,
      eachSpacing: eachSpacing
    };
  }
  
  function drawMixDataPoints(series, opts, config, context) {
    let process = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 1;
    
    let xAxisData = opts.chartData.xAxisData,
      xAxisPoints = xAxisData.xAxisPoints,
      eachSpacing = xAxisData.eachSpacing;
  
    let endY = opts.height - opts.area[2];
    let calPoints = [];
  
    var columnIndex = 0;
    var columnLength = 0;
    series.forEach(function(eachSeries, seriesIndex) {
      if (eachSeries.type == 'column') {
        columnLength += 1;
      }
    });
    context.save();
  	let leftNum=-2;
  	let rightNum=xAxisPoints.length+2;
  	let leftSpace=0;
  	let rightSpace=opts.width+eachSpacing;
    if (opts._scrollDistance_ && opts._scrollDistance_ !== 0 && opts.enableScroll === true) {
      context.translate(opts._scrollDistance_, 0);
  		leftNum=Math.floor(-opts._scrollDistance_/eachSpacing)-2;
  		rightNum=leftNum+opts.xAxis.itemCount+4;
  		leftSpace=-opts._scrollDistance_-eachSpacing+opts.area[3];
  		rightSpace=leftSpace+(opts.xAxis.itemCount+4)*eachSpacing;
    }
  
    series.forEach(function(eachSeries, seriesIndex) {
      let ranges,minRange,maxRange;
      
  		ranges = [].concat(opts.chartData.yAxisData.ranges[eachSeries.index]);
  		minRange = ranges.pop();
  		maxRange = ranges.shift();
  
      var data = eachSeries.data;
      var points = getDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, process);
      calPoints.push(points);
  
      // 绘制柱状数据图
      if (eachSeries.type == 'column') {
        points = fixColumeData(points, eachSpacing, columnLength, columnIndex, config, opts);
        for(let i=0;i<points.length;i++){
        	let item=points[i];
          if (item !== null && i>leftNum && i<rightNum) {
            context.beginPath();
            context.setStrokeStyle(item.color || eachSeries.color);
            context.setLineWidth(1)
            context.setFillStyle(item.color || eachSeries.color);
            var startX = item.x - item.width / 2;
            var height = opts.height - item.y - opts.area[2];
            context.moveTo(startX, item.y);
            context.moveTo(startX, item.y);
            context.lineTo(startX+item.width-2,item.y);
            context.lineTo(startX+item.width-2,opts.height - opts.area[2]);
            context.lineTo(startX,opts.height - opts.area[2]);
            context.lineTo(startX,item.y);
            context.closePath();
            context.stroke();
            context.fill();
            context.closePath();
            context.fill();
          }
        }
        columnIndex += 1;
      }
  
      //绘制区域图数据
  
      if (eachSeries.type == 'area') {
        let splitPointList = splitPoints(points);
        for (let i = 0; i < splitPointList.length; i++) {
          let points = splitPointList[i];
          // 绘制区域数据
          context.beginPath();
          context.setStrokeStyle(eachSeries.color);
          context.setFillStyle(hexToRgb(eachSeries.color, 0.2));
          context.setLineWidth(2 * opts.pixelRatio);
          if (points.length > 1) {
            var firstPoint = points[0];
            let lastPoint = points[points.length - 1];
            context.moveTo(firstPoint.x, firstPoint.y);
  					let startPoint=0;
            if (eachSeries.style === 'curve') {
              for(let j=0;j<points.length;j++){
              	let item=points[j];
              	if(startPoint==0 && item.x > leftSpace){
              		context.moveTo(item.x, item.y);
              		startPoint=1;
              	}
                if (j > 0 && item.x > leftSpace && item.x < rightSpace) {
                  var ctrlPoint = createCurveControlPoints(points, j - 1);
                  context.bezierCurveTo(ctrlPoint.ctrA.x, ctrlPoint.ctrA.y, ctrlPoint.ctrB.x, ctrlPoint.ctrB.y, item.x, item.y);
                }
              };
            } else {
              for(let j=0;j<points.length;j++){
              	let item=points[j];
              	if(startPoint==0 && item.x > leftSpace){
              		context.moveTo(item.x, item.y);
              		startPoint=1;
              	}
                if (j > 0 && item.x > leftSpace && item.x < rightSpace) {
                  context.lineTo(item.x, item.y);
                }
              };
            }
            context.lineTo(lastPoint.x, endY);
            context.lineTo(firstPoint.x, endY);
            context.lineTo(firstPoint.x, firstPoint.y);
          } else {
            let item = points[0];
            context.moveTo(item.x - eachSpacing / 2, item.y);
            context.lineTo(item.x + eachSpacing / 2, item.y);
            context.lineTo(item.x + eachSpacing / 2, endY);
            context.lineTo(item.x - eachSpacing / 2, endY);
            context.moveTo(item.x - eachSpacing / 2, item.y);
          }
          context.closePath();
          context.fill();
        }
      }
  
      // 绘制折线数据图
      if (eachSeries.type == 'line') {
        var splitPointList = splitPoints(points);
        splitPointList.forEach(function(points, index) {
  				if (eachSeries.lineType == 'dash') {
  					let dashLength = eachSeries.dashLength?eachSeries.dashLength:8;
  					dashLength *= opts.pixelRatio;
  				  context.setLineDash([dashLength, dashLength]);
  				}
          context.beginPath();
          context.setStrokeStyle(eachSeries.color);
          context.setLineWidth(2 * opts.pixelRatio);
          if (points.length === 1) {
            context.moveTo(points[0].x, points[0].y);
            context.arc(points[0].x, points[0].y, 1, 0, 2 * Math.PI);
          } else {
            context.moveTo(points[0].x, points[0].y);
  					let startPoint=0;
            if (eachSeries.style == 'curve') {
              for(let j=0;j<points.length;j++){
              	let item=points[j];
              	if(startPoint==0 && item.x > leftSpace){
              		context.moveTo(item.x, item.y);
              		startPoint=1;
              	}
                if (j > 0 && item.x > leftSpace && item.x < rightSpace) {
                  var ctrlPoint = createCurveControlPoints(points, j - 1);
                  context.bezierCurveTo(ctrlPoint.ctrA.x, ctrlPoint.ctrA.y, ctrlPoint.ctrB.x, ctrlPoint.ctrB.y,item.x,item.y);
                }
              }
            } else {
              for(let j=0;j<points.length;j++){
              	let item=points[j];
              	if(startPoint==0 && item.x > leftSpace){
              		context.moveTo(item.x, item.y);
              		startPoint=1;
              	}
                if (j > 0 && item.x > leftSpace && item.x < rightSpace) {
                  context.lineTo(item.x, item.y);
                }
              }
            }
            context.moveTo(points[0].x, points[0].y);
          }
          context.stroke();
  				context.setLineDash([]);
        });
      }
  
      // 绘制点数据图
      if (eachSeries.type == 'point') {
  			eachSeries.addPoint = true;
      }
  
      if (eachSeries.addPoint == true && eachSeries.type !== 'column' ) {
        drawPointShape(points, eachSeries.color, eachSeries.pointShape, context, opts);
      }
    });
    if (opts.dataLabel !== false && process === 1) {
      var columnIndex = 0;
      series.forEach(function(eachSeries, seriesIndex) {
        let ranges,minRange,maxRange;
        
  			ranges = [].concat(opts.chartData.yAxisData.ranges[eachSeries.index]);
  			minRange = ranges.pop();
  			maxRange = ranges.shift();
  				
        var data = eachSeries.data;
        var points = getDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, process);
        if (eachSeries.type !== 'column') {
          drawPointText(points, eachSeries, config, context);
        } else {
          points = fixColumeData(points, eachSpacing, columnLength, columnIndex, config, opts);
          drawPointText(points, eachSeries, config, context);
          columnIndex += 1;
        }
  
      });
    }
  
    context.restore();
  
    return {
      xAxisPoints: xAxisPoints,
      calPoints: calPoints,
      eachSpacing: eachSpacing,
    }
  }
  
  function drawToolTipBridge(opts, config, context, process, eachSpacing, xAxisPoints) {
    var toolTipOption = opts.extra.tooltip || {};
    if (toolTipOption.horizentalLine && opts.tooltip && process === 1 && (opts.type == 'line' || opts.type == 'area' || opts.type == 'column' || opts.type == 'candle' || opts.type == 'mix')) {
      drawToolTipHorizentalLine(opts, config, context, eachSpacing, xAxisPoints)
    }
    context.save();
    if (opts._scrollDistance_ && opts._scrollDistance_ !== 0 && opts.enableScroll === true) {
      context.translate(opts._scrollDistance_, 0);
    }
    if (opts.tooltip && opts.tooltip.textList && opts.tooltip.textList.length && process === 1) {
      drawToolTip(opts.tooltip.textList, opts.tooltip.offset, opts, config, context, eachSpacing, xAxisPoints);
    }
    context.restore();
  
  }
  
  function drawXAxis(categories, opts, config, context) {
  
    let xAxisData = opts.chartData.xAxisData,
      xAxisPoints = xAxisData.xAxisPoints,
      startX = xAxisData.startX,
      endX = xAxisData.endX,
      eachSpacing = xAxisData.eachSpacing;
    var boundaryGap='center';
    if (opts.type == 'line'||opts.type == 'area'){
      boundaryGap=opts.xAxis.boundaryGap;
    }
    var startY = opts.height - opts.area[2];
    var endY = opts.area[0];
  
    //绘制滚动条
    if (opts.enableScroll && opts.xAxis.scrollShow) {
      var scrollY = opts.height - opts.area[2] + config.xAxisHeight;
      var scrollScreenWidth = endX - startX;
      var scrollTotalWidth = eachSpacing * (xAxisPoints.length - 1);
      var scrollWidth = scrollScreenWidth * scrollScreenWidth / scrollTotalWidth;
      var scrollLeft = 0;
      if (opts._scrollDistance_) {
        scrollLeft = -opts._scrollDistance_ * (scrollScreenWidth) / scrollTotalWidth;
      }
      context.beginPath();
      context.setLineCap('round');
      context.setLineWidth(6 * opts.pixelRatio);
      context.setStrokeStyle(opts.xAxis.scrollBackgroundColor || "#EFEBEF");
      context.moveTo(startX, scrollY);
      context.lineTo(endX, scrollY);
      context.stroke();
      context.closePath();
      context.beginPath();
      context.setLineCap('round');
      context.setLineWidth(6 * opts.pixelRatio);
      context.setStrokeStyle(opts.xAxis.scrollColor || "#A6A6A6");
      context.moveTo(startX + scrollLeft, scrollY);
      context.lineTo(startX + scrollLeft + scrollWidth, scrollY);
      context.stroke();
      context.closePath();
      context.setLineCap('butt');
    }
  
    context.save();
  
    if (opts._scrollDistance_ && opts._scrollDistance_ !== 0) {
      context.translate(opts._scrollDistance_, 0);
    }
  	
  	//绘制X轴刻度线
  	if (opts.xAxis.calibration === true) {
  		context.setStrokeStyle(opts.xAxis.gridColor || "#cccccc");
  		context.setLineCap('butt');
  		context.setLineWidth(1 * opts.pixelRatio);
  	  xAxisPoints.forEach(function(item, index) {
  	    if (index > 0) {
  	      context.beginPath();
  	      context.moveTo(item - eachSpacing / 2, startY);
  	      context.lineTo(item - eachSpacing / 2, startY + 3 * opts.pixelRatio);
  	      context.closePath();
  	      context.stroke();
  	    }
  	  });
  	}
  	//绘制X轴网格
    if (opts.xAxis.disableGrid !== true) {
      context.setStrokeStyle(opts.xAxis.gridColor || "#cccccc");
      context.setLineCap('butt');
      context.setLineWidth(1 * opts.pixelRatio);
      if (opts.xAxis.gridType == 'dash') {
        context.setLineDash([opts.xAxis.dashLength, opts.xAxis.dashLength]);
      }
  		opts.xAxis.gridEval = opts.xAxis.gridEval || 1;
  		xAxisPoints.forEach(function(item, index) {
  			if (index % opts.xAxis.gridEval == 0) {
  				context.beginPath();
  				context.moveTo(item, startY);
  				context.lineTo(item, endY);
  				context.stroke();
  			}
  		});
      context.setLineDash([]);
    }
    
  
    //绘制X轴文案
    if (opts.xAxis.disabled !== true) {
      // 对X轴列表做抽稀处理
      //默认全部显示X轴标签
      let maxXAxisListLength = categories.length;
      //如果设置了X轴单屏数量
      if (opts.xAxis.labelCount) {
        //如果设置X轴密度
        if (opts.xAxis.itemCount) {
          maxXAxisListLength = Math.ceil(categories.length / opts.xAxis.itemCount * opts.xAxis.labelCount);
        } else {
          maxXAxisListLength = opts.xAxis.labelCount;
        }
        maxXAxisListLength -= 1;
      }
  
      let ratio = Math.ceil(categories.length / maxXAxisListLength);
  
      let newCategories = [];
      let cgLength = categories.length;
      for (let i = 0; i < cgLength; i++) {
        if (i % ratio !== 0) {
          newCategories.push("");
        } else {
          newCategories.push(categories[i]);
        }
      }
      newCategories[cgLength - 1] = categories[cgLength - 1];
  
      var xAxisFontSize = opts.xAxis.fontSize || config.fontSize;
      if (config._xAxisTextAngle_ === 0) {
        newCategories.forEach(function(item, index) {
          var offset = - measureText(String(item), xAxisFontSize) / 2;
          if(boundaryGap == 'center'){
            offset+=eachSpacing / 2;
          }
          var scrollHeight=0;
          if(opts.xAxis.scrollShow){
            scrollHeight=6*opts.pixelRatio;
          }
          context.beginPath();
          context.setFontSize(xAxisFontSize);
          context.setFillStyle(opts.xAxis.fontColor || '#666666');
          context.fillText(String(item), xAxisPoints[index] + offset, startY + xAxisFontSize + (config.xAxisHeight - scrollHeight - xAxisFontSize) / 2);
          context.closePath();
          context.stroke();
        });
  
      } else {
        newCategories.forEach(function(item, index) {
          context.save();
          context.beginPath();
          context.setFontSize(xAxisFontSize);
          context.setFillStyle(opts.xAxis.fontColor || '#666666');
          var textWidth = measureText(String(item),xAxisFontSize);
          var offset = - textWidth;
          if(boundaryGap == 'center'){
            offset+=eachSpacing / 2;
          }
          var _calRotateTranslate = calRotateTranslate(xAxisPoints[index] + eachSpacing / 2, startY + xAxisFontSize / 2 + 5, opts.height),
            transX = _calRotateTranslate.transX,
            transY = _calRotateTranslate.transY;
  
          context.rotate(-1 * config._xAxisTextAngle_);
          context.translate(transX, transY);
          context.fillText(String(item), xAxisPoints[index] + offset, startY + xAxisFontSize + 5);
          context.closePath();
          context.stroke();
          context.restore();
        });
      }
    }
    context.restore();
  	
  	//绘制X轴轴线
    if(opts.xAxis.axisLine){
      context.beginPath();
      context.setStrokeStyle(opts.xAxis.axisLineColor);
      context.setLineWidth(1 * opts.pixelRatio);
      context.moveTo(startX,opts.height-opts.area[2]);
      context.lineTo(endX,opts.height-opts.area[2]);
      context.stroke();
    }
  }
  
  function drawYAxisGrid(categories, opts, config, context) {
    if (opts.yAxis.disableGrid === true) {
      return;
    }
    let spacingValid = opts.height - opts.area[0] - opts.area[2];
    let eachSpacing = spacingValid / opts.yAxis.splitNumber;
    let startX = opts.area[3];
    let xAxisPoints = opts.chartData.xAxisData.xAxisPoints,
      xAxiseachSpacing = opts.chartData.xAxisData.eachSpacing;
    let TotalWidth = xAxiseachSpacing * (xAxisPoints.length - 1);
    let endX = startX + TotalWidth;
  
    let points = [];
    for (let i = 0; i < opts.yAxis.splitNumber + 1; i++) {
      points.push(opts.height - opts.area[2] - eachSpacing * i);
    }
  
    context.save();
    if (opts._scrollDistance_ && opts._scrollDistance_ !== 0) {
      context.translate(opts._scrollDistance_, 0);
    }
  
    if (opts.yAxis.gridType == 'dash') {
      context.setLineDash([opts.yAxis.dashLength, opts.yAxis.dashLength]);
    }
    context.setStrokeStyle(opts.yAxis.gridColor);
    context.setLineWidth(1 * opts.pixelRatio);
    points.forEach(function(item, index) {
      context.beginPath();
      context.moveTo(startX, item);
      context.lineTo(endX, item);
      context.stroke();
    });
    context.setLineDash([]);
  
    context.restore();
  }
  
  function drawYAxis(series, opts, config, context) {
    if (opts.yAxis.disabled === true) {
      return;
    }
    var spacingValid = opts.height - opts.area[0] - opts.area[2];
    var eachSpacing = spacingValid / opts.yAxis.splitNumber;
    var startX = opts.area[3];
    var endX = opts.width - opts.area[1];
    var endY = opts.height - opts.area[2];
    var fillEndY = endY + config.xAxisHeight;
    if (opts.xAxis.scrollShow) {
      fillEndY -= 3 * opts.pixelRatio;
    }
  	if (opts.xAxis.rotateLabel){
  		fillEndY = opts.height - opts.area[2]+3;
  	}
    // set YAxis background
    context.beginPath();
    context.setFillStyle(opts.background || '#ffffff');
    if (opts._scrollDistance_ < 0) {
      context.fillRect(0, 0, startX, fillEndY);
    }
    if(opts.enableScroll == true){
      context.fillRect(endX, 0, opts.width, fillEndY);
    }
    context.closePath();
    context.stroke();
  
    var points = [];
    for (let i = 0; i <= opts.yAxis.splitNumber; i++) {
      points.push(opts.area[0] + eachSpacing * i);
    }
  
    let tStartLeft=opts.area[3];
    let tStartRight=opts.width-opts.area[1];
  
    for (let i = 0; i < opts.yAxis.data.length; i++) {
      let yData = opts.yAxis.data[i];
      if(yData.disabled !== true){
        let rangesFormat = opts.chartData.yAxisData.rangesFormat[i];
        let yAxisFontSize = yData.fontSize || config.fontSize;
        let yAxisWidth = opts.chartData.yAxisData.yAxisWidth[i];
        //画Y轴刻度及文案
        rangesFormat.forEach(function(item, index) {
          var pos = points[index] ? points[index] : endY;
          context.beginPath();
          context.setFontSize(yAxisFontSize);
          context.setLineWidth(1*opts.pixelRatio);
          context.setStrokeStyle(yData.axisLineColor||'#cccccc');
          context.setFillStyle(yData.fontColor|| '#666666');
          if(yAxisWidth.position=='left'){
            context.fillText(String(item), tStartLeft - yAxisWidth.width , pos + yAxisFontSize / 2);
            //画刻度线
            if(yData.calibration==true){
              context.moveTo(tStartLeft,pos);
              context.lineTo(tStartLeft - 3*opts.pixelRatio,pos);
            }
          }else{
            context.fillText(String(item), tStartRight + 4*opts.pixelRatio, pos + yAxisFontSize / 2);
            //画刻度线
            if(yData.calibration==true){
              context.moveTo(tStartRight,pos);
              context.lineTo(tStartRight + 3*opts.pixelRatio,pos);
            }
          }
          context.closePath();
          context.stroke();
        });
        //画Y轴轴线
        if (yData.axisLine!==false) {
          context.beginPath();
          context.setStrokeStyle(yData.axisLineColor||'#cccccc');
          context.setLineWidth(1 * opts.pixelRatio);
          if(yAxisWidth.position=='left'){
            context.moveTo(tStartLeft,opts.height-opts.area[2]);
            context.lineTo(tStartLeft,opts.area[0]);
          }else{
            context.moveTo(tStartRight,opts.height-opts.area[2]);
            context.lineTo(tStartRight,opts.area[0]);
          }
          context.stroke();
        }
  			
        //画Y轴标题
        if (opts.yAxis.showTitle) {
  				
          let titleFontSize = yData.titleFontSize || config.fontSize;
          let title = yData.title;
          context.beginPath();
          context.setFontSize(titleFontSize);
          context.setFillStyle(yData.titleFontColor || '#666666');
          if(yAxisWidth.position=='left'){
            context.fillText(title, tStartLeft - measureText(title,titleFontSize)/2, opts.area[0]-10*opts.pixelRatio);
          }else{
            context.fillText(title,tStartRight - measureText(title,titleFontSize)/2, opts.area[0]-10*opts.pixelRatio);
          }
          context.closePath();
          context.stroke();
        }
        if(yAxisWidth.position=='left'){
          tStartLeft -=(yAxisWidth.width + opts.yAxis.padding);
        }else{
          tStartRight +=yAxisWidth.width+ opts.yAxis.padding;
        }
      }
    }
  }
  
  function drawLegend(series, opts, config, context, chartData) {
    if (opts.legend.show === false) {
      return;
    }
    let legendData = chartData.legendData;
    let legendList = legendData.points;
    let legendArea = legendData.area;
    let padding = opts.legend.padding;
    let fontSize = opts.legend.fontSize;
    let shapeWidth = 15 * opts.pixelRatio;
    let shapeRight = 5 * opts.pixelRatio;
    let itemGap = opts.legend.itemGap;
    let lineHeight = Math.max(opts.legend.lineHeight * opts.pixelRatio, fontSize);
  
    //画背景及边框
    context.beginPath();
    context.setLineWidth(opts.legend.borderWidth);
    context.setStrokeStyle(opts.legend.borderColor);
    context.setFillStyle(opts.legend.backgroundColor);
    context.moveTo(legendArea.start.x, legendArea.start.y);
    context.rect(legendArea.start.x, legendArea.start.y, legendArea.width, legendArea.height);
    context.closePath();
    context.fill();
    context.stroke();
  
    legendList.forEach(function(itemList, listIndex) {
      let width = 0;
      let height = 0;
      width = legendData.widthArr[listIndex];
      height = legendData.heightArr[listIndex];
      let startX = 0;
      let startY = 0;
      if (opts.legend.position == 'top' || opts.legend.position == 'bottom') {
        startX = legendArea.start.x + (legendArea.width - width) / 2;
        startY = legendArea.start.y + padding + listIndex * lineHeight;
      } else {
        if (listIndex == 0) {
          width = 0;
        } else {
          width = legendData.widthArr[listIndex - 1];
        }
        startX = legendArea.start.x + padding + width;
        startY = legendArea.start.y + padding + (legendArea.height - height) / 2;
      }
  
      context.setFontSize(config.fontSize);
      for (let i = 0; i < itemList.length; i++) {
        let item = itemList[i];
        item.area = [0, 0, 0, 0];
        item.area[0] = startX;
        item.area[1] = startY;
        item.area[3] = startY + lineHeight;
        context.beginPath();
        context.setLineWidth(1 * opts.pixelRatio);
        context.setStrokeStyle(item.show ? item.color : opts.legend.hiddenColor);
        context.setFillStyle(item.show ? item.color : opts.legend.hiddenColor);
        switch (item.legendShape) {
          case 'line':
            context.moveTo(startX, startY + 0.5 * lineHeight - 2 * opts.pixelRatio);
            context.fillRect(startX, startY + 0.5 * lineHeight - 2 * opts.pixelRatio, 15 * opts.pixelRatio, 4 * opts.pixelRatio);
            break;
          case 'triangle':
            context.moveTo(startX + 7.5 * opts.pixelRatio, startY + 0.5 * lineHeight - 5 * opts.pixelRatio);
            context.lineTo(startX + 2.5 * opts.pixelRatio, startY + 0.5 * lineHeight + 5 * opts.pixelRatio);
            context.lineTo(startX + 12.5 * opts.pixelRatio, startY + 0.5 * lineHeight + 5 * opts.pixelRatio);
            context.lineTo(startX + 7.5 * opts.pixelRatio, startY + 0.5 * lineHeight - 5 * opts.pixelRatio);
            break;
          case 'diamond':
            context.moveTo(startX + 7.5 * opts.pixelRatio, startY + 0.5 * lineHeight - 5 * opts.pixelRatio);
            context.lineTo(startX + 2.5 * opts.pixelRatio, startY + 0.5 * lineHeight);
            context.lineTo(startX + 7.5 * opts.pixelRatio, startY + 0.5 * lineHeight + 5 * opts.pixelRatio);
            context.lineTo(startX + 12.5 * opts.pixelRatio, startY + 0.5 * lineHeight);
            context.lineTo(startX + 7.5 * opts.pixelRatio, startY + 0.5 * lineHeight - 5 * opts.pixelRatio);
            break;
          case 'circle':
            context.moveTo(startX + 7.5 * opts.pixelRatio, startY + 0.5 * lineHeight);
            context.arc(startX + 7.5 * opts.pixelRatio, startY + 0.5 * lineHeight, 5 * opts.pixelRatio, 0, 2 * Math.PI);
            break;
          case 'rect':
            context.moveTo(startX, startY + 0.5 * lineHeight - 5 * opts.pixelRatio);
            context.fillRect(startX, startY + 0.5 * lineHeight - 5 * opts.pixelRatio, 15 * opts.pixelRatio, 10 * opts.pixelRatio);
            break;
          default:
            context.moveTo(startX, startY + 0.5 * lineHeight - 5 * opts.pixelRatio);
            context.fillRect(startX, startY + 0.5 * lineHeight - 5 * opts.pixelRatio, 15 * opts.pixelRatio, 10 * opts.pixelRatio);
        }
        context.closePath();
        context.fill();
        context.stroke();
  
        startX += shapeWidth + shapeRight;
        let fontTrans = 0.5 * lineHeight + 0.5 * fontSize - 2;
        context.beginPath();
        context.setFontSize(fontSize);
        context.setFillStyle(item.show ? opts.legend.fontColor : opts.legend.hiddenColor);
        context.fillText(item.name, startX, startY + fontTrans);
        context.closePath();
        context.stroke();
        if (opts.legend.position == 'top' || opts.legend.position == 'bottom') {
          startX += measureText(item.name, fontSize) + itemGap;
          item.area[2] = startX;
        } else {
          item.area[2] = startX + measureText(item.name, fontSize) + itemGap;;
          startX -= shapeWidth + shapeRight;
          startY += lineHeight;
        }
      }
    });
  }
  
  function drawPieDataPoints(series, opts, config, context) {
    var process = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 1;
    var pieOption = assign({}, {
      activeOpacity: 0.5,
      activeRadius: 10 * opts.pixelRatio,
      offsetAngle: 0,
      labelWidth: 15 * opts.pixelRatio,
      ringWidth: 0,
      border:false,
      borderWidth:2,
      borderColor:'#FFFFFF'
    }, opts.extra.pie);
    var centerPosition = {
      x: opts.area[3] + (opts.width - opts.area[1] - opts.area[3]) / 2,
      y: opts.area[0] + (opts.height - opts.area[0] - opts.area[2]) / 2
    };
    if (config.pieChartLinePadding == 0) {
      config.pieChartLinePadding = pieOption.activeRadius;
    }
  
    var radius = Math.min((opts.width - opts.area[1] - opts.area[3]) / 2 - config.pieChartLinePadding - config.pieChartTextPadding - config._pieTextMaxLength_, (opts.height - opts.area[0] - opts.area[2]) / 2 - config.pieChartLinePadding - config.pieChartTextPadding);
  
    series = getPieDataPoints(series, radius, process);
  
    var activeRadius = pieOption.activeRadius;
  
    series = series.map(function(eachSeries) {
      eachSeries._start_ += (pieOption.offsetAngle) * Math.PI / 180;
      return eachSeries;
    });
    series.forEach(function(eachSeries, seriesIndex) {
      if (opts.tooltip) {
        if (opts.tooltip.index == seriesIndex) {
          context.beginPath();
          context.setFillStyle(hexToRgb(eachSeries.color, opts.extra.pie.activeOpacity || 0.5));
          context.moveTo(centerPosition.x, centerPosition.y);
          context.arc(centerPosition.x, centerPosition.y, eachSeries._radius_ + activeRadius, eachSeries._start_,
            eachSeries._start_ + 2 *
            eachSeries._proportion_ * Math.PI);
          context.closePath();
          context.fill();
        }
      }
      context.beginPath();
      context.setLineWidth(pieOption.borderWidth * opts.pixelRatio);
      context.lineJoin = "round";
      context.setStrokeStyle(pieOption.borderColor);
      context.setFillStyle(eachSeries.color);
      context.moveTo(centerPosition.x, centerPosition.y);
      context.arc(centerPosition.x, centerPosition.y, eachSeries._radius_, eachSeries._start_, eachSeries._start_ + 2 * eachSeries._proportion_ * Math.PI);
      context.closePath();
      context.fill();
      if (pieOption.border == true) {
        context.stroke();
      }
    });
  
    if (opts.type === 'ring') {
      var innerPieWidth = radius * 0.6;
      if (typeof opts.extra.pie.ringWidth === 'number' && opts.extra.pie.ringWidth > 0) {
        innerPieWidth = Math.max(0, radius - opts.extra.pie.ringWidth);
      }
      context.beginPath();
      context.setFillStyle(opts.background || '#ffffff');
      context.moveTo(centerPosition.x, centerPosition.y);
      context.arc(centerPosition.x, centerPosition.y, innerPieWidth, 0, 2 * Math.PI);
      context.closePath();
      context.fill();
    }
  
    if (opts.dataLabel !== false && process === 1) {
      var valid = false;
      for (var i = 0, len = series.length; i < len; i++) {
        if (series[i].data > 0) {
          valid = true;
          break;
        }
      }
  
      if (valid) {
        drawPieText(series, opts, config, context, radius, centerPosition);
      }
    }
  
    if (process === 1 && opts.type === 'ring') {
      drawRingTitle(opts, config, context, centerPosition);
    }
  
    return {
      center: centerPosition,
      radius: radius,
      series: series
    };
  }
  
  function drawRoseDataPoints(series, opts, config, context) {
    var process = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 1;
    var roseOption = assign({}, {
      type: 'area',
      activeOpacity: 0.5,
      activeRadius: 10 * opts.pixelRatio,
      offsetAngle: 0,
      labelWidth: 15 * opts.pixelRatio,
      border:false,
      borderWidth:2,
      borderColor:'#FFFFFF'
    }, opts.extra.rose);
    if (config.pieChartLinePadding == 0) {
      config.pieChartLinePadding = roseOption.activeRadius;
    }
    var centerPosition = {
      x: opts.area[3] + (opts.width - opts.area[1] - opts.area[3]) / 2,
      y: opts.area[0] + (opts.height - opts.area[0] - opts.area[2]) / 2
    };
     var radius = Math.min((opts.width - opts.area[1] - opts.area[3]) / 2 - config.pieChartLinePadding - config.pieChartTextPadding - config._pieTextMaxLength_, (opts.height - opts.area[0] - opts.area[2]) / 2 - config.pieChartLinePadding - config.pieChartTextPadding);
    var minRadius = roseOption.minRadius || radius * 0.5;
  
    series = getRoseDataPoints(series, roseOption.type, minRadius, radius, process);
  
    var activeRadius = roseOption.activeRadius;
  
    series = series.map(function(eachSeries) {
      eachSeries._start_ += (roseOption.offsetAngle || 0) * Math.PI / 180;
      return eachSeries;
    });
  
    series.forEach(function(eachSeries, seriesIndex) {
      if (opts.tooltip) {
        if (opts.tooltip.index == seriesIndex) {
          context.beginPath();
          context.setFillStyle(hexToRgb(eachSeries.color, roseOption.activeOpacity || 0.5));
          context.moveTo(centerPosition.x, centerPosition.y);
          context.arc(centerPosition.x, centerPosition.y, activeRadius + eachSeries._radius_, eachSeries._start_,
            eachSeries._start_ + 2 * eachSeries._rose_proportion_ * Math.PI);
          context.closePath();
          context.fill();
        }
      }
      context.beginPath();
      context.setLineWidth(roseOption.borderWidth * opts.pixelRatio);
      context.lineJoin = "round";
      context.setStrokeStyle(roseOption.borderColor);
      context.setFillStyle(eachSeries.color);
      context.moveTo(centerPosition.x, centerPosition.y);
      context.arc(centerPosition.x, centerPosition.y, eachSeries._radius_, eachSeries._start_, eachSeries._start_ + 2 *
        eachSeries._rose_proportion_ * Math.PI);
      context.closePath();
      context.fill();
      if (roseOption.border == true) {
        context.stroke();
      }
    });
  
    if (opts.dataLabel !== false && process === 1) {
      var valid = false;
      for (var i = 0, len = series.length; i < len; i++) {
        if (series[i].data > 0) {
          valid = true;
          break;
        }
      }
  
      if (valid) {
        drawPieText(series, opts, config, context, radius, centerPosition);
      }
    }
  
    return {
      center: centerPosition,
      radius: radius,
      series: series
    };
  }
  
  function drawArcbarDataPoints(series, opts, config, context) {
    var process = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 1;
    var arcbarOption = assign({}, {
      startAngle: 0.75,
      endAngle: 0.25,
      type: 'default',
      width: 12 * opts.pixelRatio,
  		gap:2 * opts.pixelRatio
    }, opts.extra.arcbar);
  
    series = getArcbarDataPoints(series, arcbarOption, process);
  	
    var centerPosition;
  	if(arcbarOption.center){
  		centerPosition=arcbarOption.center;
  	}else{
  		centerPosition= {
  		  x: opts.width / 2,
  		  y: opts.height / 2
  		};
  	}
  	
    var radius;
  	if(arcbarOption.radius){
  		radius=arcbarOption.radius;
  	}else{
  		radius = Math.min(centerPosition.x, centerPosition.y);
  		radius -= 5 * opts.pixelRatio;
  		radius -= arcbarOption.width / 2;
  	}
  	
    for (let i = 0; i < series.length; i++) {
      let eachSeries = series[i];
  		//背景颜色
  		context.setLineWidth(arcbarOption.width);
  		context.setStrokeStyle(arcbarOption.backgroundColor || '#E9E9E9');
  		context.setLineCap('round');
  		context.beginPath();
  		if (arcbarOption.type == 'default') {
  		  context.arc(centerPosition.x, centerPosition.y, radius-(arcbarOption.width+arcbarOption.gap)*i, arcbarOption.startAngle * Math.PI, arcbarOption.endAngle * Math.PI, false);
  		} else {
  		  context.arc(centerPosition.x, centerPosition.y, radius-(arcbarOption.width+arcbarOption.gap)*i, 0, 2 * Math.PI, false);
  		}
  		context.stroke();
  		//进度条
      context.setLineWidth(arcbarOption.width);
      context.setStrokeStyle(eachSeries.color);
      context.setLineCap('round');
      context.beginPath();
      context.arc(centerPosition.x, centerPosition.y, radius-(arcbarOption.width+arcbarOption.gap)*i, arcbarOption.startAngle * Math.PI, eachSeries._proportion_ * Math.PI, false);
      context.stroke();
    }
  
    drawRingTitle(opts, config, context, centerPosition);
  
    return {
      center: centerPosition,
      radius: radius,
      series: series
    };
  }
  
  function drawGaugeDataPoints(categories, series, opts, config, context) {
    var process = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 1;
    var gaugeOption = assign({}, {
  		type:'default',
      startAngle: 0.75,
      endAngle: 0.25,
      width: 15,
      splitLine: {
        fixRadius: 0,
        splitNumber: 10,
        width: 15,
        color: '#FFFFFF',
        childNumber: 5,
        childWidth: 5
      },
      pointer: {
        width: 15,
        color: 'auto'
      }
    }, opts.extra.gauge);
  
    if (gaugeOption.oldAngle == undefined) {
      gaugeOption.oldAngle = gaugeOption.startAngle;
    }
    if (gaugeOption.oldData == undefined) {
      gaugeOption.oldData = 0;
    }
    categories = getGaugeAxisPoints(categories, gaugeOption.startAngle, gaugeOption.endAngle);
  
    var centerPosition = {
      x: opts.width / 2,
      y: opts.height / 2
    };
    var radius = Math.min(centerPosition.x, centerPosition.y);
    radius -= 5 * opts.pixelRatio;
    radius -= gaugeOption.width / 2;
    var innerRadius = radius - gaugeOption.width;
  	var totalAngle=0;
  	
  	//判断仪表盘的样式：default百度样式，progress新样式
  	if(gaugeOption.type == 'progress'){
  		
  		//## 第一步画中心圆形背景和进度条背景
  		//中心圆形背景
  		var pieRadius = radius - gaugeOption.width*3;
  		context.beginPath();
  		let gradient = context.createLinearGradient(centerPosition.x, centerPosition.y-pieRadius, centerPosition.x , centerPosition.y+pieRadius);
  		//配置渐变填充（起点：中心点向上减半径；结束点中心点向下加半径）
  		gradient.addColorStop('0', hexToRgb(series[0].color, 0.3));
  		gradient.addColorStop('1.0',hexToRgb("#FFFFFF", 0.1));
  		context.setFillStyle(gradient);
  		context.arc(centerPosition.x, centerPosition.y, pieRadius, 0, 2*Math.PI, false);
  		context.fill();
  		//画进度条背景
  		context.setLineWidth(gaugeOption.width);
  		context.setStrokeStyle(hexToRgb(series[0].color, 0.3));
  		context.setLineCap('round');
  		context.beginPath();
  		context.arc(centerPosition.x, centerPosition.y, innerRadius , gaugeOption.startAngle * Math.PI, gaugeOption.endAngle *Math.PI, false);
  		context.stroke();
  		
  		//## 第二步画刻度线
  		totalAngle = gaugeOption.startAngle - gaugeOption.endAngle + 1;
  		let splitAngle = totalAngle / gaugeOption.splitLine.splitNumber;
  		let childAngle = totalAngle / gaugeOption.splitLine.splitNumber / gaugeOption.splitLine.childNumber;
  		let startX = -radius - gaugeOption.width * 0.5 - gaugeOption.splitLine.fixRadius;
  		let endX = -radius - gaugeOption.width - gaugeOption.splitLine.fixRadius + gaugeOption.splitLine.width;
  		context.save();
  		context.translate(centerPosition.x, centerPosition.y);
  		context.rotate((gaugeOption.startAngle - 1) * Math.PI);
  		let len = gaugeOption.splitLine.splitNumber * gaugeOption.splitLine.childNumber + 1;
  		let proc = series[0].data * process;
  		for (let i = 0; i < len; i++) {
  		  context.beginPath();
  			//刻度线随进度变色
  			if(proc>(i/len)){
  				context.setStrokeStyle(hexToRgb(series[0].color, 1));
  			}else{
  				context.setStrokeStyle(hexToRgb(series[0].color, 0.3));
  			}
  		  context.setLineWidth(3 * opts.pixelRatio);
  		  context.moveTo(startX, 0);
  		  context.lineTo(endX, 0);
  		  context.stroke();
  		  context.rotate(childAngle * Math.PI);
  		}
  		context.restore();
  		
  		//## 第三步画进度条
  		series = getArcbarDataPoints(series, gaugeOption, process);
  		context.setLineWidth(gaugeOption.width);
  		context.setStrokeStyle(series[0].color);
  		context.setLineCap('round');
  		context.beginPath();
  		context.arc(centerPosition.x, centerPosition.y, innerRadius , gaugeOption.startAngle * Math.PI, series[0]._proportion_ *Math.PI, false);
  		context.stroke();
  		
  		//## 第四步画指针
  		let pointerRadius = radius - gaugeOption.width*2.5;
  		context.save();
  		context.translate(centerPosition.x, centerPosition.y);
  		context.rotate((series[0]._proportion_ - 1) * Math.PI);
  		context.beginPath();
  		context.setLineWidth(gaugeOption.width/3);
  		let gradient3 = context.createLinearGradient(0, -pointerRadius*0.6, 0 , pointerRadius*0.6);
  		gradient3.addColorStop('0', hexToRgb('#FFFFFF', 0));
  		gradient3.addColorStop('0.5', hexToRgb(series[0].color, 1));
  		gradient3.addColorStop('1.0', hexToRgb('#FFFFFF', 0));
  		context.setStrokeStyle(gradient3);
  		context.arc(0, 0, pointerRadius , 0.85* Math.PI, 1.15 * Math.PI, false);
  		context.stroke();
  		context.beginPath();
  		context.setLineWidth(1);
  		context.setStrokeStyle(series[0].color);
  		context.setFillStyle(series[0].color);
  		context.moveTo(-pointerRadius-gaugeOption.width/3/2,-4);
  		context.lineTo(-pointerRadius-gaugeOption.width/3/2-4,0);
  		context.lineTo(-pointerRadius-gaugeOption.width/3/2,4);
  		context.lineTo(-pointerRadius-gaugeOption.width/3/2,-4);
  		context.stroke();
  		context.fill();
  		context.restore();
  		
  	//default百度样式
  	}else{
  		//画背景
  		context.setLineWidth(gaugeOption.width);
  		context.setLineCap('butt');
  		for (let i = 0; i < categories.length; i++) {
  		  let eachCategories = categories[i];
  		  context.beginPath();
  		  context.setStrokeStyle(eachCategories.color);
  		  context.arc(centerPosition.x, centerPosition.y, radius, eachCategories._startAngle_ * Math.PI, eachCategories._endAngle_ *Math.PI, false);
  		  context.stroke();
  		}
  		context.save();
  		
  		//画刻度线
  		totalAngle = gaugeOption.startAngle - gaugeOption.endAngle + 1;
  		let splitAngle = totalAngle / gaugeOption.splitLine.splitNumber;
  		let childAngle = totalAngle / gaugeOption.splitLine.splitNumber / gaugeOption.splitLine.childNumber;
  		let startX = -radius - gaugeOption.width * 0.5 - gaugeOption.splitLine.fixRadius;
  		let endX = -radius - gaugeOption.width * 0.5 - gaugeOption.splitLine.fixRadius + gaugeOption.splitLine.width;
  		let childendX = -radius - gaugeOption.width * 0.5 - gaugeOption.splitLine.fixRadius + gaugeOption.splitLine.childWidth;
  		
  		context.translate(centerPosition.x, centerPosition.y);
  		context.rotate((gaugeOption.startAngle - 1) * Math.PI);
  		
  		for (let i = 0; i < gaugeOption.splitLine.splitNumber + 1; i++) {
  		  context.beginPath();
  		  context.setStrokeStyle(gaugeOption.splitLine.color);
  		  context.setLineWidth(2 * opts.pixelRatio);
  		  context.moveTo(startX, 0);
  		  context.lineTo(endX, 0);
  		  context.stroke();
  		  context.rotate(splitAngle * Math.PI);
  		}
  		context.restore();
  		
  		context.save();
  		context.translate(centerPosition.x, centerPosition.y);
  		context.rotate((gaugeOption.startAngle - 1) * Math.PI);
  		
  		for (let i = 0; i < gaugeOption.splitLine.splitNumber * gaugeOption.splitLine.childNumber + 1; i++) {
  		  context.beginPath();
  		  context.setStrokeStyle(gaugeOption.splitLine.color);
  		  context.setLineWidth(1 * opts.pixelRatio);
  		  context.moveTo(startX, 0);
  		  context.lineTo(childendX, 0);
  		  context.stroke();
  		  context.rotate(childAngle * Math.PI);
  		}
  		context.restore();
  		
  		//画指针
  		series = getGaugeDataPoints(series, categories, gaugeOption, process);
  		
  		for (let i = 0; i < series.length; i++) {
  		  let eachSeries = series[i];
  		  context.save();
  		  context.translate(centerPosition.x, centerPosition.y);
  		  context.rotate((eachSeries._proportion_ - 1) * Math.PI);
  		  context.beginPath();
  		  context.setFillStyle(eachSeries.color);
  		  context.moveTo(gaugeOption.pointer.width, 0);
  		  context.lineTo(0, -gaugeOption.pointer.width / 2);
  		  context.lineTo(-innerRadius, 0);
  		  context.lineTo(0, gaugeOption.pointer.width / 2);
  		  context.lineTo(gaugeOption.pointer.width, 0);
  		  context.closePath();
  		  context.fill();
  		  context.beginPath();
  		  context.setFillStyle('#FFFFFF');
  		  context.arc(0, 0, gaugeOption.pointer.width / 6, 0, 2 * Math.PI, false);
  		  context.fill();
  		  context.restore();
  		}
  		
  		if (opts.dataLabel !== false) {
  		  drawGaugeLabel(gaugeOption, radius, centerPosition, opts, config, context);
  		}
  	}
  	
  	//画仪表盘标题，副标题
    drawRingTitle(opts, config, context, centerPosition);
  
    if (process === 1 && opts.type === 'gauge') {
      opts.extra.gauge.oldAngle = series[0]._proportion_;
      opts.extra.gauge.oldData = series[0].data;
    }
    return {
      center: centerPosition,
      radius: radius,
      innerRadius: innerRadius,
      categories: categories,
      totalAngle: totalAngle
    };
  }
  
  function drawRadarDataPoints(series, opts, config, context) {
    var process = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 1;
    var radarOption = assign({},{
      gridColor: '#cccccc',
      labelColor: '#666666',
      opacity: 0.2,
  		gridCount:3
    },opts.extra.radar);
    
    var coordinateAngle = getRadarCoordinateSeries(opts.categories.length);
    
    var centerPosition = {
      x: opts.area[3] + (opts.width - opts.area[1] - opts.area[3]) / 2,
      y: opts.area[0] + (opts.height - opts.area[0] - opts.area[2]) / 2
    };
  
    var radius = Math.min(centerPosition.x - (getMaxTextListLength(opts.categories) + config.radarLabelTextMargin),
      centerPosition.y - config.radarLabelTextMargin);
    //TODO逻辑不对
    radius -= opts.padding[1];
  
    // draw grid
    context.beginPath();
    context.setLineWidth(1 * opts.pixelRatio);
    context.setStrokeStyle(radarOption.gridColor);
    coordinateAngle.forEach(function(angle) {
      var pos = convertCoordinateOrigin(radius * Math.cos(angle), radius * Math.sin(angle), centerPosition);
      context.moveTo(centerPosition.x, centerPosition.y);
      context.lineTo(pos.x, pos.y);
    });
    context.stroke();
    context.closePath();
    // draw split line grid
  
    var _loop = function _loop(i) {
      var startPos = {};
      context.beginPath();
      context.setLineWidth(1 * opts.pixelRatio);
      context.setStrokeStyle(radarOption.gridColor);
      coordinateAngle.forEach(function(angle, index) {
        var pos = convertCoordinateOrigin(radius / radarOption.gridCount * i * Math.cos(angle), radius / radarOption.gridCount * i * Math.sin(angle), centerPosition);
        if (index === 0) {
          startPos = pos;
          context.moveTo(pos.x, pos.y);
        } else {
          context.lineTo(pos.x, pos.y);
        }
      });
      context.lineTo(startPos.x, startPos.y);
      context.stroke();
      context.closePath();
    };
  
    for (var i = 1; i <= radarOption.gridCount; i++) {
      _loop(i);
    }
  
    var radarDataPoints = getRadarDataPoints(coordinateAngle, centerPosition, radius, series, opts, process);
  
    radarDataPoints.forEach(function(eachSeries, seriesIndex) {
      // 绘制区域数据
      context.beginPath();
      context.setFillStyle(hexToRgb(eachSeries.color, radarOption.opacity));
      eachSeries.data.forEach(function(item, index) {
        if (index === 0) {
          context.moveTo(item.position.x, item.position.y);
        } else {
          context.lineTo(item.position.x, item.position.y);
        }
      });
      context.closePath();
      context.fill();
  
      if (opts.dataPointShape !== false) {
        var points = eachSeries.data.map(function(item) {
          return item.position;
        });
        drawPointShape(points, eachSeries.color, eachSeries.pointShape, context, opts);
      }
    });
    // draw label text
    drawRadarLabel(coordinateAngle, radius, centerPosition, opts, config, context);
  
    return {
      center: centerPosition,
      radius: radius,
      angleList: coordinateAngle
    };
  }
  
  function normalInt(min, max, iter) {
      iter = iter==0?1:iter;
      var arr = [];
      for (var i = 0; i < iter; i++) {
          arr[i] = Math.random();
      };
      return  Math.floor(arr.reduce(function(i,j){return i+j})/iter*(max-min))+min;  
  };
  
  function collisionNew(area,points,width,height){
      var isIn=false;
      for(let i=0;i<points.length;i++){
        if(points[i].area){
          if(area[3]<points[i].area[1]||area[0]>points[i].area[2]||area[1]>points[i].area[3]||area[2]<points[i].area[0]){
            if(area[0]<0 || area[1]<0 || area[2]>width || area[3]>height){
              isIn=true;
              break;
            }else{
              isIn=false;
            }
          }else{
            isIn=true;
            break;
          }
        }
      }
      return isIn;
  };
  
  function getBoundingBox(data) {
    var bounds = {}, coords;
    bounds.xMin = 180;
    bounds.xMax = 0;
    bounds.yMin = 90;
    bounds.yMax = 0
    for (var i = 0; i < data.length; i++) {
        var coorda = data[i].geometry.coordinates
        for (var k = 0; k < coorda.length; k++) {
            coords = coorda[k];
            if (coords.length == 1) {
                coords = coords[0]
            }
            for (var j = 0; j < coords.length; j++) {
                var longitude = coords[j][0];
                var latitude = coords[j][1];
                var point = {
                    x: longitude, 
                    y: latitude 
                }
                bounds.xMin = bounds.xMin < point.x ? bounds.xMin : point.x;
                bounds.xMax = bounds.xMax > point.x ? bounds.xMax : point.x;
                bounds.yMin = bounds.yMin < point.y ? bounds.yMin : point.y;
                bounds.yMax = bounds.yMax > point.y ? bounds.yMax : point.y;
            }
        }
    }
    return bounds;
  }
  
  function coordinateToPoint(latitude, longitude,bounds,scale,xoffset,yoffset) {
    return {
        x: (longitude - bounds.xMin) * scale+xoffset,
        y: (bounds.yMax - latitude) * scale+yoffset
    };
  }
  
  function pointToCoordinate(pointY, pointX,bounds,scale,xoffset,yoffset) {
    return {
        x: (pointX-xoffset)/scale+bounds.xMin,
        y: bounds.yMax - (pointY-yoffset)/scale
    };
  }
  
  function isRayIntersectsSegment(poi,s_poi,e_poi){
        if (s_poi[1]==e_poi[1]){return false;} 
        if (s_poi[1]>poi[1] && e_poi[1]>poi[1]){return false;}
        if (s_poi[1]<poi[1] && e_poi[1]<poi[1]){return false;}
        if (s_poi[1]==poi[1] && e_poi[1]>poi[1]){return false;}
        if (e_poi[1]==poi[1] && s_poi[1]>poi[1]){return false;}
        if (s_poi[0]<poi[0] && e_poi[1]<poi[1]){return false;}
        let xseg=e_poi[0]-(e_poi[0]-s_poi[0])*(e_poi[1]-poi[1])/(e_poi[1]-s_poi[1]); 
        if (xseg<poi[0]){
          return false;
        }else{
          return true;
        }
  } 
  
  function isPoiWithinPoly(poi,poly){
    let sinsc=0;
    for (let i=0;i<poly.length;i++){
      let epoly=poly[i][0];
      if (poly.length == 1) {
        epoly = poly[i][0]
      }
      for(let j=0;j<epoly.length-1;j++){
        let s_poi=epoly[j];
        let e_poi=epoly[j+1];
        if (isRayIntersectsSegment(poi,s_poi,e_poi)){
          sinsc+=1;
        }
      }
    }
    
    if(sinsc%2==1){
      return true;
    }else{
      return false;
    }
  }
  
  
  function drawMapDataPoints(series, opts, config, context) {
    var mapOption=assign({},{
      border:true,
      borderWidth:1,
      borderColor:'#666666',
      fillOpacity:0.6,
      activeBorderColor:'#f04864',
      activeFillColor:'#facc14',
      activeFillOpacity:1
    },opts.extra.map);
    var coords, point;
    var data = series;
    var bounds= getBoundingBox(data);
    var xScale = opts.width / Math.abs(bounds.xMax - bounds.xMin);
    var yScale = opts.height / Math.abs(bounds.yMax - bounds.yMin);
    var scale = xScale < yScale ? xScale : yScale;
    var xoffset=opts.width/2-Math.abs(bounds.xMax - bounds.xMin)/2*scale;
    var yoffset=opts.height/2-Math.abs(bounds.yMax - bounds.yMin)/2*scale;
    context.beginPath();
    context.clearRect(0, 0, opts.width, opts.height);
    context.setFillStyle(opts.background||'#FFFFFF');
    context.rect(0,0,opts.width,opts.height);
    context.fill();
    for (var i = 0; i < data.length; i++) {
      context.beginPath();
      context.setLineWidth(mapOption.borderWidth * opts.pixelRatio);
      context.setStrokeStyle(mapOption.borderColor);
      context.setFillStyle(hexToRgb(series[i].color, mapOption.fillOpacity));
      if (opts.tooltip) {
        if (opts.tooltip.index == i ) {
          context.setStrokeStyle(mapOption.activeBorderColor);
          context.setFillStyle(hexToRgb(mapOption.activeFillColor, mapOption.activeFillOpacity));
        }
      }
      var coorda = data[i].geometry.coordinates
      for (var k = 0; k < coorda.length; k++) {
        coords = coorda[k];
        if (coords.length == 1) {
          coords = coords[0]
        }
        for (var j = 0; j < coords.length; j++) {
          point = coordinateToPoint(coords[j][1], coords[j][0],bounds,scale,xoffset,yoffset)
          if (j === 0) {
            context.beginPath();
            context.moveTo(point.x, point.y);
          } else {
            context.lineTo(point.x, point.y);
          }
        }
        context.fill();
        if(mapOption.border == true){
          context.stroke();
        }
      }
      if(opts.dataLabel == true){
        var centerPoint = data[i].properties.centroid;
        if(centerPoint){
          point = coordinateToPoint(centerPoint[1], centerPoint[0],bounds,scale,xoffset,yoffset);
          let fontSize=data[i].textSize||config.fontSize;
          let text=data[i].properties.name;
          context.beginPath();
          context.setFontSize(fontSize)
          context.setFillStyle(data[i].textColor||'#666666')
          context.fillText(text, point.x-measureText(text,fontSize)/2, point.y+fontSize/2);
          context.closePath();
          context.stroke();
        }
      }
    }
    opts.chartData.mapData={
      bounds:bounds,
      scale:scale,
      xoffset:xoffset,
      yoffset:yoffset
    }
    drawToolTipBridge(opts, config, context,1);
    context.draw();
  }
  
  function getWordCloudPoint(opts,type){
    let points = opts.series.sort(function(a,b){return parseInt(b.textSize)-parseInt(a.textSize);});
    switch (type) {
      case 'normal':
        for (let i = 0; i < points.length; i++) {
          let text = points[i].name;
          let tHeight = points[i].textSize;
          let tWidth = measureText(text,tHeight);
          let x,y;
          let area;
          let breaknum=0;
          while(true) {
              breaknum++;
              x = normalInt(-opts.width/2, opts.width/2,5) - tWidth/2;
              y = normalInt(-opts.height/2, opts.height/2,5) + tHeight/2;
              area=[x-5+opts.width/2,y-5-tHeight+opts.height/2,x+tWidth+5+opts.width/2,y+5+opts.height/2];
              let isCollision = collisionNew(area,points,opts.width,opts.height);
              if (!isCollision) break;
              if (breaknum==1000){
                area=[-100,-100,-100,-100];
                break;
              }
          };
          points[i].area=area;
        }
      break;
      case 'vertical':
        function Spin(){
          //获取均匀随机值，是否旋转，旋转的概率为（1-0.5）
          if (Math.random()>0.7) {
              return true;
          }else {return false};
        };
        for (let i = 0; i < points.length; i++) { 
          let text = points[i].name;
          let tHeight = points[i].textSize;
          let tWidth = measureText(text,tHeight);
          let isSpin = Spin(); 
          let x,y,area,areav;
          let breaknum=0;
          while(true) {
            breaknum++;
            let isCollision;
            if (isSpin) {
                x = normalInt(-opts.width/2, opts.width/2,5) - tWidth/2;
                y = normalInt(-opts.height/2, opts.height/2,5)+tHeight/2;
                area=[y-5-tWidth+opts.width/2,(-x-5+opts.height/2),y+5+opts.width/2,(-x+tHeight+5+opts.height/2)];
                areav=[opts.width-(opts.width/2-opts.height/2)-(-x+tHeight+5+opts.height/2)-5,(opts.height/2-opts.width/2)+(y-5-tWidth+opts.width/2)-5,opts.width-(opts.width/2-opts.height/2)-(-x+tHeight+5+opts.height/2)+tHeight,(opts.height/2-opts.width/2)+(y-5-tWidth+opts.width/2)+tWidth+5];
                isCollision = collisionNew(areav,points,opts.height,opts.width);
            }else{
              x = normalInt(-opts.width/2, opts.width/2,5) - tWidth/2;
              y = normalInt(-opts.height/2, opts.height/2,5)+tHeight/2;
              area=[x-5+opts.width/2,y-5-tHeight+opts.height/2,x+tWidth+5+opts.width/2,y+5+opts.height/2];
              isCollision = collisionNew(area,points,opts.width,opts.height);
            } 
            if (!isCollision) break;
            if (breaknum==1000){
              area=[-1000,-1000,-1000,-1000];
              break;
            }
          };
          if (isSpin) {
            points[i].area=areav;
            points[i].areav=area;
          }else{
            points[i].area=area;
          }
          points[i].rotate=isSpin;
        };
      break;
    }
    return points;
  }
  
  
  function drawWordCloudDataPoints(series, opts, config, context) {
    let process = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 1;
    let wordOption = assign({},{
      type: 'normal',
      autoColors: true
    },opts.extra.word);
    
    context.beginPath();
    context.setFillStyle(opts.background||'#FFFFFF');
    context.rect(0,0,opts.width,opts.height);
    context.fill();
    context.save();
    let points = opts.chartData.wordCloudData;
    context.translate(opts.width/2,opts.height/2);
    
    for(let i=0;i<points.length;i++){
        context.save();
        if(points[i].rotate){
          context.rotate(90 * Math.PI / 180);
        }
        let text = points[i].name;
        let tHeight = points[i].textSize;
        let tWidth = measureText(text,tHeight);
        context.beginPath();
        context.setStrokeStyle(points[i].color);
        context.setFillStyle(points[i].color);
        context.setFontSize(tHeight);
        if(points[i].rotate){
          if(points[i].areav[0]>0){
            if (opts.tooltip) {
              if (opts.tooltip.index == i) {
                context.strokeText(text,(points[i].areav[0]+5-opts.width/2)*process-tWidth*(1-process)/2,(points[i].areav[1]+5+tHeight-opts.height/2)*process);
                }else{
                  context.fillText(text,(points[i].areav[0]+5-opts.width/2)*process-tWidth*(1-process)/2,(points[i].areav[1]+5+tHeight-opts.height/2)*process);
                }
            }else{
              context.fillText(text,(points[i].areav[0]+5-opts.width/2)*process-tWidth*(1-process)/2,(points[i].areav[1]+5+tHeight-opts.height/2)*process);
            } 
          }
        }else{
          if(points[i].area[0]>0){
            if (opts.tooltip) {
              if (opts.tooltip.index == i) {
                context.strokeText(text,(points[i].area[0]+5-opts.width/2)*process-tWidth*(1-process)/2,(points[i].area[1]+5+tHeight-opts.height/2)*process);
              }else{
                context.fillText(text,(points[i].area[0]+5-opts.width/2)*process-tWidth*(1-process)/2,(points[i].area[1]+5+tHeight-opts.height/2)*process);
              }
            }else{
              context.fillText(text,(points[i].area[0]+5-opts.width/2)*process-tWidth*(1-process)/2,(points[i].area[1]+5+tHeight-opts.height/2)*process);
            }
              
          }
        }
        
        context.stroke();
        context.restore();
    }
    context.restore();
  }
  
  function drawFunnelDataPoints(series, opts, config, context) {
    let process = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 1;
    let funnelOption = assign({},{
      activeWidth:10,
      activeOpacity:0.3,
      border:false,
      borderWidth:2,
      borderColor:'#FFFFFF',
      fillOpacity:1,
      labelAlign:'right'
    },opts.extra.funnel);
    let eachSpacing = (opts.height - opts.area[0] - opts.area[2])/series.length;
    let centerPosition = {
      x: opts.area[3] + (opts.width - opts.area[1] - opts.area[3]) / 2,
      y: opts.height-opts.area[2]
    };
    let activeWidth = funnelOption.activeWidth;
    let radius = Math.min((opts.width - opts.area[1] - opts.area[3]) / 2 - activeWidth, (opts.height - opts.area[0] - opts.area[2]) / 2 - activeWidth);
    series = getFunnelDataPoints(series, radius, process);
    context.save();
    context.translate(centerPosition.x,centerPosition.y);
    for(let i=0;i<series.length;i++){
      if(i==0){
        if (opts.tooltip) {
          if (opts.tooltip.index == i) {
            context.beginPath();
            context.setFillStyle(hexToRgb(series[i].color, funnelOption.activeOpacity));
            context.moveTo(-activeWidth, 0);
            context.lineTo(-series[i].radius-activeWidth, -eachSpacing);
            context.lineTo(series[i].radius+activeWidth, -eachSpacing);
            context.lineTo(activeWidth, 0);
            context.lineTo(-activeWidth, 0);
            context.closePath();
            context.fill();
          }
        }
        series[i].funnelArea=[centerPosition.x-series[i].radius,centerPosition.y-eachSpacing,centerPosition.x+series[i].radius,centerPosition.y];
        context.beginPath();
        context.setLineWidth(funnelOption.borderWidth * opts.pixelRatio);
        context.setStrokeStyle(funnelOption.borderColor);
        context.setFillStyle(hexToRgb(series[i].color, funnelOption.fillOpacity));
        context.moveTo(0, 0);
        context.lineTo(-series[i].radius, -eachSpacing);
        context.lineTo(series[i].radius, -eachSpacing);
        context.lineTo(0, 0);
        context.closePath();
        context.fill();
        if(funnelOption.border == true){
          context.stroke();
        }
      }else{
        if (opts.tooltip) {
          if (opts.tooltip.index == i) {
            context.beginPath();
            context.setFillStyle(hexToRgb(series[i].color, funnelOption.activeOpacity));
            context.moveTo(0, 0);
            context.lineTo(-series[i-1].radius-activeWidth, 0);
            context.lineTo(-series[i].radius-activeWidth, -eachSpacing);
            context.lineTo(series[i].radius+activeWidth, -eachSpacing);
            context.lineTo(series[i-1].radius+activeWidth, 0);
            context.lineTo(0, 0);
            context.closePath();
            context.fill();
          }
        }
        series[i].funnelArea=[centerPosition.x-series[i].radius,centerPosition.y-eachSpacing*(i+1),centerPosition.x+series[i].radius,centerPosition.y-eachSpacing*i];
        context.beginPath();
        context.setLineWidth(funnelOption.borderWidth * opts.pixelRatio);
        context.setStrokeStyle(funnelOption.borderColor);
        context.setFillStyle(hexToRgb(series[i].color, funnelOption.fillOpacity));
        context.moveTo(0, 0);
        context.lineTo(-series[i-1].radius, 0);
        context.lineTo(-series[i].radius, -eachSpacing);
        context.lineTo(series[i].radius, -eachSpacing);
        context.lineTo(series[i-1].radius, 0);
        context.lineTo(0, 0);
        context.closePath();
        context.fill();
        if(funnelOption.border == true){
          context.stroke();
        }
      }
      context.translate(0,-eachSpacing)
    }
    context.restore();
    
    if (opts.dataLabel !== false && process === 1) {
      drawFunnelText(series, opts, context, eachSpacing, funnelOption.labelAlign, activeWidth, centerPosition);
    }
    
    return {
      center: centerPosition,
      radius: radius,
      series: series
    };
  }
  
  function drawFunnelText(series, opts, context, eachSpacing, labelAlign,activeWidth, centerPosition){
    for(let i=0;i<series.length;i++){
      let item = series[i];
      let startX,endX,startY,fontSize;
      let text = item.format ? item.format(+item._proportion_.toFixed(2)) : util.toFixed(item._proportion_ * 100) +'%';
      if(labelAlign == 'right'){
        if(i==0){
          startX=(item.funnelArea[2]+centerPosition.x)/2;
        }else{
          startX=(item.funnelArea[2]+series[i-1].funnelArea[2])/2;
        }
        endX=startX+activeWidth*2;
        startY=item.funnelArea[1]+eachSpacing/2;
        fontSize = item.textSize || opts.fontSize;
        context.setLineWidth(1 * opts.pixelRatio);
        context.setStrokeStyle(item.color);
        context.setFillStyle(item.color);
        context.beginPath();
        context.moveTo(startX,startY );
        context.lineTo(endX,startY);
        context.stroke();
        context.closePath();
        context.beginPath();
        context.moveTo(endX, startY);
        context.arc(endX, startY, 2, 0, 2 * Math.PI);
        context.closePath();
        context.fill();
        context.beginPath();
        context.setFontSize(fontSize);
        context.setFillStyle(item.textColor || '#666666');
        context.fillText(text, endX+5, startY + fontSize/2 -2);
        context.closePath();
        context.stroke();
        context.closePath();
      }else{
        if(i==0){
          startX=(item.funnelArea[0]+centerPosition.x)/2;
        }else{
          startX=(item.funnelArea[0]+series[i-1].funnelArea[0])/2;
        }
        endX=startX-activeWidth*2;
        startY=item.funnelArea[1]+eachSpacing/2;
        fontSize = item.textSize || opts.fontSize;
        context.setLineWidth(1 * opts.pixelRatio);
        context.setStrokeStyle(item.color);
        context.setFillStyle(item.color);
        context.beginPath();
        context.moveTo(startX,startY );
        context.lineTo(endX,startY);
        context.stroke();
        context.closePath();
        context.beginPath();
        context.moveTo(endX, startY);
        context.arc(endX, startY, 2, 0, 2 * Math.PI);
        context.closePath();
        context.fill();
        context.beginPath();
        context.setFontSize(fontSize);
        context.setFillStyle(item.textColor || '#666666');
        context.fillText(text, endX-5-measureText(text), startY + fontSize/2 -2);
        context.closePath();
        context.stroke();
        context.closePath();
      }
      
    }
  }
  
  
  function drawCanvas(opts, context) {
    context.draw();
  }
  
  var Timing = {
    easeIn: function easeIn(pos) {
      return Math.pow(pos, 3);
    },
    easeOut: function easeOut(pos) {
      return Math.pow(pos - 1, 3) + 1;
    },
    easeInOut: function easeInOut(pos) {
      if ((pos /= 0.5) < 1) {
        return 0.5 * Math.pow(pos, 3);
      } else {
        return 0.5 * (Math.pow(pos - 2, 3) + 2);
      }
    },
    linear: function linear(pos) {
      return pos;
    }
  };
  
  function Animation(opts) {
    this.isStop = false;
    opts.duration = typeof opts.duration === 'undefined' ? 1000 : opts.duration;
    opts.timing = opts.timing || 'linear';
    var delay = 17;
  
    function createAnimationFrame() {
      if (typeof setTimeout !== 'undefined') {
        return function(step, delay) {
          setTimeout(function() {
            var timeStamp = +new Date();
            step(timeStamp);
          }, delay);
        };
      } else if (typeof requestAnimationFrame !== 'undefined') {
        return requestAnimationFrame;
      } else {
        return function(step) {
          step(null);
        };
      }
    };
    var animationFrame = createAnimationFrame();
    var startTimeStamp = null;
    var _step = function step(timestamp) {
      if (timestamp === null || this.isStop === true) {
        opts.onProcess && opts.onProcess(1);
        opts.onAnimationFinish && opts.onAnimationFinish();
        return;
      }
      if (startTimeStamp === null) {
        startTimeStamp = timestamp;
      }
      if (timestamp - startTimeStamp < opts.duration) {
        var process = (timestamp - startTimeStamp) / opts.duration;
        var timingFunction = Timing[opts.timing];
        process = timingFunction(process);
  
        opts.onProcess && opts.onProcess(process);
        animationFrame(_step, delay);
      } else {
        opts.onProcess && opts.onProcess(1);
        opts.onAnimationFinish && opts.onAnimationFinish();
      }
    };
    _step = _step.bind(this);
    animationFrame(_step, delay);
  }
  
  // stop animation immediately
  // and tigger onAnimationFinish
  Animation.prototype.stop = function() {
    this.isStop = true;
  };
  
  function drawCharts(type, opts, config, context) {
    var _this = this;
    var series = opts.series;
    var categories = opts.categories;
    series = fillSeries(series, opts, config);
    var duration = opts.animation ? opts.duration : 0;
    _this.animationInstance && _this.animationInstance.stop();
    var seriesMA = null;
    if (type == 'candle') {
      let average = assign({}, opts.extra.candle.average);
      if (average.show) {
        seriesMA = calCandleMA(average.day, average.name, average.color, series[0].data);
        seriesMA = fillSeries(seriesMA, opts, config);
        opts.seriesMA = seriesMA;
      } else if (opts.seriesMA) {
        seriesMA = opts.seriesMA = fillSeries(opts.seriesMA, opts, config);
      } else {
        seriesMA = series;
      }
    } else {
      seriesMA = series;
    }
  
    /* 过滤掉show=false的series */
    opts._series_ = series = filterSeries(series);
  
    //重新计算图表区域
  
    opts.area = new Array(4);
    //复位绘图区域
    for (let j = 0; j < 4; j++) {
      opts.area[j] = opts.padding[j];
    }
  
    //通过计算三大区域：图例、X轴、Y轴的大小，确定绘图区域
    var _calLegendData = calLegendData(seriesMA, opts, config, opts.chartData),
      legendHeight = _calLegendData.area.wholeHeight,
      legendWidth = _calLegendData.area.wholeWidth;
      
    switch (opts.legend.position) {
      case 'top':
        opts.area[0] += legendHeight;
        break;
      case 'bottom':
        opts.area[2] += legendHeight;
        break;
      case 'left':
        opts.area[3] += legendWidth;
        break;
      case 'right':
        opts.area[1] += legendWidth;
        break;
    }
  
    let _calYAxisData = {},yAxisWidth = 0;
    if (opts.type === 'line' || opts.type === 'column' || opts.type === 'area' || opts.type === 'mix' || opts.type === 'candle') {
      _calYAxisData = calYAxisData(series, opts, config);
      yAxisWidth = _calYAxisData.yAxisWidth;
      //如果显示Y轴标题
      if(opts.yAxis.showTitle){
        let maxTitleHeight=0;
        for(let i=0;i<opts.yAxis.data.length;i++){
          maxTitleHeight = Math.max(maxTitleHeight,opts.yAxis.data[i].titleFontSize?opts.yAxis.data[i].titleFontSize:config.fontSize)
        }
        opts.area[0] += (maxTitleHeight+6)*opts.pixelRatio;
      }
      let rightIndex=0,leftIndex=0;
      //计算主绘图区域左右位置
      for(let i=0;i<yAxisWidth.length;i++){
        if(yAxisWidth[i].position=='left'){
          if(leftIndex>0){
            opts.area[3] += yAxisWidth[i].width + opts.yAxis.padding;
          }else{
            opts.area[3] += yAxisWidth[i].width;
          }
          leftIndex +=1;
        }else{
          if(rightIndex>0){
            opts.area[1] += yAxisWidth[i].width + opts.yAxis.padding;
          }else{
            opts.area[1] += yAxisWidth[i].width;
          }
          rightIndex +=1;
        }
      }
    }else{
      config.yAxisWidth = yAxisWidth;
    }
    opts.chartData.yAxisData = _calYAxisData;
  
    if (opts.categories && opts.categories.length) {
      opts.chartData.xAxisData = getXAxisPoints(opts.categories, opts, config);
      let _calCategoriesData = calCategoriesData(opts.categories, opts, config, opts.chartData.xAxisData.eachSpacing),
        xAxisHeight = _calCategoriesData.xAxisHeight,
        angle = _calCategoriesData.angle;
      config.xAxisHeight = xAxisHeight;
      config._xAxisTextAngle_ = angle;
      opts.area[2] += xAxisHeight;
      opts.chartData.categoriesData = _calCategoriesData;
    }else{
  		if (opts.type === 'line' || opts.type === 'area' || opts.type === 'points') {
  			opts.chartData.xAxisData = calXAxisData(series, opts, config);
  			categories=opts.chartData.xAxisData.rangesFormat;
  			let _calCategoriesData = calCategoriesData(categories, opts, config, opts.chartData.xAxisData.eachSpacing),
  			  xAxisHeight = _calCategoriesData.xAxisHeight,
  			  angle = _calCategoriesData.angle;
  			config.xAxisHeight = xAxisHeight;
  			config._xAxisTextAngle_ = angle;
  			opts.area[2] += xAxisHeight;
  			opts.chartData.categoriesData = _calCategoriesData;
  		}else{
  			opts.chartData.xAxisData={
  				xAxisPoints: []
  			};
  		}
  	}
    //计算右对齐偏移距离
    if (opts.enableScroll && opts.xAxis.scrollAlign == 'right' && opts._scrollDistance_ === undefined) {
      let offsetLeft = 0,
        xAxisPoints = opts.chartData.xAxisData.xAxisPoints,
        startX = opts.chartData.xAxisData.startX,
        endX = opts.chartData.xAxisData.endX,
        eachSpacing = opts.chartData.xAxisData.eachSpacing;
      let totalWidth = eachSpacing * (xAxisPoints.length - 1);
      let screenWidth = endX - startX;
      offsetLeft = screenWidth - totalWidth;
      _this.scrollOption = {
        currentOffset: offsetLeft,
        startTouchX: offsetLeft,
        distance: 0,
        lastMoveTime: 0
      };
      opts._scrollDistance_ = offsetLeft;
    }
  
    if (type === 'pie' || type === 'ring' || type === 'rose') {
      config._pieTextMaxLength_ = opts.dataLabel === false ? 0 : getPieTextMaxLength(seriesMA);
    }
  
    switch (type) {
      case 'word':
        let wordOption = assign({},{
          type: 'normal',
          autoColors: true
        },opts.extra.word);
        if(opts.updateData==true || opts.updateData==undefined){
          opts.chartData.wordCloudData=getWordCloudPoint(opts,wordOption.type);
        }
        this.animationInstance = new Animation({
          timing: 'easeInOut',
          duration: duration,
          onProcess: function(process) {
            context.clearRect(0, 0, opts.width, opts.height);
            if (opts.rotate) {
              contextRotate(context, opts);
            }
            drawWordCloudDataPoints(series, opts, config, context,process);
            drawCanvas(opts, context);
          },
          onAnimationFinish: function onAnimationFinish() {
            _this.event.trigger('renderComplete');
          }
        });
      break;
      case 'map':
        context.clearRect(0, 0, opts.width, opts.height);
        drawMapDataPoints(series, opts, config, context);
      break;
      case 'funnel':
        this.animationInstance = new Animation({
          timing: 'easeInOut',
          duration: duration,
          onProcess: function(process) {
            context.clearRect(0, 0, opts.width, opts.height);
            if (opts.rotate) {
              contextRotate(context, opts);
            }
            opts.chartData.funnelData = drawFunnelDataPoints(series, opts, config, context, process);
            drawLegend(opts.series, opts, config, context, opts.chartData);
            drawToolTipBridge(opts, config, context, process);
            drawCanvas(opts, context);
          },
          onAnimationFinish: function onAnimationFinish() {
            _this.event.trigger('renderComplete');
          }
        });
      break;
      case 'line':
        this.animationInstance = new Animation({
          timing: 'easeIn',
          duration: duration,
          onProcess: function onProcess(process) {
            context.clearRect(0, 0, opts.width, opts.height);
            if (opts.rotate) {
              contextRotate(context, opts);
            }
            drawYAxisGrid(categories, opts, config, context);
            drawXAxis(categories, opts, config, context);
            var _drawLineDataPoints = drawLineDataPoints(series, opts, config, context, process),
              xAxisPoints = _drawLineDataPoints.xAxisPoints,
              calPoints = _drawLineDataPoints.calPoints,
              eachSpacing = _drawLineDataPoints.eachSpacing;
            opts.chartData.xAxisPoints = xAxisPoints;
            opts.chartData.calPoints = calPoints;
            opts.chartData.eachSpacing = eachSpacing;
            drawYAxis(series, opts, config, context);
            if (opts.enableMarkLine !== false && process === 1) {
              drawMarkLine(opts, config, context);
            }
            drawLegend(opts.series, opts, config, context, opts.chartData);
            drawToolTipBridge(opts, config, context, process, eachSpacing, xAxisPoints);
            drawCanvas(opts, context);
  
          },
          onAnimationFinish: function onAnimationFinish() {
            _this.event.trigger('renderComplete');
          }
        });
        break;
      case 'mix':
        this.animationInstance = new Animation({
          timing: 'easeIn',
          duration: duration,
          onProcess: function onProcess(process) {
            context.clearRect(0, 0, opts.width, opts.height);
            if (opts.rotate) {
              contextRotate(context, opts);
            }
            drawYAxisGrid(categories, opts, config, context);
            drawXAxis(categories, opts, config, context);
            var _drawMixDataPoints = drawMixDataPoints(series, opts, config, context, process),
              xAxisPoints = _drawMixDataPoints.xAxisPoints,
              calPoints = _drawMixDataPoints.calPoints,
              eachSpacing = _drawMixDataPoints.eachSpacing;
            opts.chartData.xAxisPoints = xAxisPoints;
            opts.chartData.calPoints = calPoints;
            opts.chartData.eachSpacing = eachSpacing;
            drawYAxis(series, opts, config, context);
            if (opts.enableMarkLine !== false && process === 1) {
              drawMarkLine(opts, config, context);
            }
            drawLegend(opts.series, opts, config, context, opts.chartData);
            drawToolTipBridge(opts, config, context, process, eachSpacing, xAxisPoints);
            drawCanvas(opts, context);
          },
          onAnimationFinish: function onAnimationFinish() {
            _this.event.trigger('renderComplete');
          }
        });
        break;
      case 'column':
        this.animationInstance = new Animation({
          timing: 'easeIn',
          duration: duration,
          onProcess: function onProcess(process) {
            context.clearRect(0, 0, opts.width, opts.height);
            if (opts.rotate) {
              contextRotate(context, opts);
            }
            drawYAxisGrid(categories, opts, config, context);
            drawXAxis(categories, opts, config, context);
            var _drawColumnDataPoints = drawColumnDataPoints(series, opts, config, context, process),
              xAxisPoints = _drawColumnDataPoints.xAxisPoints,
              calPoints = _drawColumnDataPoints.calPoints,
              eachSpacing = _drawColumnDataPoints.eachSpacing;
            opts.chartData.xAxisPoints = xAxisPoints;
            opts.chartData.calPoints = calPoints;
            opts.chartData.eachSpacing = eachSpacing;
            drawYAxis(series, opts, config, context);
            if (opts.enableMarkLine !== false && process === 1) {
              drawMarkLine(opts, config, context);
            }
            drawLegend(opts.series, opts, config, context, opts.chartData);
            drawToolTipBridge(opts, config, context, process, eachSpacing, xAxisPoints);
            drawCanvas(opts, context);
          },
          onAnimationFinish: function onAnimationFinish() {
            _this.event.trigger('renderComplete');
          }
        });
        break;
      case 'area':
        this.animationInstance = new Animation({
          timing: 'easeIn',
          duration: duration,
          onProcess: function onProcess(process) {
            context.clearRect(0, 0, opts.width, opts.height);
            if (opts.rotate) {
              contextRotate(context, opts);
            }
            drawYAxisGrid(categories, opts, config, context);
            drawXAxis(categories, opts, config, context);
            var _drawAreaDataPoints = drawAreaDataPoints(series, opts, config, context, process),
              xAxisPoints = _drawAreaDataPoints.xAxisPoints,
              calPoints = _drawAreaDataPoints.calPoints,
              eachSpacing = _drawAreaDataPoints.eachSpacing;
            opts.chartData.xAxisPoints = xAxisPoints;
            opts.chartData.calPoints = calPoints;
            opts.chartData.eachSpacing = eachSpacing;
            drawYAxis(series, opts, config, context);
            if (opts.enableMarkLine !== false && process === 1) {
              drawMarkLine(opts, config, context);
            }
            drawLegend(opts.series, opts, config, context, opts.chartData);
            drawToolTipBridge(opts, config, context, process, eachSpacing, xAxisPoints);
            drawCanvas(opts, context);
          },
          onAnimationFinish: function onAnimationFinish() {
            _this.event.trigger('renderComplete');
          }
        });
        break;
      case 'ring':
      case 'pie':
        this.animationInstance = new Animation({
          timing: 'easeInOut',
          duration: duration,
          onProcess: function onProcess(process) {
            context.clearRect(0, 0, opts.width, opts.height);
            if (opts.rotate) {
              contextRotate(context, opts);
            }
            opts.chartData.pieData = drawPieDataPoints(series, opts, config, context, process);
            drawLegend(opts.series, opts, config, context, opts.chartData);
            drawToolTipBridge(opts, config, context, process);
            drawCanvas(opts, context);
          },
          onAnimationFinish: function onAnimationFinish() {
            _this.event.trigger('renderComplete');
          }
        });
        break;
      case 'rose':
        this.animationInstance = new Animation({
          timing: 'easeInOut',
          duration: duration,
          onProcess: function onProcess(process) {
            context.clearRect(0, 0, opts.width, opts.height);
            if (opts.rotate) {
              contextRotate(context, opts);
            }
            opts.chartData.pieData = drawRoseDataPoints(series, opts, config, context, process);
            drawLegend(opts.series, opts, config, context, opts.chartData);
            drawToolTipBridge(opts, config, context, process);
            drawCanvas(opts, context);
          },
          onAnimationFinish: function onAnimationFinish() {
            _this.event.trigger('renderComplete');
          }
        });
        break;
      case 'radar':
        this.animationInstance = new Animation({
          timing: 'easeInOut',
          duration: duration,
          onProcess: function onProcess(process) {
            context.clearRect(0, 0, opts.width, opts.height);
            if (opts.rotate) {
              contextRotate(context, opts);
            }
            opts.chartData.radarData = drawRadarDataPoints(series, opts, config, context, process);
            drawLegend(opts.series, opts, config, context, opts.chartData);
            drawToolTipBridge(opts, config, context, process);
            drawCanvas(opts, context);
          },
          onAnimationFinish: function onAnimationFinish() {
            _this.event.trigger('renderComplete');
          }
        });
        break;
      case 'arcbar':
        this.animationInstance = new Animation({
          timing: 'easeInOut',
          duration: duration,
          onProcess: function onProcess(process) {
            context.clearRect(0, 0, opts.width, opts.height);
            if (opts.rotate) {
              contextRotate(context, opts);
            }
            opts.chartData.arcbarData = drawArcbarDataPoints(series, opts, config, context, process);
            drawCanvas(opts, context);
          },
          onAnimationFinish: function onAnimationFinish() {
            _this.event.trigger('renderComplete');
          }
        });
        break;
      case 'gauge':
        this.animationInstance = new Animation({
          timing: 'easeInOut',
          duration: duration,
          onProcess: function onProcess(process) {
            context.clearRect(0, 0, opts.width, opts.height);
            if (opts.rotate) {
              contextRotate(context, opts);
            }
            opts.chartData.gaugeData = drawGaugeDataPoints(categories, series, opts, config, context, process);
            drawCanvas(opts, context);
          },
          onAnimationFinish: function onAnimationFinish() {
            _this.event.trigger('renderComplete');
          }
        });
        break;
      case 'candle':
        this.animationInstance = new Animation({
          timing: 'easeIn',
          duration: duration,
          onProcess: function onProcess(process) {
            context.clearRect(0, 0, opts.width, opts.height);
            if (opts.rotate) {
              contextRotate(context, opts);
            }
            drawYAxisGrid(categories, opts, config, context);
            drawXAxis(categories, opts, config, context);
            var _drawCandleDataPoints = drawCandleDataPoints(series, seriesMA, opts, config, context, process),
              xAxisPoints = _drawCandleDataPoints.xAxisPoints,
              calPoints = _drawCandleDataPoints.calPoints,
              eachSpacing = _drawCandleDataPoints.eachSpacing;
            opts.chartData.xAxisPoints = xAxisPoints;
            opts.chartData.calPoints = calPoints;
            opts.chartData.eachSpacing = eachSpacing;
            drawYAxis(series, opts, config, context);
            if (opts.enableMarkLine !== false && process === 1) {
              drawMarkLine(opts, config, context);
            }
            if (seriesMA) {
              drawLegend(seriesMA, opts, config, context, opts.chartData);
            } else {
              drawLegend(opts.series, opts, config, context, opts.chartData);
            }
            drawToolTipBridge(opts, config, context, process, eachSpacing, xAxisPoints);
            drawCanvas(opts, context);
          },
          onAnimationFinish: function onAnimationFinish() {
            _this.event.trigger('renderComplete');
          }
        });
        break;
    }
  }
  
  // simple event implement
  
  function Event() {
    this.events = {};
  }
  
  Event.prototype.addEventListener = function(type, listener) {
    this.events[type] = this.events[type] || [];
    this.events[type].push(listener);
  };
  
  Event.prototype.trigger = function() {
    for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
      args[_key] = arguments[_key];
    }
  
    var type = args[0];
    var params = args.slice(1);
    if (!!this.events[type]) {
      this.events[type].forEach(function(listener) {
        try {
          listener.apply(null, params);
        } catch (e) {
          console.error(e);
        }
      });
    }
  };
  
  var Charts = function Charts(opts) {
    opts.pixelRatio = opts.pixelRatio ? opts.pixelRatio : 1;
    opts.fontSize = opts.fontSize ? opts.fontSize * opts.pixelRatio : 13 * opts.pixelRatio;
    opts.title = assign({}, opts.title);
    opts.subtitle = assign({}, opts.subtitle);
    opts.duration = opts.duration ? opts.duration : 1000;
    opts.yAxis = assign({}, {
      data:[],
      showTitle:false,
      disabled:false,
      disableGrid:false,
      splitNumber:5,
      gridType: 'solid',
      dashLength: 4 * opts.pixelRatio,
      gridColor:'#cccccc',
      padding:10,
      fontColor:'#666666'
    }, opts.yAxis);
    opts.yAxis.dashLength *= opts.pixelRatio;
    opts.yAxis.padding *= opts.pixelRatio;
    opts.xAxis = assign({}, {
      rotateLabel: false,
      type: 'calibration',
      gridType: 'solid',
      dashLength: 4,
      scrollAlign: 'left',
      boundaryGap:'center',
      axisLine:true,
      axisLineColor:'#cccccc'
    }, opts.xAxis);
    opts.xAxis.dashLength *= opts.pixelRatio;
    opts.legend = assign({}, {
      show: true,
      position: 'bottom',
      float: 'center',
      backgroundColor: 'rgba(0,0,0,0)',
      borderColor: 'rgba(0,0,0,0)',
      borderWidth: 0,
      padding: 5,
      margin: 5,
      itemGap: 10,
      fontSize: opts.fontSize,
      lineHeight: opts.fontSize,
      fontColor: '#333333',
      format: {},
      hiddenColor: '#CECECE'
    }, opts.legend);
    opts.legend.borderWidth = opts.legend.borderWidth * opts.pixelRatio;
    opts.legend.itemGap = opts.legend.itemGap * opts.pixelRatio;
    opts.legend.padding = opts.legend.padding * opts.pixelRatio;
    opts.legend.margin = opts.legend.margin * opts.pixelRatio;
    opts.extra = assign({}, opts.extra);
    opts.rotate = opts.rotate ? true : false;
    opts.animation = opts.animation ? true : false;
  	opts.rotate = opts.rotate ? true : false;
  
    let config$$1 = JSON.parse(JSON.stringify(config));
    config$$1.colors = opts.colors ? opts.colors : config$$1.colors;
    config$$1.yAxisTitleWidth = opts.yAxis.disabled !== true && opts.yAxis.title ? config$$1.yAxisTitleWidth : 0;
    if (opts.type == 'pie' || opts.type == 'ring') {
      config$$1.pieChartLinePadding = opts.dataLabel === false ? 0 : opts.extra.pie.labelWidth * opts.pixelRatio || config$$1.pieChartLinePadding * opts.pixelRatio;
    }
    if (opts.type == 'rose') {
      config$$1.pieChartLinePadding = opts.dataLabel === false ? 0 : opts.extra.rose.labelWidth * opts.pixelRatio || config$$1.pieChartLinePadding * opts.pixelRatio;
    }
    config$$1.pieChartTextPadding = opts.dataLabel === false ? 0 : config$$1.pieChartTextPadding * opts.pixelRatio;
    config$$1.yAxisSplit = opts.yAxis.splitNumber ? opts.yAxis.splitNumber : config.yAxisSplit;
  
    //屏幕旋转
    config$$1.rotate = opts.rotate;
    if (opts.rotate) {
      let tempWidth = opts.width;
      let tempHeight = opts.height;
      opts.width = tempHeight;
      opts.height = tempWidth;
    }
  
    //适配高分屏
    opts.padding = opts.padding ? opts.padding : config$$1.padding;
    for (let i = 0; i < 4; i++) {
      opts.padding[i] *= opts.pixelRatio;
    }
    config$$1.yAxisWidth = config.yAxisWidth * opts.pixelRatio;
    config$$1.xAxisHeight = config.xAxisHeight * opts.pixelRatio;
    if (opts.enableScroll && opts.xAxis.scrollShow) {
      config$$1.xAxisHeight += 6 * opts.pixelRatio;
    }
    config$$1.xAxisLineHeight = config.xAxisLineHeight * opts.pixelRatio;
    config$$1.fontSize = opts.fontSize;
    config$$1.titleFontSize = config.titleFontSize * opts.pixelRatio;
    config$$1.subtitleFontSize = config.subtitleFontSize * opts.pixelRatio;
    config$$1.toolTipPadding = config.toolTipPadding * opts.pixelRatio;
    config$$1.toolTipLineHeight = config.toolTipLineHeight * opts.pixelRatio;
    config$$1.columePadding = config.columePadding * opts.pixelRatio;
    opts.$this = opts.$this ? opts.$this : this;
    
    this.context = uni.createCanvasContext(opts.canvasId, opts.$this);
    /* 兼容原生H5
    this.context = document.getElementById(opts.canvasId).getContext("2d");
    this.context.setStrokeStyle = function(e){ return this.strokeStyle=e; }
    this.context.setLineWidth = function(e){ return this.lineWidth=e; }
    this.context.setLineCap = function(e){ return this.lineCap=e; }
    this.context.setFontSize = function(e){ return this.font=e+"px sans-serif"; }
    this.context.setFillStyle = function(e){ return this.fillStyle=e; }
    this.context.draw = function(){ }
    */
  
    opts.chartData = {};
    this.event = new Event();
    this.scrollOption = {
      currentOffset: 0,
      startTouchX: 0,
      distance: 0,
      lastMoveTime: 0
    };
  
    this.opts = opts;
    this.config = config$$1;
  
    drawCharts.call(this, opts.type, opts, config$$1, this.context);
  };
  
  Charts.prototype.updateData = function() {
    let data = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
    this.opts = assign({}, this.opts, data);
    this.opts.updateData = true;
    let scrollPosition = data.scrollPosition || 'current';
    switch (scrollPosition) {
      case 'current':
        this.opts._scrollDistance_ = this.scrollOption.currentOffset;
        break;
      case 'left':
        this.opts._scrollDistance_ = 0;
        this.scrollOption = {
          currentOffset: 0,
          startTouchX: 0,
          distance: 0,
          lastMoveTime: 0
        };
        break;
      case 'right':
        let _calYAxisData = calYAxisData(this.opts.series, this.opts, this.config),
          yAxisWidth = _calYAxisData.yAxisWidth;
        this.config.yAxisWidth = yAxisWidth;
        let offsetLeft = 0;
        let _getXAxisPoints0 = getXAxisPoints(this.opts.categories, this.opts, this.config),
          xAxisPoints = _getXAxisPoints0.xAxisPoints,
          startX = _getXAxisPoints0.startX,
          endX = _getXAxisPoints0.endX,
          eachSpacing = _getXAxisPoints0.eachSpacing;
        let totalWidth = eachSpacing * (xAxisPoints.length - 1);
        let screenWidth = endX - startX;
        offsetLeft = screenWidth - totalWidth;
        this.scrollOption = {
          currentOffset: offsetLeft,
          startTouchX: offsetLeft,
          distance: 0,
          lastMoveTime: 0
        };
        this.opts._scrollDistance_ = offsetLeft;
        break;
    }
    drawCharts.call(this, this.opts.type, this.opts, this.config, this.context);
  };
  
  Charts.prototype.zoom = function() {
    var val = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.opts.xAxis.itemCount;
    if (this.opts.enableScroll !== true) {
      console.log('请启用滚动条后使用！')
      return;
    }
    //当前屏幕中间点
    let centerPoint = Math.round(Math.abs(this.scrollOption.currentOffset) / this.opts.chartData.eachSpacing) + Math.round(
      this.opts.xAxis.itemCount / 2);
    this.opts.animation = false;
    this.opts.xAxis.itemCount = val.itemCount;
    //重新计算x轴偏移距离
    let _calYAxisData = calYAxisData(this.opts.series, this.opts, this.config),
      yAxisWidth = _calYAxisData.yAxisWidth;
    this.config.yAxisWidth = yAxisWidth;
    let offsetLeft = 0;
    let _getXAxisPoints0 = getXAxisPoints(this.opts.categories, this.opts, this.config),
      xAxisPoints = _getXAxisPoints0.xAxisPoints,
      startX = _getXAxisPoints0.startX,
      endX = _getXAxisPoints0.endX,
      eachSpacing = _getXAxisPoints0.eachSpacing;
    let centerLeft = eachSpacing * centerPoint;
    let screenWidth = endX - startX;
    let MaxLeft = screenWidth - eachSpacing * (xAxisPoints.length - 1);
    offsetLeft = screenWidth / 2 - centerLeft;
    if (offsetLeft > 0) {
      offsetLeft = 0;
    }
    if (offsetLeft < MaxLeft) {
      offsetLeft = MaxLeft;
    }
    this.scrollOption = {
      currentOffset: offsetLeft,
      startTouchX: offsetLeft,
      distance: 0,
      lastMoveTime: 0
    };
    this.opts._scrollDistance_ = offsetLeft;
    drawCharts.call(this, this.opts.type, this.opts, this.config, this.context);
  };
  
  Charts.prototype.stopAnimation = function() {
    this.animationInstance && this.animationInstance.stop();
  };
  
  Charts.prototype.addEventListener = function(type, listener) {
    this.event.addEventListener(type, listener);
  };
  
  Charts.prototype.getCurrentDataIndex = function(e) {
    var touches = null;
    if (e.changedTouches) {
      touches = e.changedTouches[0];
    } else {
      touches = e.mp.changedTouches[0];
    }
    if (touches) {
      let _touches$ = getTouches(touches, this.opts, e);
      if (this.opts.type === 'pie' || this.opts.type === 'ring' || this.opts.type === 'rose') {
        return findPieChartCurrentIndex({
          x: _touches$.x,
          y: _touches$.y
        }, this.opts.chartData.pieData);
      } else if (this.opts.type === 'radar') {
        return findRadarChartCurrentIndex({
          x: _touches$.x,
          y: _touches$.y
        }, this.opts.chartData.radarData, this.opts.categories.length);
      } else if (this.opts.type === 'funnel') {
        return findFunnelChartCurrentIndex({
          x: _touches$.x,
          y: _touches$.y
        }, this.opts.chartData.funnelData);
      } else if (this.opts.type === 'map') {
        return findMapChartCurrentIndex({
          x: _touches$.x,
          y: _touches$.y
        }, this.opts);
      }else if (this.opts.type === 'word') {
        return findWordChartCurrentIndex({
          x: _touches$.x,
          y: _touches$.y
        }, this.opts.chartData.wordCloudData);
      } else {
        return findCurrentIndex({
          x: _touches$.x,
          y: _touches$.y
        }, this.opts.chartData.calPoints, this.opts, this.config, Math.abs(this.scrollOption.currentOffset));
      }
    }
    return -1;
  };
  
  Charts.prototype.getLegendDataIndex = function(e) {
    var touches = null;
    if (e.changedTouches) {
      touches = e.changedTouches[0];
    } else {
      touches = e.mp.changedTouches[0];
    }
    if (touches) {
      let _touches$ = getTouches(touches, this.opts, e);
      return findLegendIndex({
        x: _touches$.x,
        y: _touches$.y
      }, this.opts.chartData.legendData);
    }
    return -1;
  };
  
  Charts.prototype.touchLegend = function(e) {
    var option = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    var touches = null;
    if (e.changedTouches) {
      touches = e.changedTouches[0];
    } else {
      touches = e.mp.changedTouches[0];
    }
    if (touches) {
      var _touches$ = getTouches(touches, this.opts, e);
      var index = this.getLegendDataIndex(e);
      if (index >= 0) {
        this.opts.series[index].show = !this.opts.series[index].show;
        this.opts.animation = option.animation ? true : false;
  			this.opts._scrollDistance_= this.scrollOption.currentOffset;
        drawCharts.call(this, this.opts.type, this.opts, this.config, this.context);
      }
    }
  
  };
  
  Charts.prototype.showToolTip = function(e) {
    var option = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    var touches = null;
    if (e.changedTouches) {
      touches = e.changedTouches[0];
    } else {
      touches = e.mp.changedTouches[0];
    }
    if (!touches) {
      console.log("touchError");
    }
    var _touches$ = getTouches(touches, this.opts, e);
    var currentOffset = this.scrollOption.currentOffset;
    var opts = assign({}, this.opts, {
      _scrollDistance_: currentOffset,
      animation: false
    });
    if (this.opts.type === 'line' || this.opts.type === 'area' || this.opts.type === 'column') {
      var index = option.index==undefined? this.getCurrentDataIndex(e):option.index ;
      if (index > -1) {
        var seriesData = getSeriesDataItem(this.opts.series, index);
        if (seriesData.length !== 0) {
          var _getToolTipData = getToolTipData(seriesData, this.opts.chartData.calPoints, index, this.opts.categories,option),
            textList = _getToolTipData.textList,
            offset = _getToolTipData.offset;
          offset.y = _touches$.y;
          opts.tooltip = {
            textList: option.textList?option.textList:textList,
            offset: offset,
            option: option,
            index: index
          };
        }
      }
      drawCharts.call(this, opts.type, opts, this.config, this.context);
    }
    if (this.opts.type === 'mix') {
      var index = option.index==undefined? this.getCurrentDataIndex(e):option.index ;
      if (index > -1) {
        var currentOffset = this.scrollOption.currentOffset;
        var opts = assign({}, this.opts, {
          _scrollDistance_: currentOffset,
          animation: false
        });
        var seriesData = getSeriesDataItem(this.opts.series, index);
        if (seriesData.length !== 0) {
          var _getMixToolTipData = getMixToolTipData(seriesData, this.opts.chartData.calPoints, index, this.opts.categories,option),
            textList = _getMixToolTipData.textList,
            offset = _getMixToolTipData.offset;
          offset.y = _touches$.y;
          opts.tooltip = {
            textList: option.textList?option.textList:textList,
            offset: offset,
            option: option,
            index: index
          };
        }
      }
      drawCharts.call(this, opts.type, opts, this.config, this.context);
    }
    if (this.opts.type === 'candle') {
      var index = option.index==undefined? this.getCurrentDataIndex(e):option.index ;
      if (index > -1) {
        var currentOffset = this.scrollOption.currentOffset;
        var opts = assign({}, this.opts, {
          _scrollDistance_: currentOffset,
          animation: false
        });
        var seriesData = getSeriesDataItem(this.opts.series, index);
        if (seriesData.length !== 0) {
          var _getToolTipData = getCandleToolTipData(this.opts.series[0].data, seriesData, this.opts.chartData.calPoints,
              index, this.opts.categories, this.opts.extra.candle, option),
            textList = _getToolTipData.textList,
            offset = _getToolTipData.offset;
          offset.y = _touches$.y;
          opts.tooltip = {
            textList: option.textList?option.textList:textList,
            offset: offset,
            option: option,
            index: index
          };
        }
      }
      drawCharts.call(this, opts.type, opts, this.config, this.context);
    }
    if (this.opts.type === 'pie' || this.opts.type === 'ring' || this.opts.type === 'rose'||this.opts.type === 'funnel' ) {
      var index = option.index==undefined? this.getCurrentDataIndex(e):option.index ;
      if (index > -1) {
        var currentOffset = this.scrollOption.currentOffset;
        var opts = assign({}, this.opts, {
          _scrollDistance_: currentOffset,
          animation: false
        });
        var seriesData = this.opts._series_[index];
        var textList = [{
          text: option.format ? option.format(seriesData) : seriesData.name + ': ' + seriesData.data,
          color: seriesData.color
        }];
        var offset = {
          x: _touches$.x,
          y: _touches$.y
        };
        opts.tooltip = {
          textList: option.textList?option.textList:textList,
          offset: offset,
          option: option,
          index: index
        };
      }
      drawCharts.call(this, opts.type, opts, this.config, this.context);
    }
    if (this.opts.type === 'map'||this.opts.type === 'word') {
      var index = option.index==undefined? this.getCurrentDataIndex(e):option.index ;
      if (index > -1) {
        var currentOffset = this.scrollOption.currentOffset;
        var opts = assign({}, this.opts, {
          _scrollDistance_: currentOffset,
          animation: false
        });
        var seriesData = this.opts._series_[index];
        var textList = [{
          text: option.format ? option.format(seriesData) : seriesData.properties.name ,
          color: seriesData.color
        }];
        var offset = {
          x: _touches$.x,
          y: _touches$.y
        };
        opts.tooltip = {
          textList: option.textList?option.textList:textList,
          offset: offset,
          option: option,
          index: index
        };
      }
      opts.updateData = false;
      drawCharts.call(this, opts.type, opts, this.config, this.context);
    }
    if (this.opts.type === 'radar') {
      var index = option.index==undefined? this.getCurrentDataIndex(e):option.index ;
      if (index > -1) {
        var currentOffset = this.scrollOption.currentOffset;
        var opts = assign({}, this.opts, {
          _scrollDistance_: currentOffset,
          animation: false
        });
        var seriesData = getSeriesDataItem(this.opts.series, index);
        if (seriesData.length !== 0) {
          var textList = seriesData.map(function(item) {
            return {
              text: option.format ? option.format(item) : item.name + ': ' + item.data,
              color: item.color
            };
          });
          var offset = {
            x: _touches$.x,
            y: _touches$.y
          };
          opts.tooltip = {
            textList: option.textList?option.textList:textList,
            offset: offset,
            option: option,
            index: index
          };
        }
      }
      drawCharts.call(this, opts.type, opts, this.config, this.context);
    }
  };
  
  Charts.prototype.translate = function(distance) {
    this.scrollOption = {
      currentOffset: distance,
      startTouchX: distance,
      distance: 0,
      lastMoveTime: 0
    };
    let opts = assign({}, this.opts, {
      _scrollDistance_: distance,
      animation: false
    });
    drawCharts.call(this, this.opts.type, opts, this.config, this.context);
  };
  
  Charts.prototype.scrollStart = function(e) {
    var touches = null;
    if (e.changedTouches) {
      touches = e.changedTouches[0];
    } else {
      touches = e.mp.changedTouches[0];
    }
    var _touches$ = getTouches(touches, this.opts, e);
    if (touches && this.opts.enableScroll === true) {
      this.scrollOption.startTouchX = _touches$.x;
    }
  };
  
  Charts.prototype.scroll = function(e) {
    if (this.scrollOption.lastMoveTime === 0) {
      this.scrollOption.lastMoveTime = Date.now();
    }
    let Limit = this.opts.extra.touchMoveLimit || 20;
    let currMoveTime = Date.now();
    let duration = currMoveTime - this.scrollOption.lastMoveTime;
    if (duration < Math.floor(1000 / Limit)) return;
    this.scrollOption.lastMoveTime = currMoveTime;
    var touches = null;
    if (e.changedTouches) {
      touches = e.changedTouches[0];
    } else {
      touches = e.mp.changedTouches[0];
    }
    if (touches && this.opts.enableScroll === true) {
      var _touches$ = getTouches(touches, this.opts, e);
      var _distance;
      _distance = _touches$.x - this.scrollOption.startTouchX;
      var currentOffset = this.scrollOption.currentOffset;
      var validDistance = calValidDistance(this,currentOffset + _distance, this.opts.chartData, this.config, this.opts);
      this.scrollOption.distance = _distance = validDistance - currentOffset;
      var opts = assign({}, this.opts, {
        _scrollDistance_: currentOffset + _distance,
        animation: false
      });
      drawCharts.call(this, opts.type, opts, this.config, this.context);
      return currentOffset + _distance;
    }
  };
  
  Charts.prototype.scrollEnd = function(e) {
    if (this.opts.enableScroll === true) {
      var _scrollOption = this.scrollOption,
        currentOffset = _scrollOption.currentOffset,
        distance = _scrollOption.distance;
      this.scrollOption.currentOffset = currentOffset + distance;
      this.scrollOption.distance = 0;
    }
  };
  if (typeof module === "object" && typeof module.exports === "object") {
    module.exports = Charts;
    //export default Charts;//建议使用nodejs的module导出方式，如报错请使用export方式导出
  }
  