<!DOCTYPE html>
|
<html>
|
<head>
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
<title>The source code</title>
|
<link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
|
<script type="text/javascript" src="../resources/prettify/prettify.js"></script>
|
<style type="text/css">
|
.highlight { display: block; background-color: #ddd; }
|
</style>
|
<script type="text/javascript">
|
function highlight() {
|
document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
|
}
|
</script>
|
</head>
|
<body onload="prettyPrint(); highlight();">
|
<pre class="prettyprint lang-js"><span id='Ext-chart-series-Gauge'>/**
|
</span> * @class Ext.chart.series.Gauge
|
*
|
* Creates a Gauge Chart. Gauge Charts are used to show progress in a certain variable. There are two ways of using the Gauge chart.
|
* One is setting a store element into the Gauge and selecting the field to be used from that store. Another one is instantiating the
|
* visualization and using the `setValue` method to adjust the value you want.
|
*
|
* An example of Gauge visualization:
|
*
|
* @example
|
* var store = Ext.create('Ext.data.JsonStore', {
|
* fields: ['value'],
|
* data: [
|
* { 'value':80 }
|
* ]
|
* });
|
*
|
* Ext.create('Ext.chart.Chart', {
|
* renderTo: Ext.getBody(),
|
* store: store,
|
* width: 400,
|
* height: 250,
|
* animate: true,
|
* insetPadding: 30,
|
* axes: [{
|
* type: 'gauge',
|
* position: 'gauge',
|
* minimum: 0,
|
* maximum: 100,
|
* steps: 10,
|
* margin: 10
|
* }],
|
* series: [{
|
* type: 'gauge',
|
* field: 'value',
|
* donut: 30,
|
* colorSet: ['#F49D10', '#ddd']
|
* }]
|
* });
|
*
|
* Ext.widget("button", {
|
* renderTo: Ext.getBody(),
|
* text: "Refresh",
|
* handler: function() {
|
* store.getAt(0).set('value', Math.round(Math.random()*100));
|
* }
|
* });
|
*
|
* In this example we create a special Gauge axis to be used with the gauge visualization (describing half-circle markers), and also we're
|
* setting a maximum, minimum and steps configuration options into the axis. The Gauge series configuration contains the store field to be bound to
|
* the visual display and the color set to be used with the visualization.
|
*/
|
Ext.define('Ext.chart.series.Gauge', {
|
|
/* Begin Definitions */
|
|
extend: 'Ext.chart.series.Series',
|
|
<span id='Ext-chart-series-Gauge-cfg-type'> /* End Definitions */
|
</span>
|
type: "gauge",
|
alias: 'series.gauge',
|
|
<span id='Ext-chart-series-Gauge-property-rad'> rad: Math.PI / 180,
|
</span>
|
<span id='Ext-chart-series-Gauge-cfg-highlightDuration'> /**
|
</span> * @cfg {Number} highlightDuration
|
* The duration for the pie slice highlight effect.
|
*/
|
highlightDuration: 150,
|
|
<span id='Ext-chart-series-Gauge-cfg-angleField'> /**
|
</span> * @cfg {String} angleField (required)
|
* The store record field name to be used for the pie angles.
|
* The values bound to this field name must be positive real numbers.
|
*/
|
angleField: false,
|
|
<span id='Ext-chart-series-Gauge-cfg-needle'> /**
|
</span> * @cfg {Boolean} needle
|
* Use the Gauge Series as an area series or add a needle to it. Default's false.
|
*/
|
needle: false,
|
|
<span id='Ext-chart-series-Gauge-cfg-donut'> /**
|
</span> * @cfg {Boolean/Number} donut
|
* Use the entire disk or just a fraction of it for the gauge. Default's false.
|
*/
|
donut: false,
|
|
<span id='Ext-chart-series-Gauge-cfg-showInLegend'> /**
|
</span> * @cfg {Boolean} showInLegend
|
* Whether to add the pie chart elements as legend items. Default's false.
|
*/
|
showInLegend: false,
|
|
<span id='Ext-chart-series-Gauge-cfg-style'> /**
|
</span> * @cfg {Object} style
|
* An object containing styles for overriding series styles from Theming.
|
*/
|
style: {},
|
|
<span id='Ext-chart-series-Gauge-method-constructor'> constructor: function(config) {
|
</span> this.callParent(arguments);
|
var me = this,
|
chart = me.chart,
|
surface = chart.surface,
|
store = chart.store,
|
shadow = chart.shadow, i, l, cfg;
|
Ext.apply(me, config, {
|
shadowAttributes: [{
|
"stroke-width": 6,
|
"stroke-opacity": 1,
|
stroke: 'rgb(200, 200, 200)',
|
translate: {
|
x: 1.2,
|
y: 2
|
}
|
},
|
{
|
"stroke-width": 4,
|
"stroke-opacity": 1,
|
stroke: 'rgb(150, 150, 150)',
|
translate: {
|
x: 0.9,
|
y: 1.5
|
}
|
},
|
{
|
"stroke-width": 2,
|
"stroke-opacity": 1,
|
stroke: 'rgb(100, 100, 100)',
|
translate: {
|
x: 0.6,
|
y: 1
|
}
|
}]
|
});
|
me.group = surface.getGroup(me.seriesId);
|
if (shadow) {
|
for (i = 0, l = me.shadowAttributes.length; i < l; i++) {
|
me.shadowGroups.push(surface.getGroup(me.seriesId + '-shadows' + i));
|
}
|
}
|
surface.customAttributes.segment = function(opt) {
|
return me.getSegment(opt);
|
};
|
},
|
|
<span id='Ext-chart-series-Gauge-method-initialize'> // @private updates some onbefore render parameters.
|
</span> initialize: function() {
|
var me = this,
|
store = me.chart.getChartStore(),
|
data = store.data.items,
|
label = me.label,
|
ln = data.length;
|
|
me.yField = [];
|
if (label && label.field && ln > 0) {
|
me.yField.push(data[0].get(label.field));
|
}
|
},
|
|
<span id='Ext-chart-series-Gauge-method-getSegment'> // @private returns an object with properties for a Slice
|
</span> getSegment: function(opt) {
|
var me = this,
|
rad = me.rad,
|
cos = Math.cos,
|
sin = Math.sin,
|
abs = Math.abs,
|
x = me.centerX,
|
y = me.centerY,
|
x1 = 0, x2 = 0, x3 = 0, x4 = 0,
|
y1 = 0, y2 = 0, y3 = 0, y4 = 0,
|
delta = 1e-2,
|
r = opt.endRho - opt.startRho,
|
startAngle = opt.startAngle,
|
endAngle = opt.endAngle,
|
midAngle = (startAngle + endAngle) / 2 * rad,
|
margin = opt.margin || 0,
|
flag = abs(endAngle - startAngle) > 180,
|
a1 = Math.min(startAngle, endAngle) * rad,
|
a2 = Math.max(startAngle, endAngle) * rad,
|
singleSlice = false;
|
|
x += margin * cos(midAngle);
|
y += margin * sin(midAngle);
|
|
x1 = x + opt.startRho * cos(a1);
|
y1 = y + opt.startRho * sin(a1);
|
|
x2 = x + opt.endRho * cos(a1);
|
y2 = y + opt.endRho * sin(a1);
|
|
x3 = x + opt.startRho * cos(a2);
|
y3 = y + opt.startRho * sin(a2);
|
|
x4 = x + opt.endRho * cos(a2);
|
y4 = y + opt.endRho * sin(a2);
|
|
if (abs(x1 - x3) <= delta && abs(y1 - y3) <= delta) {
|
singleSlice = true;
|
}
|
//Solves mysterious clipping bug with IE
|
if (singleSlice) {
|
return {
|
path: [
|
["M", x1, y1],
|
["L", x2, y2],
|
["A", opt.endRho, opt.endRho, 0, +flag, 1, x4, y4],
|
["Z"]]
|
};
|
} else {
|
return {
|
path: [
|
["M", x1, y1],
|
["L", x2, y2],
|
["A", opt.endRho, opt.endRho, 0, +flag, 1, x4, y4],
|
["L", x3, y3],
|
["A", opt.startRho, opt.startRho, 0, +flag, 0, x1, y1],
|
["Z"]]
|
};
|
}
|
},
|
|
<span id='Ext-chart-series-Gauge-method-calcMiddle'> // @private utility function to calculate the middle point of a pie slice.
|
</span> calcMiddle: function(item) {
|
var me = this,
|
rad = me.rad,
|
slice = item.slice,
|
x = me.centerX,
|
y = me.centerY,
|
startAngle = slice.startAngle,
|
endAngle = slice.endAngle,
|
radius = Math.max(('rho' in slice) ? slice.rho: me.radius, me.label.minMargin),
|
donut = +me.donut,
|
a1 = Math.min(startAngle, endAngle) * rad,
|
a2 = Math.max(startAngle, endAngle) * rad,
|
midAngle = -(a1 + (a2 - a1) / 2),
|
xm = x + (item.endRho + item.startRho) / 2 * Math.cos(midAngle),
|
ym = y - (item.endRho + item.startRho) / 2 * Math.sin(midAngle);
|
|
item.middle = {
|
x: xm,
|
y: ym
|
};
|
},
|
|
<span id='Ext-chart-series-Gauge-method-drawSeries'> /**
|
</span> * Draws the series for the current chart.
|
*/
|
drawSeries: function() {
|
var me = this,
|
chart = me.chart,
|
store = chart.getChartStore(),
|
group = me.group,
|
animate = me.chart.animate,
|
axis = me.chart.axes.get(0),
|
minimum = axis && axis.minimum || me.minimum || 0,
|
maximum = axis && axis.maximum || me.maximum || 0,
|
field = me.angleField || me.field || me.xField,
|
surface = chart.surface,
|
chartBBox = chart.chartBBox,
|
rad = me.rad,
|
donut = +me.donut,
|
values = {},
|
items = [],
|
seriesStyle = me.seriesStyle,
|
seriesLabelStyle = me.seriesLabelStyle,
|
colorArrayStyle = me.colorArrayStyle,
|
colorArrayLength = colorArrayStyle && colorArrayStyle.length || 0,
|
cos = Math.cos,
|
sin = Math.sin,
|
rendererAttributes, centerX, centerY, slice, slices, sprite, value,
|
item, ln, record, i, j, startAngle, endAngle, middleAngle, sliceLength, path,
|
p, spriteOptions, bbox, splitAngle, sliceA, sliceB;
|
|
Ext.apply(seriesStyle, me.style || {});
|
|
me.setBBox();
|
bbox = me.bbox;
|
|
//override theme colors
|
if (me.colorSet) {
|
colorArrayStyle = me.colorSet;
|
colorArrayLength = colorArrayStyle.length;
|
}
|
|
//if not store or store is empty then there's nothing to draw
|
if (!store || !store.getCount() || me.seriesIsHidden) {
|
me.hide();
|
me.items = [];
|
return;
|
}
|
|
centerX = me.centerX = chartBBox.x + (chartBBox.width / 2);
|
centerY = me.centerY = chartBBox.y + chartBBox.height;
|
me.radius = Math.min(centerX - chartBBox.x, centerY - chartBBox.y);
|
me.slices = slices = [];
|
me.items = items = [];
|
|
if (!me.value) {
|
record = store.getAt(0);
|
me.value = record.get(field);
|
}
|
|
value = me.value;
|
if (me.needle) {
|
sliceA = {
|
series: me,
|
value: value,
|
startAngle: -180,
|
endAngle: 0,
|
rho: me.radius
|
};
|
splitAngle = -180 * (1 - (value - minimum) / (maximum - minimum));
|
slices.push(sliceA);
|
} else {
|
splitAngle = -180 * (1 - (value - minimum) / (maximum - minimum));
|
sliceA = {
|
series: me,
|
value: value,
|
startAngle: -180,
|
endAngle: splitAngle,
|
rho: me.radius
|
};
|
sliceB = {
|
series: me,
|
value: me.maximum - value,
|
startAngle: splitAngle,
|
endAngle: 0,
|
rho: me.radius
|
};
|
slices.push(sliceA, sliceB);
|
}
|
|
//do pie slices after.
|
for (i = 0, ln = slices.length; i < ln; i++) {
|
slice = slices[i];
|
sprite = group.getAt(i);
|
//set pie slice properties
|
rendererAttributes = Ext.apply({
|
segment: {
|
startAngle: slice.startAngle,
|
endAngle: slice.endAngle,
|
margin: 0,
|
rho: slice.rho,
|
startRho: slice.rho * +donut / 100,
|
endRho: slice.rho
|
}
|
}, Ext.apply(seriesStyle, colorArrayStyle && { fill: colorArrayStyle[i % colorArrayLength] } || {}));
|
|
item = Ext.apply({},
|
rendererAttributes.segment, {
|
slice: slice,
|
series: me,
|
storeItem: record,
|
index: i
|
});
|
items[i] = item;
|
// Create a new sprite if needed (no height)
|
if (!sprite) {
|
spriteOptions = Ext.apply({
|
type: "path",
|
group: group
|
}, Ext.apply(seriesStyle, colorArrayStyle && { fill: colorArrayStyle[i % colorArrayLength] } || {}));
|
sprite = surface.add(Ext.apply(spriteOptions, rendererAttributes));
|
}
|
slice.sprite = slice.sprite || [];
|
item.sprite = sprite;
|
slice.sprite.push(sprite);
|
if (animate) {
|
rendererAttributes = me.renderer(sprite, record, rendererAttributes, i, store);
|
sprite._to = rendererAttributes;
|
me.onAnimate(sprite, {
|
to: rendererAttributes
|
});
|
} else {
|
rendererAttributes = me.renderer(sprite, record, Ext.apply(rendererAttributes, {
|
hidden: false
|
}), i, store);
|
sprite.setAttributes(rendererAttributes, true);
|
}
|
}
|
|
if (me.needle) {
|
splitAngle = splitAngle * Math.PI / 180;
|
|
if (!me.needleSprite) {
|
me.needleSprite = me.chart.surface.add({
|
type: 'path',
|
path: ['M', centerX + (me.radius * +donut / 100) * cos(splitAngle),
|
centerY + -Math.abs((me.radius * +donut / 100) * sin(splitAngle)),
|
'L', centerX + me.radius * cos(splitAngle),
|
centerY + -Math.abs(me.radius * sin(splitAngle))],
|
'stroke-width': 4,
|
'stroke': '#222'
|
});
|
} else {
|
if (animate) {
|
me.onAnimate(me.needleSprite, {
|
to: {
|
path: ['M', centerX + (me.radius * +donut / 100) * cos(splitAngle),
|
centerY + -Math.abs((me.radius * +donut / 100) * sin(splitAngle)),
|
'L', centerX + me.radius * cos(splitAngle),
|
centerY + -Math.abs(me.radius * sin(splitAngle))]
|
}
|
});
|
} else {
|
me.needleSprite.setAttributes({
|
type: 'path',
|
path: ['M', centerX + (me.radius * +donut / 100) * cos(splitAngle),
|
centerY + -Math.abs((me.radius * +donut / 100) * sin(splitAngle)),
|
'L', centerX + me.radius * cos(splitAngle),
|
centerY + -Math.abs(me.radius * sin(splitAngle))]
|
});
|
}
|
}
|
me.needleSprite.setAttributes({
|
hidden: false
|
}, true);
|
}
|
|
delete me.value;
|
},
|
|
<span id='Ext-chart-series-Gauge-method-setValue'> /**
|
</span> * Sets the Gauge chart to the current specified value.
|
*/
|
setValue: function (value) {
|
this.value = value;
|
this.drawSeries();
|
},
|
|
<span id='Ext-chart-series-Gauge-method-onCreateLabel'> // @private callback for when creating a label sprite.
|
</span> onCreateLabel: function(storeItem, item, i, display) {},
|
|
<span id='Ext-chart-series-Gauge-method-onPlaceLabel'> // @private callback for when placing a label sprite.
|
</span> onPlaceLabel: function(label, storeItem, item, i, display, animate, index) {},
|
|
<span id='Ext-chart-series-Gauge-method-onPlaceCallout'> // @private callback for when placing a callout.
|
</span> onPlaceCallout: function() {},
|
|
<span id='Ext-chart-series-Gauge-method-onAnimate'> // @private handles sprite animation for the series.
|
</span> onAnimate: function(sprite, attr) {
|
sprite.show();
|
return this.callParent(arguments);
|
},
|
|
<span id='Ext-chart-series-Gauge-method-isItemInPoint'> isItemInPoint: function(x, y, item, i) {
|
</span> var me = this,
|
cx = me.centerX,
|
cy = me.centerY,
|
abs = Math.abs,
|
dx = abs(x - cx),
|
dy = abs(y - cy),
|
startAngle = item.startAngle,
|
endAngle = item.endAngle,
|
rho = Math.sqrt(dx * dx + dy * dy),
|
angle = Math.atan2(y - cy, x - cx) / me.rad;
|
|
//Only trigger events for the filled portion of the Gauge.
|
return (i === 0) && (angle >= startAngle && angle < endAngle &&
|
rho >= item.startRho && rho <= item.endRho);
|
},
|
|
<span id='Ext-chart-series-Gauge-method-getLegendColor'> /**
|
</span> * Returns the color of the series (to be displayed as color for the series legend item).
|
* @param item {Object} Info about the item; same format as returned by #getItemForPoint
|
*/
|
getLegendColor: function(index) {
|
var colors = this.colorSet || this.colorArrayStyle;
|
return colors[index % colors.length];
|
}
|
});
|
|
</pre>
|
</body>
|
</html>
|