问题介绍:
Echarts插件的坐标轴类型主要分为时间(time)、数值(value)、对数(log)、类目(category)四大类,当采用二维的直角坐标系时,一般x轴通过采用类目轴,而y轴通常采用value轴。官方的有关的直角坐标系例子,大多遵循这个原则配置的。这样配置其实只适用于离散型数据,而对于时间型数据,虽然有时间轴的支持,但是效果却差强人意,甚至存在展示的缺陷。

如图1所示为日的时间型数据,从图上可以看出柱状图的柱子跟y轴有重叠,x轴刻度值少的问题。如图2、3所示为月和年的时间型数据,问题跟图1差不多,但还有一个不一样的问题,就是刻度值,不管是月还是年的数据,刻度值都显示的日的刻度值,这个对于用户来说,体验应该是不好的。更好的体验应该是,月的时间型数据,显示的刻度值应该都是月的刻度值;年的时间型数据,显示的应该都是年的刻度值。如图4、5、6,是本人优化和处理后的显示效果图。

总的来说,主要存在以下三个问题
(1)不同时间格式下,计算出来的刻度不符合预期;
(2)数据的最小值容易与坐标轴重叠,最大值容易显示在图表之外;
(3)计算出来的刻度个数偏少,数据之间容易重叠。

图1 时间格式为日的时间轴展示

图2 时间格式为月的时间轴展示

图3 时间格式为年的时间轴展示

图4 时间格式为日的时间轴展示

图5 时间格式为月的时间轴展示

图6 时间格式为年的时间轴展示

Echarts时间型刻度的计算方法:

通过阅读Echarts源码,可以了解Echarts对于时间型刻度的计算方法: 在Echarts的架构中, echarts/scale/Time模块负责对时间刻度进行处理和计算,但求解刻度的算法还是采用线性比例尺的算法,即将时间型数据转为时间戳(变为纯数字),然后通过线性比例尺求刻度的方法计算得出时间戳类型的刻度,最后通过时间转换函数,将时间戳刻度,转为时间型刻度。而且在源码里面,将数据的最小值和最大值,默认设置为刻度的最小值和最大值,并且刻度个数通常只有五个,偏少。

改进和优化方法:
针对前文总结出的三点缺陷,现提出以下的改进和优化方法:
(1)针对不同时间格式下刻度的计算问题,借鉴d3.js的时间比例尺接口和方法,并将其整合到Echarts源码中。d3.js针对不同时间格式,采用不同函数计算时间刻度,针对年、月、日、小时分别有year、month、day、hour等函数。不同时间格式函数处理刻度方法稍有不同。具体方法下文会详细介绍。
(2)针对最小值容易与坐标轴重叠,最大值容易显示在图形之外问题。采用保留刻度法予以解决。保留刻度法:即数据最小值与刻度最小值之差为正且小于步长20%,或者数据最小值与刻度最小值之差为负,则最小刻度需要再向下增加一个刻度;若数据最大值与刻度最大值之差为负且绝对值小于步长20%,或者数据最小值与刻度最小值之差为正, 则最大刻度需要再向上增加一个刻度。
(3)针对计算出来的刻度个数偏少问题,采用自适应宽度的方法,力求最大的刻度个数。
规定刻度标签的旋转45度,每一个标签占用30px的宽度,这样,如果图表的宽为600px,则理论上可以在坐标轴上最大放置20个刻度。

具体实现:
(1)通过调试d3.js源码和研究,发现d3.js处理时间比例尺的接口主要是以下代码,代码中newInterval可以看做一个构造函数,传入不同的参数,都会返回interval对象,但interval对象的方法却因为参数和变量值的不一样,而有不同的功能。比如:year对象专门处理只与年相关的比例尺,month对象则专门处理与月相关的比例尺,minute对象则专门处理与分钟相关的比例尺…这些对象有1个方法,是计算比例尺的关键,它就是range(start,stop,step)函数,它接受三个参数,start:数据的开始点(数据最小值),stop:数据的终点(数据最大值),step:相邻刻度的步长。以年为例,假设现在数据最小值是2011,最大值是2028年,步长是2。则通过range函数,计算出来的刻度是[1293811200000, 1356969600000, 1420041600000, 1483200000000, 1546272000000, 1609430400000, 1672502400000, 1735660800000, 1798732800000],接着再将时间戳格式化[“2011-01-01 00:00:00”, “2013-01-01 00:00:00”, “2015-01-01 00:00:00”, “2017-01-01 00:00:00”, “2019-01-01 00:00:00”, “2021-01-01 00:00:00”, “2023-01-01 00:00:00”, “2025-01-01 00:00:00”, “2027-01-01 00:00:00”]。从结果可以看出,计算出来的刻度值只是在年份上递增,满足我们的预期,同理,其他月、日、小时的时间格式比例尺计算方法也跟这个类似。

 function(module, exports, __webpack_require__) {var t0$1 = new Date;var t1$1 = new Date;var timeInterval = {};function newInterval(floori, offseti, count, field) {function interval(date) {return floori(date = new Date(+date)), date;}interval.floor = interval;interval.ceil = function(date) {return floori(date = new Date(date - 1)), offseti(date, 1), floori(date), date;};interval.round = function(date) {var d0 = interval(date),d1 = interval.ceil(date);return date - d0 < d1 - date ? d0 : d1;};interval.offset = function(date, step) {return offseti(date = new Date(+date), step == null ? 1 : Math.floor(step)), date;};interval.range = function(start, stop, step) {var range = [];start = interval.ceil(start);step = step == null ? 1 : Math.floor(step);if (!(start < stop) || !(step > 0)) return range; // also handles Invalid Datedo range.push(new Date(+start).getTime()); while (offseti(start, step), floori(start), start < stop)return range;};interval.filter = function(test) {return newInterval(function(date) {if (date >= date) while (floori(date), !test(date)) date.setTime(date - 1);}, function(date, step) {if (date >= date) {if (step < 0) while (++step <= 0) {while (offseti(date, -1), !test(date)) {} // eslint-disable-line no-empty} else while (--step >= 0) {while (offseti(date, +1), !test(date)) {} // eslint-disable-line no-empty}}});};if (count) {interval.count = function(start, end) {t0$1.setTime(+start), t1$1.setTime(+end);floori(t0$1), floori(t1$1);return Math.floor(count(t0$1, t1$1));};interval.every = function(step) {step = Math.floor(step);return !isFinite(step) || !(step > 0) ? null: !(step > 1) ? interval: interval.filter(field? function(d) { return field(d) % step === 0; }: function(d) { return interval.count(0, d) % step === 0; });};}return interval;}var millisecond = newInterval(function() {// noop}, function(date, step) {date.setTime(+date + step);}, function(start, end) {return end - start;});// An optimized implementation for this simple case.millisecond.every = function(k) {k = Math.floor(k);if (!isFinite(k) || !(k > 0)) return null;if (!(k > 1)) return millisecond;return newInterval(function(date) {date.setTime(Math.floor(date / k) * k);}, function(date, step) {date.setTime(+date + step * k);}, function(start, end) {return (end - start) / k;});};var milliseconds = millisecond.range;var durationSecond$1 = 1e3;var durationMinute$1 = 6e4;var durationHour$1 = 36e5;var durationDay$1 = 864e5;var durationWeek$1 = 6048e5;var second = newInterval(function(date) {date.setTime(Math.floor(date / durationSecond$1) * durationSecond$1);}, function(date, step) {date.setTime(+date + step * durationSecond$1);}, function(start, end) {return (end - start) / durationSecond$1;}, function(date) {return date.getUTCSeconds();});var seconds = second.range;var minute = newInterval(function(date) {date.setTime(Math.floor(date / durationMinute$1) * durationMinute$1);}, function(date, step) {date.setTime(+date + step * durationMinute$1);}, function(start, end) {return (end - start) / durationMinute$1;}, function(date) {return date.getMinutes();});var minutes = minute.range;var hour = newInterval(function(date) {var offset = date.getTimezoneOffset() * durationMinute$1 % durationHour$1;if (offset < 0) offset += durationHour$1;date.setTime(Math.floor((+date - offset) / durationHour$1) * durationHour$1 + offset);}, function(date, step) {date.setTime(+date + step * durationHour$1);}, function(start, end) {return (end - start) / durationHour$1;}, function(date) {return date.getHours();});var hours = hour.range;var day = newInterval(function(date) {date.setHours(0, 0, 0, 0);}, function(date, step) {date.setDate(date.getDate() + step);}, function(start, end) {return (end - start - (end.getTimezoneOffset() - start.getTimezoneOffset()) * durationMinute$1) / durationDay$1;}, function(date) {return date.getDate() - 1;});var days = day.range;function weekday(i) {return newInterval(function(date) {date.setDate(date.getDate() - (date.getDay() + 7 - i) % 7);date.setHours(0, 0, 0, 0);}, function(date, step) {date.setDate(date.getDate() + step * 7);}, function(start, end) {return (end - start - (end.getTimezoneOffset() - start.getTimezoneOffset()) * durationMinute$1) / durationWeek$1;});}var sunday = weekday(0);var monday = weekday(1);var tuesday = weekday(2);var wednesday = weekday(3);var thursday = weekday(4);var friday = weekday(5);var saturday = weekday(6);var sundays = sunday.range;var mondays = monday.range;var tuesdays = tuesday.range;var wednesdays = wednesday.range;var thursdays = thursday.range;var fridays = friday.range;var saturdays = saturday.range;var month = newInterval(function(date) {date.setDate(1);date.setHours(0, 0, 0, 0);}, function(date, step) {date.setMonth(date.getMonth() + step);}, function(start, end) {return end.getMonth() - start.getMonth() + (end.getFullYear() - start.getFullYear()) * 12;}, function(date) {return date.getMonth();});var months = month.range;var year = newInterval(function(date) {date.setMonth(0, 1);date.setHours(0, 0, 0, 0);}, function(date, step) {date.setFullYear(date.getFullYear() + step);}, function(start, end) {return end.getFullYear() - start.getFullYear();}, function(date) {return date.getFullYear();});// An optimized implementation for this simple case.year.every = function(k) {return !isFinite(k = Math.floor(k)) || !(k > 0) ? null : newInterval(function(date) {date.setFullYear(Math.floor(date.getFullYear() / k) * k);date.setMonth(0, 1);date.setHours(0, 0, 0, 0);}, function(date, step) {date.setFullYear(date.getFullYear() + step * k);});};var years = year.range;timeInterval.timeYear = year;timeInterval.timeYears = years;timeInterval.timeMonths = months;timeInterval.timeMonth = month;timeInterval.timeSecond = second;timeInterval.timeSeconds = seconds;timeInterval.timeDay = day;timeInterval.timeDays = days;   timeInterval.timeHour = hour;timeInterval.timeHours = hours; timeInterval.timeMinute = minute;timeInterval.timeMinutes = minutes;module.exports = timeInterval;},       

(2)针对第二个问题的解决方法,主要是对(1)中算出的刻度进一步处理,说白了,就是给刻度的两个端点适量留一些空白,以便不影响柱状图等图形显示。具体代码如下所示:

        /*** 优化并调整刻度的最大刻度和最小刻度,* 若存在,直接返回刻度值* @param {Object} params * @param {Array.<Number>} params.ticks: 包含刻度的数组* @param {Array.<Number>} params.extent: 最大刻度和最小刻度数组* @param {Array.<Number>} params.niceTickExtent: 更新后的最大刻度和最小刻度数组* @param {Number} params.splitNum: 刻度轴的分割数目* @param {Function} params.offset: 时间轴时,为时间的偏移函数;数值轴时为null* @param {Number} params.step: 时间轴时,刻度之间的步长* @param {Number} params.intervalPrecision: 数值轴时,刻度值的精度*/helper.adjustTickExtent = function (params) {var ticks = params.ticks,extent = params.extent,niceTickExtent = params.niceTickExtent,splitNum = params.splitNum;var context = this;var interval;var name = extent[0] + '@#' + extent[1] + '&' + splitNum;// 缓存存在直接返回         if (this.cache[name]) {return this.cache[name];}   if (processOnlyNum (params, context)) {return ticks;}   preprocessTicks(params);                    calTickMin(params, extent[0]);calTickMax(params, extent[1]);                                          setAdjustExtent.call(this, niceTickExtent, extent, ticks, splitNum);return ticks;/*** 当数据最大值和最小值相等时* 此时刻度值数目只有三个* @param {Object} params: 包含参数值的对象* @param {Object} context: 执行环境的上下文*/function processOnlyNum (params, context) {var ticks = params.ticks;var offset = params.offset;var adjustInterval = 1;var extent = params.extent;var step = params.step;var onlyNum = params.onlyNum;var intervalPrecision = params.intervalPrecision;//onlyNum表示数据只有一条if (onlyNum != null) {if (offset === null) {ticks.length = 0;adjustInterval = extent[1] - extent[0];ticks[0] = extent[0];ticks[1] = extent[1];onlyNum == extent[0] ? ticks.unshift(extent[0] - adjustInterval) : ticks.push(extent[1] + adjustInterval);              } else {ticks.length = 0;ticks[0] = offset(onlyNum, -step).getTime(); ticks[1] = onlyNum;ticks[2] = offset(onlyNum, step).getTime(); }       setAdjustExtent.call(context, niceTickExtent, extent, ticks, splitNum);return true;} return false;}/*** 预处理刻度,功能:*(1)移除刻度中数值等于extent[0]和extent[1]的刻度*(2)将只包含一个刻度和两个刻度的刻度数组扩展成多个* @param {Object} params: 包含各种参数的对象*/function preprocessTicks(params) {  var ticks = params.ticks;var offset = params.offset;var extent = params.extent;var step = params.step;var intervalPrecision = params.intervalPrecision;//ticks等于1,只有可能是时间轴的情况if (ticks.length == 1) {            ticks.unshift(offset(ticks[0], -step).getTime()); ticks.push(offset(ticks[ticks.length - 1], step).getTime());                    } else if ((ticks.length == 2 || ticks.length == 3) && offset == null) {//当刻度的最小值是数据的最小值时,根据step求出最小刻度tick = ticks[0];                                        if (tick == extent[0] && (tick % step != 0)) {tick = roundNumber(tick - (tick % step) , intervalPrecision);ticks[0] = tick; }//当刻度的最大值是数据的最大值时,根据step求出最大刻度tick = ticks[ticks.length - 1];if (tick == extent[1] && (tick % step != 0)) {                  tick = roundNumber(tick + step - (tick % step) , intervalPrecision);ticks[ticks.length - 1] = tick;}               } else if (ticks.length > 3) {ticks[0] == extent[0] && ticks.shift();ticks[ticks.length - 1] == extent[1] && ticks.pop();}               }/*** 计算刻度的最小刻度* @param {Object} params: 包含各种参数的对象* @param {Number} min:数据的最小值         */          function calTickMin(params, min) {  var ticks = params.ticks;var offset = params.offset;                         var step = params.step;         var intervalPrecision = params.intervalPrecision;   var interval = offset === null ? step : (ticks[1] - ticks[0]);                      var i = 0, tickMin, differ, tick;           while (true) {i++;if (i == 3) {break;}tickMin = ticks[0];differ = min - tickMin;                                if (differ > 0 && differ >= interval * 0.2 ) {                  break;}   /** 若数据最小值与刻度最小值之差为正且小于步长20%,* 或者数据最小值与刻度最小值之差为负* 则最小刻度需要再向下增加一个刻度    */                      if ((differ > 0 && differ < interval * 0.2) || differ <= 0) {   //数值轴if (offset == null) {tick = roundNumber(tickMin - step, intervalPrecision);//时间轴} else {tick = offset(tickMin, -step).getTime();}                                   ticks.unshift(tick);                }}   }/*** 计算刻度的最小刻度* @param {Object} params: 包含各种参数的对象* @param {Number} min:数据的最小值           */  function calTickMax(params, max, interval) {var ticks = params.ticks;var offset = params.offset;                         var step = params.step;         var intervalPrecision = params.intervalPrecision;var interval = offset === null ? step : (ticks[1] - ticks[0]);                  var i = 0, tickMax, differ, tick;       while (true) {  i++;    //防止陷入死循环if (i == 3) {break;}               tickMax = ticks[ticks.length - 1];differ = max - tickMax;if (differ < 0 && Math.abs(differ) >= interval * 0.2) {                 break;}/** 若数据最大值与刻度最大值之差为负且绝对值小于步长20%,* 或者数据最小值与刻度最小值之差为正* 则最大刻度需要再向上增加一个刻度    */                                  if (differ >= 0 || (differ < 0 && Math.abs(differ) < interval * 0.2)) {if (offset == null) {tick = roundNumber(tickMax + step, intervalPrecision);} else {tick = offset(tickMax, step).getTime();}ticks.push(tick);           } }}/*** 设置extent,并存入缓存* @param {Array} niceTickExtent* @param {Array} extent* @param {Array} ticks* @param {Array} splitNum*/function setAdjustExtent(niceTickExtent, extent, ticks, splitNum) {//修正轴的extent                niceTickExtent[0] = extent[0] = ticks[0];niceTickExtent[1] = extent[1] = ticks[ticks.length - 1];    var name = extent[0] + '@#' + extent[1] + '&' + splitNum;this.cache[name] = ticks;}           };      

(3)第三个问题,相对来说,简单很多,采用比较暴力的方法,规定标签统一倾斜45度展示,每一个标签在x轴上占用40px。这样可以计算出一根轴上最大容纳的标签数目。方法主要扩展在axisHelper对象上。

        /*** 计算出轴能容纳的最大标签个数* @param {Number} width:轴的总宽* @return {Number} num: 容纳的标签数目*/axisHelper.getMaxTickNum = function (width) {var perWidth = 40px;var num = Math.floor(width / perWidth);num = num < 1 ? 1 : num;return num;};axisHelper.niceScaleExtent = function (scale, model) {var extent = axisHelper.getScaleExtent(scale, model);var axis = model.axis;var fixMin = model.getMin() != null;var fixMax = model.getMax() != null;var splitNumber = model.get('splitNumber');var mulDimension = model.get('mulDimension');//  时间轴刻度自适应if (axis.model.get('tickMax') && axis.type != 'category' && axis.dim == 'x') {splitNumber = axisHelper.getMaxTickNum(axis._extent[1] - axis._extent[0], mulDimension);    scale.splitNumber = splitNumber;}if (scale.type === 'log') {scale.base = model.get('logBase');}       scale.setExtent(extent[0], extent[1]);scale.niceExtent({splitNumber: splitNumber,fixMin: fixMin,fixMax: fixMax,minInterval: scale.type === 'interval' ? model.get('minInterval') : null});// If some one specified the min, max. And the default calculated interval// is not good enough. He can specify the interval. It is often appeared// in angle axis with angle 0 - 360. Interval calculated in interval scale is hard// to be 60.// FIXMEvar interval = model.get('interval');if (interval != null) {scale.setInterval && scale.setInterval(interval);}};

(4)为了实现这些问题,还有一些初始化工作需要处理,主要有一下代码实现:

/*** 根据条件计算出最佳的时间刻度值* @param {Object} params: 包含参数的集合* @param {Array} params.extent: 刻度的范围* @param {Array} params.niceTickExtent: 更新后的刻度范围* @param {Number} params.splitNum: 刻度分割数目* @param {String} params.defaultFormat: 初始的时间格式* @param {Number} params.onlyNum: 是否只有单个数值          */helper.processTimeTicks = function (params) {var ticks = [], timeIntervalPro;var extent = params.extent;var splitNum = params.splitNum;var format = selectTimeFormat(extent, splitNum, timeInterval, params.defaultFormat);timeIntervalPro = Math.ceil(timeInterval[format].count(extent[0], extent[1]) / splitNum);timeIntervalPro = timeIntervalPro < 1 ? 1 : timeIntervalPro;ticks = timeInterval[format+'s'](extent[0], extent[1], timeIntervalPro);//调整刻度的最大刻度和最小刻度ticks = this.adjustTickExtent({ticks: ticks,extent: extent, niceTickExtent: params.niceTickExtent,splitNum: splitNum,offset: timeInterval[format].offset,step: timeIntervalPro,onlyNum: params.onlyNum});return ticks;/*** 判断使用哪种时间格式求时间刻度* @param {Array} extent: 刻度的范围* @param {Number} splitNum: 刻度分割数目* @param {Function} timeInterval: 处理时间刻度的函数集合* @param {String} defaultFormat: 初始的时间格式*/function selectTimeFormat(extent, splitNum, timeInterval, defaultFormat)  {var format = 'timeSecond';if (defaultFormat == 'timeYear') {return 'timeYear';}if (timeInterval.timeYear.count(extent[0], extent[1]) >= splitNum) {format = 'timeYear';} else if (timeInterval.timeMonth.count(extent[0], extent[1]) >= splitNum) {format = 'timeMonth';} else if (timeInterval.timeDay.count(extent[0], extent[1]) >= splitNum) {format = 'timeDay';} else if (timeInterval.timeHour.count(extent[0], extent[1]) >= splitNum) {format = 'timeHour';} else if (timeInterval.timeMinute.count(extent[0], extent[1]) >= splitNum) {format = 'timeMinute';} else {format = 'timeSecond';}format = correctResult(format, defaultFormat);return format;/*** 判断出的时间格式,可能不满足展示要求,* 需要重新进行修正* @param {String} format: 判断出的时间格式* @param {String} defaultFormat: 初始化的时间格式* @return {String} format: 修正后的时间格式*/function correctResult(format, defaultFormat) {if (defaultFormat == 'timeDay') {if (!/timeYear|timeMonth|timeDay/.test(format)) {format = 'timeDay';}} else if (defaultFormat == 'timeMonth') {if (!/timeYear|timeMonth/.test(format)) {format = 'timeMonth';}} else if (defaultFormat == 'timeSecond') {if (!/timeHour|timeMinute|timeSecond/.test(format)) {format = 'timeSecond';}}return format;}}};/*** 得到时间格式对应的时间类型* @param {String} timeFormat: 时间格式* @return {String} type: 时间类型:year、month、day、second;*/helper.getTimeType = function(timeFormat) {var type = null;if (['month', 'year', 'day', 'second'].indexOf(timeFormat) > -1) {type = timeFormat;} else if (timeFormat == 'hh:mm:ss' || timeFormat == 'yyyy-MM-dd hh:mm:ss' || timeFormat == 'yyyy/MM/dd hh:mm:ss') {type = 'second';} else if (timeFormat == 'yyyy') {type = 'year';} else if (timeFormat == 'yyyy/MM' || timeFormat == 'yyyyMM' || timeFormat == 'yyyy-MM') {type = 'month';} else if (/(yyyy\-MM\-dd)|(yyyy\/MM\/dd)|(dd\/MM\/yyyy)|(yyyyMMdd)/i.test(timeFormat)) {type = 'day';} else {type = 'second';}return type;};helper.intervalScaleGetTicks = function (extent, niceTickExtent, params) {var ticks = [], timeIntervalPro;var splitNum = params && params.splitNum || 5;var mulDimension = params && params.mulDimension;var timeFormat = params && params.timeFormat;var interval = params.interval;var intervalPrecision = params.intervalPrecision;var type = params.type;// If interval is 0, return [];if (!interval) {return ticks;}splitNum == 1 && (splitNum += 2) || splitNum == 2 && (splitNum += 1);var name = extent[0] + '@#' + extent[1] + '&' + splitNum;if (this.cache[name]) {return this.cache[name];}//沈才良添加 修复时间处于年和月的显示错误if (type == 'time' && params) { timeFormat = this.getTimeType(timeFormat);if (['year', 'month', 'day', 'second'].indexOf(timeFormat) > -1) {ticks = this.processTimeTicks({extent: extent, niceTickExtent: niceTickExtent, splitNum: splitNum, defaultFormat: 'time' + timeFormat.slice(0, 1).toUpperCase() + timeFormat.slice(1), onlyNum: params.onlyNum});return ticks;}}// Consider this case: using dataZoom toolbox, zoom and zoom.var safeLimit = 10000;if (extent[0] < niceTickExtent[0]) {ticks.push(extent[0]);}var tick = niceTickExtent[0];while (tick <= niceTickExtent[1]) {ticks.push(tick);// Avoid rounding errortick = roundNumber(tick + interval, intervalPrecision);if (tick === ticks[ticks.length - 1]) {// Consider out of safe float point, e.g.,// -3711126.9907707 + 2e-10 === -3711126.9907707break;}if (ticks.length > safeLimit) {return [];}}// Consider this case: the last item of ticks is smaller// than niceTickExtent[1] and niceTickExtent[1] === extent[1].if (extent[1] > (ticks.length ? ticks[ticks.length - 1] : niceTickExtent[1])) {ticks.push(extent[1]);}       }           return ticks;       };

(5)Echarts的年时间轴option配置项,主要是设置两个参数”timeFormat”、 “tickMax”。”timeFormat”表示时间轴的格式,可以为”yyyy”、“yyyy-MM”、“yyyy-MM-dd”“hh:mm:ss”等,tickMax则表示是否启用最大刻度个数,blooean类型:true表示启用。

        var option ={"color":["#4cc5f4","#fa5879","#f8e402",              ],"tooltip":{"trigger": "item"},"legend":{"show":false,"data":["2013-01","2013-07","2013-09","2013-11","2013-03","2013-05","2013-02","2013-04","2013-06","2013-08","2013-10","2013-12"]},"calculable":true,"xAxis":[{"type":"time",                                      "timeFormat": 'yyyy',               "rotate":45,"tickMax": true,min: 'dataMin',max: 'dataMax',"axisLabel":{"tooltip":{"show":true                         },formatter: function (params) {return new Date(params).Format('yyyy');}},"silent":false,"triggerEvent":true,"selectEvent":true}],"yAxis":[{"type":"value","key":"huan_bi","field":"huan_bi","rotate":0,"boundaryGap":null,"scale":false,"axisLabel":{"margin":10,"tooltip":{"show":true}},"silent":false,"triggerEvent":true,"selectEvent":true,"show":true,"splitLine":{"show":false},"axisTick":{"secondTick":{"show":false,"length":3},"length":6}}],"series":[{"name":"huan_bi","data":[["1998",0.11299999999999999],["1999",0.11299999999999999],["2000",0.12],["2001",0.135],["2003",0.149]],"selectEvent":true,"key":"huan_bi","type":"bar",barWidth:'20%',"symbol":"circle","showAllSymbol":true,"label":{"normal":{"show":false,"textStyle":{"color":"#000"},"formatter":"","position":"top"}}}],"backgroundColor":"#fff"};

详细源码:http://download.csdn.net/download/mulumeng981/9977121

写作水平有限,敬请谅解。
谢谢。

Echarts时间坐标轴刻度的改进和优化相关推荐

  1. html中的x轴y轴坐标图,ECharts xAxis配置 x坐标轴刻度

    xAxis.min   |   number, string, function [ default: null ] 坐标轴刻度最小值. 可以设置成特殊值 'dataMin',此时取数据在该轴上的最小 ...

  2. echarts中y轴设置刻度_ECharts中y坐标轴刻度的属性

    坐标轴刻度作为直角坐标系中重要的组成部分,我们需要学会合理的设置坐标轴的刻度,本节列举了一些 ECharts 中 y 轴刻度的一些属性设置.如果您还不知道如何显示坐标轴刻度,不晓得怎么控制 y 轴刻度 ...

  3. ECharts图表坐标轴数据超出显示范围,以及坐标轴刻度标签显示不全解决方法

    1.在使用ECharts图表时,比较常见到坐标轴数据超出显示范围的情况,如下 解决办法:添加配置 2.坐标轴刻度标签显示不全 就简单描述一下,比如你的数据横坐标有15个,但是默认不会显示全部 解决方法 ...

  4. Echarts时间轴补刻度

    有时候我们Echarts图表可能数据不够,只有一两条时,呈现效果不是很好 ,比如这样 所以我们需要填充数据,让图表就算没有数据看起来横轴也可以接受 // 补时间轴数值,数据不足10条时,补足10条,把 ...

  5. Echarts坐标轴刻度线宽度美化

    Echarts坐标轴刻度线宽度 平时Echarts的y轴刻度线是一条细直线,现在有一个需求是美工设计的刻度线是有宽度的,即粗刻度线还有粗线条设置了颜色,效果比较好,所以这个是必须要实现的: - ech ...

  6. 坐标轴刻度取值算法-基于魔数数组-源于echarts的y轴刻度计算需求

    数值型坐标轴刻度计算算法 前言 算法描述 上代码 代码运行效果 ts版本(2021/3/10补充) 结语 前言 因实习的公司是做大数据的,而我的工作刚好又是需要绘制一些数据图表的.绘制图表有许多现成的 ...

  7. #时间预测算法_改进的智慧交通系统出行时间预测算法

    引用 Chowdhury N K, Leung C K S. Improved travel time prediction algorithms for intelligent transporta ...

  8. 修改python plot折线图的坐标轴刻度

    修改python plot折线图的坐标轴刻度,这里修改为整数: 代码如下: from matplotlib import pyplot as plt import matplotlib.ticker ...

  9. 基于Flume的美团日志收集系统(二)改进和优化

    在<基于Flume的美团日志收集系统(一)架构和设计>中,我们详述了基于Flume的美团日志收集系统的架构设计,以及为什么做这样的设计.在本节中,我们将会讲述在实际部署和使用过程中遇到的问 ...

最新文章

  1. python3 实现 A+B Problem(百练OJ:1000)
  2. Java 动态修改的数组——ArrayList
  3. 程序员怎么样保证自己的程序没有BUG
  4. oracle mysql 创建表,Oracle 创建表用户、空间
  5. Android 里的数据储存
  6. Logistic regression Newton’s method
  7. tomcat java_opts 最大_tomcat性能优化(JAVA_OPTS)
  8. java多条件不定条件查询
  9. React 毁了 Web 开发?
  10. 回溯策略的汉诺塔问题
  11. labview如何安装modbus离线模块_Chrome73如何下载和安装扩展离线文件crx
  12. 云忧cms搭建在宝塔nginx服务器,登录报错
  13. 360浏览器打不开html5文件,电脑中360安全浏览器无法打开的解决方法
  14. 永中office属于职称计算机吗,职称计算机考核永中Office辅导之文字处理.docx
  15. 先进驾驶员辅助系统ADSA
  16. 自然语言处理之中文文本分析(jieba分词、词袋doc2bow、TFIDF文本挖掘)
  17. 大数据在智慧消防领域的应用
  18. 15.9 文本查询程序再探(继承)
  19. 知识点滴 - 无线电频谱划分
  20. 档案馆中温湿度要求的数据 资料分享

热门文章

  1. IT创业疯魔史(读书笔记)
  2. ADC中的LSB误差是什么意思
  3. 算法训练 强力党逗志芃
  4. mpu6050判断自由落体状态的方法
  5. NLP相关的语言学基础
  6. 关于mac升级Catalina后无法打开有道问题
  7. 哑铃锻炼身体各个部位的方法
  8. PVE虚拟化平台之创建虚拟机流程
  9. 牛客网-4 腾讯2016笔试题
  10. 有线网络和无线网络wifi优先级问题解决方法