Echarts时间坐标轴刻度的改进和优化
来源:互联网 发布:各个国家顶级域名 编辑:程序博客网 时间:2024/05/17 04:44
问题介绍:
Echarts插件的坐标轴类型主要分为时间(time)、数值(value)、对数(log)、类目(category)四大类,当采用二维的直角坐标系时,一般x轴通过采用类目轴,而y轴通常采用value轴。官方的有关的直角坐标系例子,大多遵循这个原则配置的。这样配置其实只适用于离散型数据,而对于时间型数据,虽然有时间轴的支持,但是效果却差强人意,甚至存在展示的缺陷。
如图1所示为日的时间型数据,从图上可以看出柱状图的柱子跟y轴有重叠,x轴刻度值少的问题。如图2、3所示为月和年的时间型数据,问题跟图1差不多,但还有一个不一样的问题,就是刻度值,不管是月还是年的数据,刻度值都显示的日的刻度值,这个对于用户来说,体验应该是不好的。更好的体验应该是,月的时间型数据,显示的刻度值应该都是月的刻度值;年的时间型数据,显示的应该都是年的刻度值。如图4、5、6,是本人优化和处理后的显示效果图。
总的来说,主要存在以下三个问题
(1)不同时间格式下,计算出来的刻度不符合预期;
(2)数据的最小值容易与坐标轴重叠,最大值容易显示在图表之外;
(3)计算出来的刻度个数偏少,数据之间容易重叠。
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 Date do 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. // FIXME var 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 error tick = roundNumber(tick + interval, intervalPrecision); if (tick === ticks[ticks.length - 1]) { // Consider out of safe float point, e.g., // -3711126.9907707 + 2e-10 === -3711126.9907707 break; } 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时间坐标轴刻度的改进和优化
- R作图控制坐标轴刻度的个数和标注
- R作图控制坐标轴刻度的个数和标注
- 调整坐标轴的刻度(locator_params)
- ECharts关于“动态数据+时间坐标轴”的问题
- Highcharts坐标轴只显示起始刻度和终止刻度
- MATLAB 调整坐标轴刻度的显示
- 图表轴刻度的思路,算法: 转载3【求算法:图表控件,如何确定坐标轴的范围和刻度?】
- Matlab绘图笔记:修改坐标轴显示的刻度密度,lable文字,和位置
- 实现echarts插件刻度轴的二级刻度
- ECharts | 改变 yAxis y轴的刻度
- Matlab 绘图 坐标轴 刻度
- HighCharts自定义坐标轴刻度
- matplotlib去掉坐标轴刻度
- 刻度ticks和时间戳
- MATLAB画图:改变坐标轴刻度的显示数值
- 曲线坐标轴范围及刻度的自适应算法综述
- 几何画板如何隐藏坐标轴上的刻度
- Android实现的写字板(绘画板)
- Query Mobile学习笔记(七)——主题与打包
- 智能性爱机器人的“黑暗面”:伴侣也会变杀手
- Java的多线程
- angular下拉框模糊查询
- Echarts时间坐标轴刻度的改进和优化
- 正确使用日志的技巧
- Selenium中被误用的XPath
- 在CENTOS7上玩转Ethereum区块链(1):虚机架设篇
- DB2函数大全
- 中科院分词系统(NLPIR)JAVA简易教程(转载)
- Android多线程机制
- win7_不主动发送智能卡命令
- js计算出现多位小数-Javascript 浮点运算问题分析与解决