前言
在Extjs 中。 单一的 Column Chart 的展示效果如上。
定义的过程例如以下:
1. 创建一个 Ext.chart.Chart
2. 创建两个坐标轴。 axes
一个 Category 类型的横坐标用来显示日期
一个Numeric 类型的纵坐标用来显示数据
3. 配置显示的图 series
配置 column 类型的柱状图。
详细代码例如以下:
<!--
Author : oscar999
Date :
ALL RIGHTS RESERVED
-->
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<script type="text/javascript" src="../lib/extjs/ext-all.js"></script>
<link rel="stylesheet" type="text/css" href="../lib/extjs/resources/ext-theme-neptune/ext-theme-neptune-all.css" />
<script>
Ext.onReady(function(){
window.generateData = function(n, floor){
var data = [],
p = (Math.random() * 11) + 1,
i;
floor = (!floor && floor !== 0)?
20 : floor; for (i = 0; i < (n || 12); i++) { data.push({ name: Ext.Date.monthNames[i % 12], data1: Math.floor(Math.max((Math.random() * 100), floor)), data2: Math.floor(Math.max((Math.random() * 100), floor)), data3: Math.floor(Math.max((Math.random() * 100), floor)), data4: Math.floor(Math.max((Math.random() * 100), floor)), data5: Math.floor(Math.max((Math.random() * 100), floor)), data6: Math.floor(Math.max((Math.random() * 100), floor)), data7: Math.floor(Math.max((Math.random() * 100), floor)), data8: Math.floor(Math.max((Math.random() * 100), floor)), data9: Math.floor(Math.max((Math.random() * 100), floor)) }); } return data; }; var store1 = Ext.create('Ext.data.JsonStore', { fields: ['name', 'data1', 'data2', 'data3', 'data4', 'data5', 'data6', 'data7', 'data9', 'data9'], data: generateData() }); var chart = Ext.create('Ext.chart.Chart', { style: 'background:#fff', animate: true, shadow: true, store: store1, //maxWidth: 500, //columnWidth : 0.1, axes: [{ type: 'Numeric', position: 'left', fields: ['data1'], label: { renderer: Ext.util.Format.numberRenderer('0,0') }, title: 'Number of Hits', grid: true, minimum: 0 }, { type: 'Category', position: 'bottom', fields: ['name'], //categoryNames:new String("111"), title: 'Month of the Year' }], series: [{ type: 'column', axis: 'left', highlight: true, tips: { trackMouse: true, width: 140, height: 28, renderer: function(storeItem, item) { this.setTitle(storeItem.get('name') + ': ' + storeItem.get('data1') + ' $'); } }, label: { display: 'insideEnd', 'text-anchor': 'middle', field: 'data1', //renderer: Ext.util.Format.numberRenderer('0'), orientation: 'vertical', color: '#FFF' }, style:{ opacity: 0.95 //,width:100 }, //xPadding:{left:100,right:100}, xField: 'name', yField: 'data1' }] }); var win = Ext.create('Ext.window.Window', { width: 800, height: 600, minHeight: 400, minWidth: 550, hidden: false, maximizable: true, title: 'Column Chart', autoShow: true, layout: 'fit', tbar: [{ text: 'Save Chart', handler: function() { Ext.MessageBox.confirm('Confirm Download', 'Would you like to download the chart as an image?', function(choice){ if(choice == 'yes'){ chart.save({ type: 'image/png' }); } }); } }, { text: 'Reload Data', handler: function() { // Add a short delay to prevent fast sequential clicks window.loadTask.delay(100, function() { store1.loadData(generateData()); }); } }], items: chart }); }); </script> </head> <body> </body> </html>
怎样设置坐标轴的长度
针对上面的样例, 坐标轴的长度是Extjs依据数据大小自己主动运算并设置的。
假设须要手动定义这个长度的话,改怎样设置呢?
对于Numeric这样的坐标轴来说, 有maximum 和 minimun 这样的參数能够配置。
并且配置也非常easy了, 就不多介绍了。
这里仅仅是对红色的部分做一个伏笔(这样的设置对于配置 堆叠的图形不使用)
多个图形。 两个纵坐标轴
有的状况下, 可能会使用多个图形,
由于图形的范围不同,可能须要使用两个纵坐标轴。
相似的情景能够是这样:
有三个图, 两个柱状图。 一个折线图
折线图和柱状图的数据范围或是单位可能不同。
这里看上去好像是一个折线图和一个柱状图。
事实上是有两个柱状图, 仅仅只是一个被还有一个盖住了。
出现这样的状况的原因是在定义的时候, 往series 中加入了两个column 的chart.
这样的状况的源代码是:
<!--
Author : oscar999
Date :
ALL RIGHTS RESERVED
-->
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<script type="text/javascript" src="../lib/extjs/ext-all.js"></script>
<link rel="stylesheet" type="text/css" href="../lib/extjs/resources/ext-theme-neptune/ext-theme-neptune-all.css" />
<script>
Ext.onReady(function(){
window.generateData = function(n, floor){
var data = [],
p = (Math.random() * 11) + 1,
i;
floor = (!floor && floor !== 0)? 20 : floor;
for (i = 0; i < (n || 12); i++) {
data.push({
name: Ext.Date.monthNames[i % 12],
data1: (i+1)*8,
data2: (i+1)*10,
data3: (i+1)*8
});
}
return data;
};
var store1 = Ext.create('Ext.data.JsonStore', {
fields: ['name', 'data1', 'data2', 'data3', 'data4', 'data5', 'data6', 'data7', 'data9', 'data9'],
data: generateData()
});
var chart = Ext.create('Ext.chart.Chart', {
style: 'background:#fff',
animate: true,
shadow: true,
store: store1,
legend:'right',
axes: [{
type: 'Numeric',
position: 'left',
fields: ['data1','data2'],
label: {
renderer: Ext.util.Format.numberRenderer('0,0')
},
title: 'Number of Hits',
grid: true
},{
type: 'Numeric',
position: 'right',
fields: ['data3'],
label: {
renderer: Ext.util.Format.numberRenderer('0,0')
},
//title: 'Number of Hits',
grid: true
}, {
type: 'Category',
position: 'bottom',
fields: ['name'],
title: 'Month of the Year'
}],
series: [{
type: 'column',
axis: 'left',
highlight: true,
tips: {
trackMouse: true,
width: 140,
height: 28,
renderer: function(storeItem, item) {
this.setTitle(storeItem.get('name') + ': ' + storeItem.get('data1') + ' $');
}
},
label: {
display: 'insideEnd',
'text-anchor': 'middle',
field: 'data1',
//renderer: Ext.util.Format.numberRenderer('0'),
orientation: 'vertical',
color: '#FFF'
},
style:{
opacity: 0.95
//,width:100
},
//xPadding:{left:100,right:100},
xField: 'name',
yField: ['data1']
}
,{
type: 'column',
axis: 'left',
highlight: true,
tips: {
trackMouse: true,
width: 140,
height: 28,
renderer: function(storeItem, item) {
this.setTitle(storeItem.get('name') + ': ' + storeItem.get('data2') + ' $');
}
},
label: {
display: 'insideEnd',
'text-anchor': 'middle',
field: ['data1','data2'],
//renderer: Ext.util.Format.numberRenderer('0'),
orientation: 'vertical',
color: '#FFF'
},
style:{
opacity: 0.95
//,width:100
},
//xPadding:{left:100,right:100},
xField: 'name',
yField: ['data2']
}
,
{
type: 'line',
axis: 'right',
highlight: true,
tips: {
trackMouse: true,
width: 140,
height: 28,
renderer: function(storeItem, item) {
this.setTitle(storeItem.get('name') + ': ' + storeItem.get('data3') + ' $');
}
},
label: {
display: 'insideEnd',
'text-anchor': 'middle',
field: 'data3',
//renderer: Ext.util.Format.numberRenderer('0'),
orientation: 'vertical',
color: '#FFF'
},
style:{
opacity: 0.95
//,width:100
},
//xPadding:{left:100,right:100},
xField: 'name',
yField: 'data3'
}]
});
var win = Ext.create('Ext.window.Window', {
width: 800,
height: 600,
minHeight: 400,
minWidth: 550,
hidden: false,
maximizable: true,
title: 'Column Chart',
autoShow: true,
layout: 'fit',
tbar: [{
text: 'Save Chart',
handler: function() {
Ext.MessageBox.confirm('Confirm Download', 'Would you like to download the chart as an image?', function(choice){
if(choice == 'yes'){
chart.save({
type: 'image/png'
});
}
});
}
}, {
text: 'Reload Data',
handler: function() {
// Add a short delay to prevent fast sequential clicks
window.loadTask.delay(100, function() {
store1.loadData(generateData());
});
}
}],
items: chart
});
});
</script>
</head>
<body>
</body>
</html>
基本上这不是我们想要的效果。
我们要的效果应该是这样:
这是完美的呈现方式。
完美方式实现的思想是:
1 创建两个坐标轴。 axes
一个 Category 类型的横坐标用来显示日期
一个Numeric 类型的纵坐标用来显示柱状图数据一个Numeric 类型的纵坐标用来显示折线图数据
2. series 加入一个 column Chart. stacked.
以上定义完毕之后。 会发生基本上都正常, 可是有一点就是, 左右的两个纵坐标的尺度可能不同。
通过配置maximum 和maximum 来设置坐标并不会生效。
这里就要提到上面框出的maximum配置的红色部分了, 由于这个配置对于堆叠的图已经不适用了。
堆叠图性的左右坐标一致
为什么会出现左右坐标不一致的状况, 看一看Extjs 的 Ext.chart.axis.Numeric 的定义
原来是这个地方有限制。这应该是Extjs 有意为之了, 至于原因是什么,尚不可知。
无论这样。先去除这个限制得到想要的效果。
解决方式就是定义一个和 Ext.chart.axis.Numeric 相似的坐标定义
/**
* Add by Oscar999
*/
Ext.define('Ext.chart.axis.StackedNumeric', {
/* Begin Definitions */
extend: 'Ext.chart.axis.Axis',
alternateClassName: 'Ext.chart.StackedNumericAxis',
/* End Definitions */
type: 'StackedNumeric',
// @private
isNumericAxis: true,
alias: 'axis.stackednumeric',
uses: ['Ext.data.Store'],
constructor: function(config) {
var me = this,
hasLabel = !!(config.label && config.label.renderer),
label;
me.callParent([config]);
label = me.label;
if (config.constrain == null) {
me.constrain = (config.minimum != null && config.maximum != null);
}
if (!hasLabel) {
label.renderer = function(v) {
return me.roundToDecimal(v, me.decimals);
};
}
},
roundToDecimal: function(v, dec) {
var val = Math.pow(10, dec || 0);
return Math.round(v * val) / val;
},
/**
* @cfg {Number} minimum
* The minimum value drawn by the axis. If not set explicitly, the axis
* minimum will be calculated automatically. It is ignored for stacked charts.
*/
minimum: NaN,
/**
* @cfg {Number} maximum
* The maximum value drawn by the axis. If not set explicitly, the axis
* maximum will be calculated automatically. It is ignored for stacked charts.
*/
maximum: NaN,
/**
* @cfg {Boolean} constrain
* If true, the values of the chart will be rendered only if they belong between minimum and maximum.
* If false, all values of the chart will be rendered, regardless of whether they belong between minimum and maximum or not.
* Default's true if maximum and minimum is specified. It is ignored for stacked charts.
*/
constrain: true,
/**
* @cfg {Number} decimals
* The number of decimals to round the value to.
*/
decimals: 2,
/**
* @cfg {String} scale
* The scaling algorithm to use on this axis. May be "linear" or
* "logarithmic". Currently only linear scale is implemented.
* @private
*/
scale: "linear",
// @private constrains to datapoints between minimum and maximum only
doConstrain: function() {
var me = this,
chart = me.chart,
store = chart.getChartStore(),
items = store.data.items,
d, dLen, record,
series = chart.series.items,
fields = me.fields,
ln = fields.length,
range = me.calcEnds(),
min = range.from, max = range.to, i, l,
useAcum = false,
value, data = [],
addRecord;
for (d = 0, dLen = items.length; d < dLen; d++) {
addRecord = true;
record = items[d];
for (i = 0; i < ln; i++) {
value = record.get(fields[i]);
if (me.type == 'Time' && typeof value == "string") {
value = Date.parse(value);
}
if (+value < +min) {
addRecord = false;
break;
}
if (+value > +max) {
addRecord = false;
break;
}
}
if (addRecord) {
data.push(record);
}
}
chart.setSubStore(new Ext.data.Store({
model: store.model,
data: data
}));
},
/**
* @cfg {String} position
* Indicates the position of the axis relative to the chart
*/
position: 'left',
/**
* @cfg {Boolean} adjustMaximumByMajorUnit
* Indicates whether to extend maximum beyond data's maximum to the nearest
* majorUnit.
*/
adjustMaximumByMajorUnit: false,
/**
* @cfg {Boolean} adjustMinimumByMajorUnit
* Indicates whether to extend the minimum beyond data's minimum to the
* nearest majorUnit.
*/
adjustMinimumByMajorUnit: false,
// applying constraint
processView: function() {
var me = this;
if (me.constrain) {
me.doConstrain();
}
},
// @private apply data.
applyData: function() {
this.callParent();
return this.calcEnds();
}
});
这一段建议是单独放在一个js 文件里。 通过导入的方式使用。
以下给出一个放在同一份文件里完整的源代码:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<script type="text/javascript" src="../lib/extjs/ext-all.js"></script>
<script>
/*Ext.define('mtk.chart.axis.Numeric',
{
extend : 'Ext.chart.axis.Numeric',
type: 'stackedNumeric',
initComponent: function(config) {
this.processView = function() {
var me = this,
chart = me.chart,
series = chart.series.items,
i, l;
for (i = 0, l = series.length; i < l; i++) {
if (series[i].stacked) {
// Do not constrain stacked charts (bar, column, or area).
delete me.minimum;
delete me.maximum;
me.constrain = false;
break;
}
}
if (me.constrain) {
me.doConstrain();
}
};
this.callParent([config]);
}
});*/
Ext.define('Ext.chart.axis.StackedNumeric', {
/* Begin Definitions */
extend: 'Ext.chart.axis.Axis',
alternateClassName: 'Ext.chart.StackedNumericAxis',
/* End Definitions */
type: 'StackedNumeric',
// @private
isNumericAxis: true,
alias: 'axis.stackednumeric',
uses: ['Ext.data.Store'],
constructor: function(config) {
var me = this,
hasLabel = !!(config.label && config.label.renderer),
label;
me.callParent([config]);
label = me.label;
if (config.constrain == null) {
me.constrain = (config.minimum != null && config.maximum != null);
}
if (!hasLabel) {
label.renderer = function(v) {
return me.roundToDecimal(v, me.decimals);
};
}
},
roundToDecimal: function(v, dec) {
var val = Math.pow(10, dec || 0);
return Math.round(v * val) / val;
},
/**
* @cfg {Number} minimum
* The minimum value drawn by the axis. If not set explicitly, the axis
* minimum will be calculated automatically. It is ignored for stacked charts.
*/
minimum: NaN,
/**
* @cfg {Number} maximum
* The maximum value drawn by the axis. If not set explicitly, the axis
* maximum will be calculated automatically. It is ignored for stacked charts.
*/
maximum: NaN,
/**
* @cfg {Boolean} constrain
* If true, the values of the chart will be rendered only if they belong between minimum and maximum.
* If false, all values of the chart will be rendered, regardless of whether they belong between minimum and maximum or not.
* Default's true if maximum and minimum is specified. It is ignored for stacked charts.
*/
constrain: true,
/**
* @cfg {Number} decimals
* The number of decimals to round the value to.
*/
decimals: 2,
/**
* @cfg {String} scale
* The scaling algorithm to use on this axis. May be "linear" or
* "logarithmic". Currently only linear scale is implemented.
* @private
*/
scale: "linear",
// @private constrains to datapoints between minimum and maximum only
doConstrain: function() {
var me = this,
chart = me.chart,
store = chart.getChartStore(),
items = store.data.items,
d, dLen, record,
series = chart.series.items,
fields = me.fields,
ln = fields.length,
range = me.calcEnds(),
min = range.from, max = range.to, i, l,
useAcum = false,
value, data = [],
addRecord;
for (d = 0, dLen = items.length; d < dLen; d++) {
addRecord = true;
record = items[d];
for (i = 0; i < ln; i++) {
value = record.get(fields[i]);
if (me.type == 'Time' && typeof value == "string") {
value = Date.parse(value);
}
if (+value < +min) {
addRecord = false;
break;
}
if (+value > +max) {
addRecord = false;
break;
}
}
if (addRecord) {
data.push(record);
}
}
chart.setSubStore(new Ext.data.Store({
model: store.model,
data: data
}));
},
/**
* @cfg {String} position
* Indicates the position of the axis relative to the chart
*/
position: 'left',
/**
* @cfg {Boolean} adjustMaximumByMajorUnit
* Indicates whether to extend maximum beyond data's maximum to the nearest
* majorUnit.
*/
adjustMaximumByMajorUnit: false,
/**
* @cfg {Boolean} adjustMinimumByMajorUnit
* Indicates whether to extend the minimum beyond data's minimum to the
* nearest majorUnit.
*/
adjustMinimumByMajorUnit: false,
// applying constraint
processView: function() {
/*var me = this,
chart = me.chart,
series = chart.series.items,
i, l;
for (i = 0, l = series.length; i < l; i++) {
if (series[i].stacked) {
// Do not constrain stacked charts (bar, column, or area).
delete me.minimum;
delete me.maximum;
me.constrain = false;
break;
}
}
if (me.constrain) {
me.doConstrain();
}*/
var me = this;
if (me.constrain) {
me.doConstrain();
}
},
// @private apply data.
applyData: function() {
this.callParent();
return this.calcEnds();
}
});
</script>
<link rel="stylesheet" type="text/css" href="../lib/extjs/resources/ext-theme-neptune/ext-theme-neptune-all.css" />
<script>
Ext.onReady(function(){
window.generateData = function(n, floor){
var data = [],
p = (Math.random() * 11) + 1,
i;
floor = (!floor && floor !== 0)? 20 : floor;
for (i = 0; i < (n || 12); i++) {
data.push({
name: Ext.Date.monthNames[i % 12],
/*data1: Math.floor(Math.max((Math.random() * 100), floor)),
data2: Math.floor(Math.max((Math.random() * 100), floor)),
data3: Math.floor(Math.max((Math.random() * 100), floor)),
data4: Math.floor(Math.max((Math.random() * 100), floor)),
data5: Math.floor(Math.max((Math.random() * 100), floor)),
data6: Math.floor(Math.max((Math.random() * 100), floor)),
data7: Math.floor(Math.max((Math.random() * 100), floor)),
data8: Math.floor(Math.max((Math.random() * 100), floor)),
data9: Math.floor(Math.max((Math.random() * 100), floor))*/
data1: (i+1)*8,
data2: (i+1)*8,
data3: (i+1)*8,
data4: (i+1)*8,
data5: (i+1)*8,
data6: (i+1)*8,
data7: (i+1)*8,
data8: (i+1)*8,
data9: (i+1)*8
});
}
return data;
};
var store1 = Ext.create('Ext.data.JsonStore', {
fields: ['name', 'data1', 'data2', 'data3', 'data4', 'data5', 'data6', 'data7', 'data9', 'data9'],
data: generateData()
});
var chart = Ext.create('Ext.chart.Chart', {
style: 'background:#fff',
animate: true,
shadow: true,
store: store1,
//maxWidth: 500,
//columnWidth : 0.1,
legend:'right',
axes: [{
type: 'StackedNumeric',
position: 'left',
fields: ['data1','data2'],
label: {
renderer: Ext.util.Format.numberRenderer('0,0')
},
title: 'Number of Hits',
grid: true,
minimum: 0,
maximum:200
},{
type: 'StackedNumeric',
position: 'right',
fields: ['data3'],
label: {
renderer: Ext.util.Format.numberRenderer('0,0')
},
//title: 'Number of Hits',
grid: true,
minimum: 0,
maximum:200
}, {
type: 'Category',
position: 'bottom',
fields: ['name'],
//categoryNames:new String("111"),
title: 'Month of the Year'
}],
series: [{
type: 'column',
axis: 'left',
//stacked:false,
stacked:true,
highlight: true,
tips: {
trackMouse: true,
width: 140,
height: 28,
renderer: function(storeItem, item) {
this.setTitle(storeItem.get('name') + ': ' + storeItem.get('data1') + ' $');
}
},
label: {
display: 'insideEnd',
'text-anchor': 'middle',
field: ['data1','data2'],
//renderer: Ext.util.Format.numberRenderer('0'),
orientation: 'vertical',
color: '#FFF'
},
style:{
opacity: 0.95
//,width:100
},
//xPadding:{left:100,right:100},
xField: 'name',
yField: ['data1','data2']
}
,
{
type: 'line',
axis: 'right',
highlight: true,
tips: {
trackMouse: true,
width: 140,
height: 28,
renderer: function(storeItem, item) {
this.setTitle(storeItem.get('name') + ': ' + storeItem.get('data3') + ' $');
}
},
label: {
display: 'insideEnd',
'text-anchor': 'middle',
field: 'data3',
//renderer: Ext.util.Format.numberRenderer('0'),
orientation: 'vertical',
color: '#FFF'
},
style:{
opacity: 0.95
//,width:100
},
//xPadding:{left:100,right:100},
xField: 'name',
yField: 'data3'
}]
});
var win = Ext.create('Ext.window.Window', {
width: 800,
height: 600,
minHeight: 400,
minWidth: 550,
hidden: false,
maximizable: true,
title: 'Column Chart',
autoShow: true,
layout: 'fit',
tbar: [{
text: 'Save Chart',
handler: function() {
Ext.MessageBox.confirm('Confirm Download', 'Would you like to download the chart as an image?', function(choice){
if(choice == 'yes'){
chart.save({
type: 'image/png'
});
}
});
}
}, {
text: 'Reload Data',
handler: function() {
// Add a short delay to prevent fast sequential clicks
window.loadTask.delay(100, function() {
store1.loadData(generateData());
});
}
}],
items: chart
});
});
</script>
</head>
<body>
</body>
</html>
版权声明:本文博主原创文章。博客,未经同意不得转载。