12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124 |
- <!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-Line'>/**
- </span> * @class Ext.chart.series.Line
- * @extends Ext.chart.series.Cartesian
- *
- * Creates a Line Chart. A Line Chart is a useful visualization technique to display quantitative information for different
- * categories or other real values (as opposed to the bar chart), that can show some progression (or regression) in the dataset.
- * As with all other series, the Line Series must be appended in the *series* Chart array configuration. See the Chart
- * documentation for more information. A typical configuration object for the line series could be:
- *
- * @example
- * var store = Ext.create('Ext.data.JsonStore', {
- * fields: ['name', 'data1', 'data2', 'data3', 'data4', 'data5'],
- * data: [
- * { 'name': 'metric one', 'data1': 10, 'data2': 12, 'data3': 14, 'data4': 8, 'data5': 13 },
- * { 'name': 'metric two', 'data1': 7, 'data2': 8, 'data3': 16, 'data4': 10, 'data5': 3 },
- * { 'name': 'metric three', 'data1': 5, 'data2': 2, 'data3': 14, 'data4': 12, 'data5': 7 },
- * { 'name': 'metric four', 'data1': 2, 'data2': 14, 'data3': 6, 'data4': 1, 'data5': 23 },
- * { 'name': 'metric five', 'data1': 4, 'data2': 4, 'data3': 36, 'data4': 13, 'data5': 33 }
- * ]
- * });
- *
- * Ext.create('Ext.chart.Chart', {
- * renderTo: Ext.getBody(),
- * width: 500,
- * height: 300,
- * animate: true,
- * store: store,
- * axes: [
- * {
- * type: 'Numeric',
- * position: 'left',
- * fields: ['data1', 'data2'],
- * label: {
- * renderer: Ext.util.Format.numberRenderer('0,0')
- * },
- * title: 'Sample Values',
- * grid: true,
- * minimum: 0
- * },
- * {
- * type: 'Category',
- * position: 'bottom',
- * fields: ['name'],
- * title: 'Sample Metrics'
- * }
- * ],
- * series: [
- * {
- * type: 'line',
- * highlight: {
- * size: 7,
- * radius: 7
- * },
- * axis: 'left',
- * xField: 'name',
- * yField: 'data1',
- * markerConfig: {
- * type: 'cross',
- * size: 4,
- * radius: 4,
- * 'stroke-width': 0
- * }
- * },
- * {
- * type: 'line',
- * highlight: {
- * size: 7,
- * radius: 7
- * },
- * axis: 'left',
- * fill: true,
- * xField: 'name',
- * yField: 'data2',
- * markerConfig: {
- * type: 'circle',
- * size: 4,
- * radius: 4,
- * 'stroke-width': 0
- * }
- * }
- * ]
- * });
- *
- * In this configuration we're adding two series (or lines), one bound to the `data1`
- * property of the store and the other to `data3`. The type for both configurations is
- * `line`. The `xField` for both series is the same, the name propert of the store.
- * Both line series share the same axis, the left axis. You can set particular marker
- * configuration by adding properties onto the markerConfig object. Both series have
- * an object as highlight so that markers animate smoothly to the properties in highlight
- * when hovered. The second series has `fill=true` which means that the line will also
- * have an area below it of the same color.
- *
- * **Note:** In the series definition remember to explicitly set the axis to bind the
- * values of the line series to. This can be done by using the `axis` configuration property.
- */
- Ext.define('Ext.chart.series.Line', {
- /* Begin Definitions */
- extend: 'Ext.chart.series.Cartesian',
- alternateClassName: ['Ext.chart.LineSeries', 'Ext.chart.LineChart'],
- requires: ['Ext.chart.axis.Axis', 'Ext.chart.Shape', 'Ext.draw.Draw', 'Ext.fx.Anim'],
- /* End Definitions */
- type: 'line',
- alias: 'series.line',
- <span id='Ext-chart-series-Line-cfg-axis'> /**
- </span> * @cfg {String} axis
- * The position of the axis to bind the values to. Possible values are 'left', 'bottom', 'top' and 'right'.
- * You must explicitly set this value to bind the values of the line series to the ones in the axis, otherwise a
- * relative scale will be used.
- */
- <span id='Ext-chart-series-Line-cfg-selectionTolerance'> /**
- </span> * @cfg {Number} selectionTolerance
- * The offset distance from the cursor position to the line series to trigger events (then used for highlighting series, etc).
- */
- selectionTolerance: 20,
- <span id='Ext-chart-series-Line-cfg-showMarkers'> /**
- </span> * @cfg {Boolean} showMarkers
- * Whether markers should be displayed at the data points along the line. If true,
- * then the {@link #markerConfig} config item will determine the markers' styling.
- */
- showMarkers: true,
- <span id='Ext-chart-series-Line-cfg-markerConfig'> /**
- </span> * @cfg {Object} markerConfig
- * The display style for the markers. Only used if {@link #showMarkers} is true.
- * The markerConfig is a configuration object containing the same set of properties defined in
- * the Sprite class. For example, if we were to set red circles as markers to the line series we could
- * pass the object:
- *
- <pre><code>
- markerConfig: {
- type: 'circle',
- radius: 4,
- 'fill': '#f00'
- }
- </code></pre>
- */
- markerConfig: {},
- <span id='Ext-chart-series-Line-cfg-style'> /**
- </span> * @cfg {Object} style
- * An object containing style properties for the visualization lines and fill.
- * These styles will override the theme styles. The following are valid style properties:
- *
- * - `stroke` - an rgb or hex color string for the background color of the line
- * - `stroke-width` - the width of the stroke (integer)
- * - `fill` - the background fill color string (hex or rgb), only works if {@link #fill} is `true`
- * - `opacity` - the opacity of the line and the fill color (decimal)
- *
- * Example usage:
- *
- * style: {
- * stroke: '#00ff00',
- * 'stroke-width': 10,
- * fill: '#80A080',
- * opacity: 0.2
- * }
- */
- style: {},
- <span id='Ext-chart-series-Line-cfg-smooth'> /**
- </span> * @cfg {Boolean/Number} smooth
- * If set to `true` or a non-zero number, the line will be smoothed/rounded around its points; otherwise
- * straight line segments will be drawn.
- *
- * A numeric value is interpreted as a divisor of the horizontal distance between consecutive points in
- * the line; larger numbers result in sharper curves while smaller numbers result in smoother curves.
- *
- * If set to `true` then a default numeric value of 3 will be used. Defaults to `false`.
- */
- smooth: false,
- <span id='Ext-chart-series-Line-property-defaultSmoothness'> /**
- </span> * @private Default numeric smoothing value to be used when {@link #smooth} = true.
- */
- defaultSmoothness: 3,
- <span id='Ext-chart-series-Line-cfg-fill'> /**
- </span> * @cfg {Boolean} fill
- * If true, the area below the line will be filled in using the {@link #style eefill} and
- * {@link #style opacity} config properties. Defaults to false.
- */
- fill: false,
- constructor: function(config) {
- this.callParent(arguments);
- var me = this,
- surface = me.chart.surface,
- shadow = me.chart.shadow,
- i, l;
- config.highlightCfg = Ext.Object.merge({ 'stroke-width': 3 }, config.highlightCfg);
- Ext.apply(me, config, {
- shadowAttributes: [{
- "stroke-width": 6,
- "stroke-opacity": 0.05,
- stroke: 'rgb(0, 0, 0)',
- translate: {
- x: 1,
- y: 1
- }
- }, {
- "stroke-width": 4,
- "stroke-opacity": 0.1,
- stroke: 'rgb(0, 0, 0)',
- translate: {
- x: 1,
- y: 1
- }
- }, {
- "stroke-width": 2,
- "stroke-opacity": 0.15,
- stroke: 'rgb(0, 0, 0)',
- translate: {
- x: 1,
- y: 1
- }
- }]
- });
- me.group = surface.getGroup(me.seriesId);
- if (me.showMarkers) {
- me.markerGroup = surface.getGroup(me.seriesId + '-markers');
- }
- if (shadow) {
- for (i = 0, l = me.shadowAttributes.length; i < l; i++) {
- me.shadowGroups.push(surface.getGroup(me.seriesId + '-shadows' + i));
- }
- }
- },
- // @private makes an average of points when there are more data points than pixels to be rendered.
- shrink: function(xValues, yValues, size) {
- // Start at the 2nd point...
- var len = xValues.length,
- ratio = Math.floor(len / size),
- i = 1,
- xSum = 0,
- ySum = 0,
- xRes = [+xValues[0]],
- yRes = [+yValues[0]];
- for (; i < len; ++i) {
- xSum += +xValues[i] || 0;
- ySum += +yValues[i] || 0;
- if (i % ratio == 0) {
- xRes.push(xSum/ratio);
- yRes.push(ySum/ratio);
- xSum = 0;
- ySum = 0;
- }
- }
- return {
- x: xRes,
- y: yRes
- };
- },
- <span id='Ext-chart-series-Line-method-drawSeries'> /**
- </span> * Draws the series for the current chart.
- */
- drawSeries: function() {
- var me = this,
- chart = me.chart,
- chartAxes = chart.axes,
- store = chart.getChartStore(),
- data = store.data.items,
- record,
- storeCount = store.getCount(),
- surface = me.chart.surface,
- bbox = {},
- group = me.group,
- showMarkers = me.showMarkers,
- markerGroup = me.markerGroup,
- enableShadows = chart.shadow,
- shadowGroups = me.shadowGroups,
- shadowAttributes = me.shadowAttributes,
- smooth = me.smooth,
- lnsh = shadowGroups.length,
- dummyPath = ["M"],
- path = ["M"],
- renderPath = ["M"],
- smoothPath = ["M"],
- markerIndex = chart.markerIndex,
- axes = [].concat(me.axis),
- shadowBarAttr,
- xValues = [],
- xValueMap = {},
- yValues = [],
- yValueMap = {},
- onbreak = false,
- storeIndices = [],
- markerStyle = me.markerStyle,
- seriesStyle = me.seriesStyle,
- colorArrayStyle = me.colorArrayStyle,
- colorArrayLength = colorArrayStyle && colorArrayStyle.length || 0,
- isNumber = Ext.isNumber,
- seriesIdx = me.seriesIdx,
- boundAxes = me.getAxesForXAndYFields(),
- boundXAxis = boundAxes.xAxis,
- boundYAxis = boundAxes.yAxis,
- shadows, shadow, shindex, fromPath, fill, fillPath, rendererAttributes,
- x, y, prevX, prevY, firstX, firstY, markerCount, i, j, ln, axis, ends, marker, markerAux, item, xValue,
- yValue, coords, xScale, yScale, minX, maxX, minY, maxY, line, animation, endMarkerStyle,
- endLineStyle, type, count, opacity, lineOpacity, fillOpacity, fillDefaultValue;
- if (me.fireEvent('beforedraw', me) === false) {
- return;
- }
- //if store is empty or the series is excluded in the legend then there's nothing to draw.
- if (!storeCount || me.seriesIsHidden) {
- me.hide();
- me.items = [];
- if (me.line) {
- me.line.hide(true);
- if (me.line.shadows) {
- shadows = me.line.shadows;
- for (j = 0, lnsh = shadows.length; j < lnsh; j++) {
- shadow = shadows[j];
- shadow.hide(true);
- }
- }
- if (me.fillPath) {
- me.fillPath.hide(true);
- }
- }
- me.line = null;
- me.fillPath = null;
- return;
- }
- //prepare style objects for line and markers
- endMarkerStyle = Ext.apply(markerStyle || {}, me.markerConfig, {
- fill: me.seriesStyle.fill || colorArrayStyle[seriesIdx % colorArrayStyle.length]
- });
- type = endMarkerStyle.type;
- delete endMarkerStyle.type;
- endLineStyle = seriesStyle;
- //if no stroke with is specified force it to 0.5 because this is
- //about making *lines*
- if (!endLineStyle['stroke-width']) {
- endLineStyle['stroke-width'] = 0.5;
- }
-
- //set opacity values
- opacity = 'opacity' in endLineStyle ? endLineStyle.opacity : 1;
- fillDefaultValue = 'opacity' in endLineStyle ? endLineStyle.opacity : 0.3;
- lineOpacity = 'lineOpacity' in endLineStyle ? endLineStyle.lineOpacity : opacity;
- fillOpacity = 'fillOpacity' in endLineStyle ? endLineStyle.fillOpacity : fillDefaultValue;
- //If we're using a time axis and we need to translate the points,
- //then reuse the first markers as the last markers.
- if (markerIndex && markerGroup && markerGroup.getCount()) {
- for (i = 0; i < markerIndex; i++) {
- marker = markerGroup.getAt(i);
- markerGroup.remove(marker);
- markerGroup.add(marker);
- markerAux = markerGroup.getAt(markerGroup.getCount() - 2);
- marker.setAttributes({
- x: 0,
- y: 0,
- translate: {
- x: markerAux.attr.translation.x,
- y: markerAux.attr.translation.y
- }
- }, true);
- }
- }
- me.unHighlightItem();
- me.cleanHighlights();
- me.setBBox();
- bbox = me.bbox;
- me.clipRect = [bbox.x, bbox.y, bbox.width, bbox.height];
- if (axis = chartAxes.get(boundXAxis)) {
- ends = axis.applyData();
- minX = ends.from;
- maxX = ends.to;
- }
- if (axis = chartAxes.get(boundYAxis)) {
- ends = axis.applyData();
- minY = ends.from;
- maxY = ends.to;
- }
- // If a field was specified without a corresponding axis, create one to get bounds
- if (me.xField && !Ext.isNumber(minX)) {
- axis = me.getMinMaxXValues();
- minX = axis[0];
- maxX = axis[1];
- }
- if (me.yField && !Ext.isNumber(minY)) {
- axis = me.getMinMaxYValues();
- minY = axis[0];
- maxY = axis[1];
- }
-
- if (isNaN(minX)) {
- minX = 0;
- xScale = bbox.width / ((storeCount - 1) || 1);
- }
- else {
- xScale = bbox.width / ((maxX - minX) || (storeCount -1) || 1);
- }
- if (isNaN(minY)) {
- minY = 0;
- yScale = bbox.height / ((storeCount - 1) || 1);
- }
- else {
- yScale = bbox.height / ((maxY - minY) || (storeCount - 1) || 1);
- }
- // Extract all x and y values from the store
- for (i = 0, ln = data.length; i < ln; i++) {
- record = data[i];
- xValue = record.get(me.xField);
- // Ensure a value
- if (typeof xValue == 'string' || typeof xValue == 'object' && !Ext.isDate(xValue)
- //set as uniform distribution if the axis is a category axis.
- || boundXAxis && chartAxes.get(boundXAxis) && chartAxes.get(boundXAxis).type == 'Category') {
- if (xValue in xValueMap) {
- xValue = xValueMap[xValue];
- } else {
- xValue = xValueMap[xValue] = i;
- }
- }
- // Filter out values that don't fit within the pan/zoom buffer area
- yValue = record.get(me.yField);
- //skip undefined values
- if (typeof yValue == 'undefined' || (typeof yValue == 'string' && !yValue)) {
- //<debug warn>
- if (Ext.isDefined(Ext.global.console)) {
- Ext.global.console.warn("[Ext.chart.series.Line] Skipping a store element with an undefined value at ", record, xValue, yValue);
- }
- //</debug>
- continue;
- }
- // Ensure a value
- if (typeof yValue == 'string' || typeof yValue == 'object' && !Ext.isDate(yValue)
- //set as uniform distribution if the axis is a category axis.
- || boundYAxis && chartAxes.get(boundYAxis) && chartAxes.get(boundYAxis).type == 'Category') {
- yValue = i;
- }
- storeIndices.push(i);
- xValues.push(xValue);
- yValues.push(yValue);
- }
- ln = xValues.length;
- if (ln > bbox.width) {
- coords = me.shrink(xValues, yValues, bbox.width);
- xValues = coords.x;
- yValues = coords.y;
- }
- me.items = [];
- count = 0;
- ln = xValues.length;
- for (i = 0; i < ln; i++) {
- xValue = xValues[i];
- yValue = yValues[i];
- if (yValue === false) {
- if (path.length == 1) {
- path = [];
- }
- onbreak = true;
- me.items.push(false);
- continue;
- } else {
- x = (bbox.x + (xValue - minX) * xScale).toFixed(2);
- y = ((bbox.y + bbox.height) - (yValue - minY) * yScale).toFixed(2);
- if (onbreak) {
- onbreak = false;
- path.push('M');
- }
- path = path.concat([x, y]);
- }
- if ((typeof firstY == 'undefined') && (typeof y != 'undefined')) {
- firstY = y;
- firstX = x;
- }
- // If this is the first line, create a dummypath to animate in from.
- if (!me.line || chart.resizing) {
- dummyPath = dummyPath.concat([x, bbox.y + bbox.height / 2]);
- }
- // When resizing, reset before animating
- if (chart.animate && chart.resizing && me.line) {
- me.line.setAttributes({
- path: dummyPath,
- opacity: lineOpacity
- }, true);
- if (me.fillPath) {
- me.fillPath.setAttributes({
- path: dummyPath,
- opacity: fillOpacity
- }, true);
- }
- if (me.line.shadows) {
- shadows = me.line.shadows;
- for (j = 0, lnsh = shadows.length; j < lnsh; j++) {
- shadow = shadows[j];
- shadow.setAttributes({
- path: dummyPath
- }, true);
- }
- }
- }
- if (showMarkers) {
- marker = markerGroup.getAt(count++);
- if (!marker) {
- marker = Ext.chart.Shape[type](surface, Ext.apply({
- group: [group, markerGroup],
- x: 0, y: 0,
- translate: {
- x: +(prevX || x),
- y: prevY || (bbox.y + bbox.height / 2)
- },
- value: '"' + xValue + ', ' + yValue + '"',
- zIndex: 4000
- }, endMarkerStyle));
- marker._to = {
- translate: {
- x: +x,
- y: +y
- }
- };
- } else {
- marker.setAttributes({
- value: '"' + xValue + ', ' + yValue + '"',
- x: 0, y: 0,
- hidden: false
- }, true);
- marker._to = {
- translate: {
- x: +x,
- y: +y
- }
- };
- }
- }
- me.items.push({
- series: me,
- value: [xValue, yValue],
- point: [x, y],
- sprite: marker,
- storeItem: store.getAt(storeIndices[i])
- });
- prevX = x;
- prevY = y;
- }
- if (path.length <= 1) {
- //nothing to be rendered
- return;
- }
- if (me.smooth) {
- smoothPath = Ext.draw.Draw.smooth(path, isNumber(smooth) ? smooth : me.defaultSmoothness);
- }
- renderPath = smooth ? smoothPath : path;
- //Correct path if we're animating timeAxis intervals
- if (chart.markerIndex && me.previousPath) {
- fromPath = me.previousPath;
- if (!smooth) {
- Ext.Array.erase(fromPath, 1, 2);
- }
- } else {
- fromPath = path;
- }
- // Only create a line if one doesn't exist.
- if (!me.line) {
- me.line = surface.add(Ext.apply({
- type: 'path',
- group: group,
- path: dummyPath,
- stroke: endLineStyle.stroke || endLineStyle.fill
- }, endLineStyle || {}));
- //set configuration opacity
- me.line.setAttributes({
- opacity: lineOpacity
- }, true);
- if (enableShadows) {
- me.line.setAttributes(Ext.apply({}, me.shadowOptions), true);
- }
- //unset fill here (there's always a default fill withing the themes).
- me.line.setAttributes({
- fill: 'none',
- zIndex: 3000
- });
- if (!endLineStyle.stroke && colorArrayLength) {
- me.line.setAttributes({
- stroke: colorArrayStyle[seriesIdx % colorArrayLength]
- }, true);
- }
- if (enableShadows) {
- //create shadows
- shadows = me.line.shadows = [];
- for (shindex = 0; shindex < lnsh; shindex++) {
- shadowBarAttr = shadowAttributes[shindex];
- shadowBarAttr = Ext.apply({}, shadowBarAttr, { path: dummyPath });
- shadow = surface.add(Ext.apply({}, {
- type: 'path',
- group: shadowGroups[shindex]
- }, shadowBarAttr));
- shadows.push(shadow);
- }
- }
- }
- if (me.fill) {
- fillPath = renderPath.concat([
- ["L", x, bbox.y + bbox.height],
- ["L", firstX, bbox.y + bbox.height],
- ["L", firstX, firstY]
- ]);
- if (!me.fillPath) {
- me.fillPath = surface.add({
- group: group,
- type: 'path',
- fill: endLineStyle.fill || colorArrayStyle[seriesIdx % colorArrayLength],
- path: dummyPath
- });
- }
- }
- markerCount = showMarkers && markerGroup.getCount();
- if (chart.animate) {
- fill = me.fill;
- line = me.line;
- //Add renderer to line. There is not unique record associated with this.
- rendererAttributes = me.renderer(line, false, { path: renderPath }, i, store);
- Ext.apply(rendererAttributes, endLineStyle || {}, {
- stroke: endLineStyle.stroke || endLineStyle.fill
- });
- //fill should not be used here but when drawing the special fill path object
- delete rendererAttributes.fill;
- line.show(true);
- if (chart.markerIndex && me.previousPath) {
- me.animation = animation = me.onAnimate(line, {
- to: rendererAttributes,
- from: {
- path: fromPath
- }
- });
- } else {
- me.animation = animation = me.onAnimate(line, {
- to: rendererAttributes
- });
- }
- //animate shadows
- if (enableShadows) {
- shadows = line.shadows;
- for(j = 0; j < lnsh; j++) {
- shadows[j].show(true);
- if (chart.markerIndex && me.previousPath) {
- me.onAnimate(shadows[j], {
- to: { path: renderPath },
- from: { path: fromPath }
- });
- } else {
- me.onAnimate(shadows[j], {
- to: { path: renderPath }
- });
- }
- }
- }
- //animate fill path
- if (fill) {
- me.fillPath.show(true);
- me.onAnimate(me.fillPath, {
- to: Ext.apply({}, {
- path: fillPath,
- fill: endLineStyle.fill || colorArrayStyle[seriesIdx % colorArrayLength],
- 'stroke-width': 0,
- opacity: fillOpacity
- }, endLineStyle || {})
- });
- }
- //animate markers
- if (showMarkers) {
- count = 0;
- for(i = 0; i < ln; i++) {
- if (me.items[i]) {
- item = markerGroup.getAt(count++);
- if (item) {
- rendererAttributes = me.renderer(item, store.getAt(i), item._to, i, store);
- me.onAnimate(item, {
- to: Ext.apply(rendererAttributes, endMarkerStyle || {})
- });
- item.show(true);
- }
- }
- }
- for(; count < markerCount; count++) {
- item = markerGroup.getAt(count);
- item.hide(true);
- }
- // for(i = 0; i < (chart.markerIndex || 0)-1; i++) {
- // item = markerGroup.getAt(i);
- // item.hide(true);
- // }
- }
- } else {
- rendererAttributes = me.renderer(me.line, false, { path: renderPath, hidden: false }, i, store);
- Ext.apply(rendererAttributes, endLineStyle || {}, {
- stroke: endLineStyle.stroke || endLineStyle.fill
- });
- //fill should not be used here but when drawing the special fill path object
- delete rendererAttributes.fill;
- me.line.setAttributes(rendererAttributes, true);
- me.line.setAttributes({
- opacity: lineOpacity
- }, true);
- //set path for shadows
- if (enableShadows) {
- shadows = me.line.shadows;
- for(j = 0; j < lnsh; j++) {
- shadows[j].setAttributes({
- path: renderPath,
- hidden: false
- }, true);
- }
- }
- if (me.fill) {
- me.fillPath.setAttributes({
- path: fillPath,
- hidden: false,
- opacity: fillOpacity
- }, true);
- }
- if (showMarkers) {
- count = 0;
- for(i = 0; i < ln; i++) {
- if (me.items[i]) {
- item = markerGroup.getAt(count++);
- if (item) {
- rendererAttributes = me.renderer(item, store.getAt(i), item._to, i, store);
- item.setAttributes(Ext.apply(endMarkerStyle || {}, rendererAttributes || {}), true);
- if (!item.attr.hidden) {
- item.show(true);
- }
- }
- }
- }
- for(; count < markerCount; count++) {
- item = markerGroup.getAt(count);
- item.hide(true);
- }
- }
- }
- if (chart.markerIndex) {
- if (me.smooth) {
- Ext.Array.erase(path, 1, 2);
- } else {
- Ext.Array.splice(path, 1, 0, path[1], path[2]);
- }
- me.previousPath = path;
- }
- me.renderLabels();
- me.renderCallouts();
- me.fireEvent('draw', me);
- },
- // @private called when a label is to be created.
- onCreateLabel: function(storeItem, item, i, display) {
- var me = this,
- group = me.labelsGroup,
- config = me.label,
- bbox = me.bbox,
- endLabelStyle = Ext.apply(config, me.seriesLabelStyle);
- return me.chart.surface.add(Ext.apply({
- 'type': 'text',
- 'text-anchor': 'middle',
- 'group': group,
- 'x': item.point[0],
- 'y': bbox.y + bbox.height / 2
- }, endLabelStyle || {}));
- },
- // @private called when a label is to be created.
- onPlaceLabel: function(label, storeItem, item, i, display, animate) {
- var me = this,
- chart = me.chart,
- resizing = chart.resizing,
- config = me.label,
- format = config.renderer,
- field = config.field,
- bbox = me.bbox,
- x = item.point[0],
- y = item.point[1],
- radius = item.sprite.attr.radius,
- bb, width, height;
- label.setAttributes({
- text: format(storeItem.get(field)),
- hidden: true
- }, true);
- if (display == 'rotate') {
- label.setAttributes({
- 'text-anchor': 'start',
- 'rotation': {
- x: x,
- y: y,
- degrees: -45
- }
- }, true);
- //correct label position to fit into the box
- bb = label.getBBox();
- width = bb.width;
- height = bb.height;
- x = x < bbox.x? bbox.x : x;
- x = (x + width > bbox.x + bbox.width)? (x - (x + width - bbox.x - bbox.width)) : x;
- y = (y - height < bbox.y)? bbox.y + height : y;
- } else if (display == 'under' || display == 'over') {
- //TODO(nicolas): find out why width/height values in circle bounding boxes are undefined.
- bb = item.sprite.getBBox();
- bb.width = bb.width || (radius * 2);
- bb.height = bb.height || (radius * 2);
- y = y + (display == 'over'? -bb.height : bb.height);
- //correct label position to fit into the box
- bb = label.getBBox();
- width = bb.width/2;
- height = bb.height/2;
- x = x - width < bbox.x? bbox.x + width : x;
- x = (x + width > bbox.x + bbox.width) ? (x - (x + width - bbox.x - bbox.width)) : x;
- y = y - height < bbox.y? bbox.y + height : y;
- y = (y + height > bbox.y + bbox.height) ? (y - (y + height - bbox.y - bbox.height)) : y;
- }
- if (me.chart.animate && !me.chart.resizing) {
- label.show(true);
- me.onAnimate(label, {
- to: {
- x: x,
- y: y
- }
- });
- } else {
- label.setAttributes({
- x: x,
- y: y
- }, true);
- if (resizing && me.animation) {
- me.animation.on('afteranimate', function() {
- label.show(true);
- });
- } else {
- label.show(true);
- }
- }
- },
- // @private Overriding highlights.js highlightItem method.
- highlightItem: function() {
- var me = this;
- me.callParent(arguments);
- if (me.line && !me.highlighted) {
- if (!('__strokeWidth' in me.line)) {
- me.line.__strokeWidth = parseFloat(me.line.attr['stroke-width']) || 0;
- }
- if (me.line.__anim) {
- me.line.__anim.paused = true;
- }
- me.line.__anim = Ext.create('Ext.fx.Anim', {
- target: me.line,
- to: {
- 'stroke-width': me.line.__strokeWidth + 3
- }
- });
- me.highlighted = true;
- }
- },
- // @private Overriding highlights.js unHighlightItem method.
- unHighlightItem: function() {
- var me = this;
- me.callParent(arguments);
- if (me.line && me.highlighted) {
- me.line.__anim = Ext.create('Ext.fx.Anim', {
- target: me.line,
- to: {
- 'stroke-width': me.line.__strokeWidth
- }
- });
- me.highlighted = false;
- }
- },
- // @private called when a callout needs to be placed.
- onPlaceCallout : function(callout, storeItem, item, i, display, animate, index) {
- if (!display) {
- return;
- }
- var me = this,
- chart = me.chart,
- surface = chart.surface,
- resizing = chart.resizing,
- config = me.callouts,
- items = me.items,
- prev = i == 0? false : items[i -1].point,
- next = (i == items.length -1)? false : items[i +1].point,
- cur = [+item.point[0], +item.point[1]],
- dir, norm, normal, a, aprev, anext,
- offsetFromViz = config.offsetFromViz || 30,
- offsetToSide = config.offsetToSide || 10,
- offsetBox = config.offsetBox || 3,
- boxx, boxy, boxw, boxh,
- p, clipRect = me.clipRect,
- bbox = {
- width: config.styles.width || 10,
- height: config.styles.height || 10
- },
- x, y;
- //get the right two points
- if (!prev) {
- prev = cur;
- }
- if (!next) {
- next = cur;
- }
- a = (next[1] - prev[1]) / (next[0] - prev[0]);
- aprev = (cur[1] - prev[1]) / (cur[0] - prev[0]);
- anext = (next[1] - cur[1]) / (next[0] - cur[0]);
- norm = Math.sqrt(1 + a * a);
- dir = [1 / norm, a / norm];
- normal = [-dir[1], dir[0]];
- //keep the label always on the outer part of the "elbow"
- if (aprev > 0 && anext < 0 && normal[1] < 0
- || aprev < 0 && anext > 0 && normal[1] > 0) {
- normal[0] *= -1;
- normal[1] *= -1;
- } else if (Math.abs(aprev) < Math.abs(anext) && normal[0] < 0
- || Math.abs(aprev) > Math.abs(anext) && normal[0] > 0) {
- normal[0] *= -1;
- normal[1] *= -1;
- }
- //position
- x = cur[0] + normal[0] * offsetFromViz;
- y = cur[1] + normal[1] * offsetFromViz;
- //box position and dimensions
- boxx = x + (normal[0] > 0? 0 : -(bbox.width + 2 * offsetBox));
- boxy = y - bbox.height /2 - offsetBox;
- boxw = bbox.width + 2 * offsetBox;
- boxh = bbox.height + 2 * offsetBox;
- //now check if we're out of bounds and invert the normal vector correspondingly
- //this may add new overlaps between labels (but labels won't be out of bounds).
- if (boxx < clipRect[0] || (boxx + boxw) > (clipRect[0] + clipRect[2])) {
- normal[0] *= -1;
- }
- if (boxy < clipRect[1] || (boxy + boxh) > (clipRect[1] + clipRect[3])) {
- normal[1] *= -1;
- }
- //update positions
- x = cur[0] + normal[0] * offsetFromViz;
- y = cur[1] + normal[1] * offsetFromViz;
- //update box position and dimensions
- boxx = x + (normal[0] > 0? 0 : -(bbox.width + 2 * offsetBox));
- boxy = y - bbox.height /2 - offsetBox;
- boxw = bbox.width + 2 * offsetBox;
- boxh = bbox.height + 2 * offsetBox;
- if (chart.animate) {
- //set the line from the middle of the pie to the box.
- me.onAnimate(callout.lines, {
- to: {
- path: ["M", cur[0], cur[1], "L", x, y, "Z"]
- }
- });
- //set component position
- if (callout.panel) {
- callout.panel.setPosition(boxx, boxy, true);
- }
- }
- else {
- //set the line from the middle of the pie to the box.
- callout.lines.setAttributes({
- path: ["M", cur[0], cur[1], "L", x, y, "Z"]
- }, true);
- //set component position
- if (callout.panel) {
- callout.panel.setPosition(boxx, boxy);
- }
- }
- for (p in callout) {
- callout[p].show(true);
- }
- },
- isItemInPoint: function(x, y, item, i) {
- var me = this,
- items = me.items,
- tolerance = me.selectionTolerance,
- result = null,
- prevItem,
- nextItem,
- prevPoint,
- nextPoint,
- ln,
- x1,
- y1,
- x2,
- y2,
- xIntersect,
- yIntersect,
- dist1, dist2, dist, midx, midy,
- sqrt = Math.sqrt, abs = Math.abs;
- nextItem = items[i];
- prevItem = i && items[i - 1];
- if (i >= ln) {
- prevItem = items[ln - 1];
- }
- prevPoint = prevItem && prevItem.point;
- nextPoint = nextItem && nextItem.point;
- x1 = prevItem ? prevPoint[0] : nextPoint[0] - tolerance;
- y1 = prevItem ? prevPoint[1] : nextPoint[1];
- x2 = nextItem ? nextPoint[0] : prevPoint[0] + tolerance;
- y2 = nextItem ? nextPoint[1] : prevPoint[1];
- dist1 = sqrt((x - x1) * (x - x1) + (y - y1) * (y - y1));
- dist2 = sqrt((x - x2) * (x - x2) + (y - y2) * (y - y2));
- dist = Math.min(dist1, dist2);
- if (dist <= tolerance) {
- return dist == dist1? prevItem : nextItem;
- }
- return false;
- },
- // @private toggle visibility of all series elements (markers, sprites).
- toggleAll: function(show) {
- var me = this,
- i, ln, shadow, shadows;
- if (!show) {
- Ext.chart.series.Cartesian.prototype.hideAll.call(me);
- }
- else {
- Ext.chart.series.Cartesian.prototype.showAll.call(me);
- }
- if (me.line) {
- me.line.setAttributes({
- hidden: !show
- }, true);
- //hide shadows too
- if (me.line.shadows) {
- for (i = 0, shadows = me.line.shadows, ln = shadows.length; i < ln; i++) {
- shadow = shadows[i];
- shadow.setAttributes({
- hidden: !show
- }, true);
- }
- }
- }
- if (me.fillPath) {
- me.fillPath.setAttributes({
- hidden: !show
- }, true);
- }
- },
- // @private hide all series elements (markers, sprites).
- hideAll: function() {
- this.toggleAll(false);
- },
- // @private hide all series elements (markers, sprites).
- showAll: function() {
- this.toggleAll(true);
- }
- });
- </pre>
- </body>
- </html>
|