Browse Source

Added candle-stick chart beta

pull/1701/head
Roman Charugin 9 years ago
parent
commit
cffb472a51
  1. 1
      Gruntfile.coffee
  2. 12
      c3.css
  3. 324
      c3.js
  4. 10
      c3.min.js
  5. 17
      htdocs/samples/chart_bar.html
  6. 44
      htdocs/samples/chart_candlestick.html
  7. 31
      htdocs/samples/simple.html
  8. 21
      src/class.js
  9. 10
      src/config.js
  10. 18
      src/core.js
  11. 2
      src/domain.js
  12. 262
      src/shape.candlestick.js
  13. 10
      src/type.js

1
Gruntfile.coffee

@ -35,6 +35,7 @@ module.exports = (grunt) ->
'src/shape.js',
'src/shape.line.js',
'src/shape.bar.js',
'src/shape.candlestick.js',
'src/text.js',
'src/type.js',
'src/grid.js',

12
c3.css

@ -1,7 +1,7 @@
/*-- Chart --*/
.c3 svg {
font: 10px sans-serif;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0); }
-webkit-tap-highlight-color: transparent; }
.c3 path, .c3 line {
fill: none;
@ -12,7 +12,11 @@
-moz-user-select: none;
user-select: none; }
.c3-legend-item-tile, .c3-xgrid-focus, .c3-ygrid, .c3-event-rect, .c3-bars path {
.c3-legend-item-tile,
.c3-xgrid-focus,
.c3-ygrid,
.c3-event-rect,
.c3-bars path {
shape-rendering: crispEdges; }
.c3-chart-arc path {
@ -71,11 +75,11 @@
/*-- Region --*/
.c3-region {
fill: steelblue;
fill-opacity: 0.1; }
fill-opacity: .1; }
/*-- Brush --*/
.c3-brush .extent {
fill-opacity: 0.1; }
fill-opacity: .1; }
/*-- Select - Drag --*/
/*-- Legend --*/

324
c3.js

@ -181,6 +181,7 @@
if (this.initArc) { this.initArc(); }
if (this.initGauge) { this.initGauge(); }
if (this.initText) { this.initText(); }
if (this.initCandleStick) { this.initCandleStick(); }
};
c3_chart_internal_fn.initWithData = function (data) {
@ -451,6 +452,9 @@
//-- Arc --//
if ($$.hasArcType() && $$.updateTargetsForArc) { $$.updateTargetsForArc(targets); }
//-- Candle stick --//
if ($$.hasCandleStickType() && $$.updateTargetsForArc) { $$.updateTargetsForCandleStick(targets); }
/*-- Sub --*/
if ($$.updateTargetsForSubchart) { $$.updateTargetsForSubchart(targets); }
@ -467,12 +471,15 @@
c3_chart_internal_fn.redraw = function (options, transitions) {
var $$ = this, main = $$.main, d3 = $$.d3, config = $$.config;
var areaIndices = $$.getShapeIndices($$.isAreaType), barIndices = $$.getShapeIndices($$.isBarType), lineIndices = $$.getShapeIndices($$.isLineType);
var areaIndices = $$.getShapeIndices($$.isAreaType),
barIndices = $$.getShapeIndices($$.isBarType),
lineIndices = $$.getShapeIndices($$.isLineType),
candleStickIndicies = $$.getShapeIndices($$.isCandleStickType);
var withY, withSubchart, withTransition, withTransitionForExit, withTransitionForAxis,
withTransform, withUpdateXDomain, withUpdateOrgXDomain, withTrimXDomain, withLegend,
withEventRect, withDimension, withUpdateXAxis;
var hideAxis = $$.hasArcType();
var drawArea, drawBar, drawLine, xForText, yForText;
var drawArea, drawBar, drawCandleStick, drawLine, xForText, yForText;
var duration, durationForExit, durationForAxis;
var waitForDraw, flow;
var targetsToShow = $$.filterTargetsToShow($$.data.targets), tickValues, i, intervalForCulling, xDomainForZoom;
@ -566,6 +573,7 @@
// setup drawer - MEMO: these must be called after axis updated
drawArea = $$.generateDrawArea ? $$.generateDrawArea(areaIndices, false) : undefined;
drawBar = $$.generateDrawBar ? $$.generateDrawBar(barIndices) : undefined;
drawCandleStick = $$.generateDrawCandleStick ? $$.generateDrawCandleStick(candleStickIndicies) : undefined;
drawLine = $$.generateDrawLine ? $$.generateDrawLine(lineIndices, false) : undefined;
xForText = $$.generateXYForText(areaIndices, barIndices, lineIndices, true);
yForText = $$.generateXYForText(areaIndices, barIndices, lineIndices, false);
@ -596,6 +604,9 @@
// bars
$$.updateBar(durationForExit);
// candlestick
$$.updateCandleStick(durationForExit);
// lines, areas and cricles
$$.updateLine(durationForExit);
$$.updateArea(durationForExit);
@ -642,6 +653,7 @@
flow: options.flow,
duration: options.flow.duration,
drawBar: drawBar,
drawCandleStick: drawCandleStick,
drawLine: drawLine,
drawArea: drawArea,
cx: cx,
@ -662,6 +674,7 @@
$$.redrawBar(drawBar, true),
$$.redrawLine(drawLine, true),
$$.redrawArea(drawArea, true),
$$.redrawCandleStick(drawCandleStick, true),
$$.redrawCircle(cx, cy, true),
$$.redrawText(xForText, yForText, options.flow, true),
$$.redrawRegion(true),
@ -691,6 +704,7 @@
$$.redrawBar(drawBar);
$$.redrawLine(drawLine);
$$.redrawArea(drawArea);
$$.redrawCandleStick(drawCandleStick),
$$.redrawCircle(cx, cy);
$$.redrawText(xForText, yForText, options.flow);
$$.redrawRegion();
@ -1187,7 +1201,7 @@
axis_y_label: {},
axis_y_tick_format: undefined,
axis_y_tick_outer: true,
axis_y_tick_values: null,
axis_y_tick_values: null,
axis_y_tick_rotate: 0,
axis_y_tick_count: undefined,
axis_y_tick_time_value: undefined,
@ -1233,6 +1247,14 @@
bar_width_ratio: 0.6,
bar_width_max: undefined,
bar_zerobased: true,
// candlestick
candlestick_width: undefined,
candlestick_width_ratio: 0.6,
candlestick_width_max: undefined,
candlestick_data_min: 'min',
candlestick_data_max: 'max',
candlestick_data_start: 'start',
candlestick_data_end: 'end',
// area
area_zerobased: true,
// pie
@ -1575,7 +1597,7 @@
maxDataCount, padding, paddingLeft, paddingRight;
if ($$.isCategorized()) {
padding = 0;
} else if ($$.hasType('bar')) {
} else if ($$.hasType('bar') || $$.hasType('candle-stick')) {
maxDataCount = $$.getMaxDataCount();
padding = maxDataCount > 1 ? (diff / (maxDataCount - 1)) / 2 : 0.5;
} else {
@ -3364,6 +3386,269 @@
return sx < mouse[0] && mouse[0] < ex && ey < mouse[1] && mouse[1] < sy;
};
// Candle stick chart initialization
c3_chart_internal_fn.initCandleStick = function() {
var $$ = this;
$$.main.select('.' + CLASS.chart).append("g")
.attr('class', CLASS.chartCandleSticks);
};
// Convert candlestick data
c3_chart_internal_fn.convertTargetsForCandleStick = function (targets) {
var $$ = this,
config = $$.config,
minValues = (targets.filter(function (t) { return t.id === config.candlestick_data_min; })[0] || {}).values,
maxValues = (targets.filter(function (t) { return t.id === config.candlestick_data_max; })[0] || {}).values,
startValues = (targets.filter(function (t) { return t.id === config.candlestick_data_start; })[0] || {}).values,
endValues = (targets.filter(function (t) { return t.id === config.candlestick_data_end; })[0] || {}).values;
if (!minValues) {
throw new Error('No min values found at ' + config.candlestick_data_min + ' data property!');
}
if (!maxValues) {
throw new Error('No max values found at ' + config.candlestick_data_max + ' data property!');
}
if (!startValues) {
throw new Error('No start values found at ' + config.candlestick_data_start + ' data property!');
}
if (!endValues) {
throw new Error('No end values found at ' + config.candlestick_data_end + ' data property!');
}
var dataNum = Math.max(minValues.length, maxValues.length, startValues.length, endValues.length);
if (minValues.length !== dataNum) {
throw new Error('Not enough min values! Required ' + dataNum + ' values');
}
if (maxValues.length !== dataNum) {
throw new Error('Not enough max values! Required ' + dataNum + ' values');
}
if (startValues.length !== dataNum) {
throw new Error('Not enough start values! Required ' + dataNum + ' values');
}
if (endValues.length !== dataNum) {
throw new Error('Not enough end values! Required ' + dataNum + ' values');
}
// Saving data to internal var
var candleStickValues = minValues.map(function(min, index) {
return {
id: 'cs-data',
index: index,
value: min.value,
csValue: {
min: min.value,
max: maxValues[index].value,
start: startValues[index].value,
end: endValues[index].value
},
x: min.x
};
});
$$.config.data_types['cs-data'] = 'candle-stick';
$$.candleStickValues = candleStickValues;
return $$.candleStickValues;
};
// Updating candle stick chart containers
c3_chart_internal_fn.updateTargetsForCandleStick = function (targets) {
var $$ = this,
mainCandleStickUpdate,
mainCandleStickEnter,
classChartCandleStick = $$.classChartCandleStick.bind($$),
classFocus = $$.classFocus.bind($$),
classCandleStick = $$.classCandleStick.bind($$),
classCandleSticks = $$.classCandleSticks.bind($$);
var candleStickValues = $$.convertTargetsForCandleStick(targets);
mainCandleStickUpdate = $$.main.select('.' + CLASS.chartCandleSticks).selectAll('.' + CLASS.chartCandleStick)
.data(candleStickValues.length > 0 ? [{id: 'cs-data', values: candleStickValues}] : null)
.attr('class', function (d) { return classCandleStick(d) + classFocus(d); });
mainCandleStickEnter = mainCandleStickUpdate.enter().append('g')
.attr('class', classChartCandleStick)
.style('opacity', 0)
.style('pointer-events', 'none');
// Candlestick for data
mainCandleStickEnter.append('g')
.attr('class', classCandleSticks);
};
// Updating one candle stick
c3_chart_internal_fn.updateCandleStick = function (durationForExit) {
var $$ = this,
candleStickData = $$.candleStickData.bind($$),
classCandleStick = $$.classCandleStick.bind($$),
classCandleStickShadowUpper = $$.classCandleStickShadowUpper.bind($$),
classCandleStickShadowLower = $$.classCandleStickShadowLower.bind($$);
$$.mainCandleStick = $$.main.selectAll('.' + CLASS.candleSticks)
.selectAll('.' + CLASS.candleStick)
.data(candleStickData);
$$.mainCandleStick.enter().append('path')
.attr('class', classCandleStick)
.style('fill', function(d) {
return !!d.csValue && d.csValue.start < d.csValue.end ? 'green' : !!d.csValue && d.csValue.start > d.csValue.end ? 'red' : 'gray';
});
$$.mainCandleStick.exit().transition().duration(durationForExit)
.style('opacity', 0)
.remove();
$$.mainCandleStickShadowsUpper = $$.main.selectAll('.' + CLASS.candleSticks)
.selectAll('.' + CLASS.candleStickShadowUpper)
.data(candleStickData);
$$.mainCandleStickShadowsUpper.enter()
.append('path')
.attr('class', classCandleStickShadowUpper);
$$.mainCandleStickShadowsUpper.exit().transition().duration(durationForExit)
.style('opacity', 0)
.remove();
$$.mainCandleStickShadowsLower = $$.main.selectAll('.' + CLASS.candleSticks)
.selectAll('.' + CLASS.candleStickShadowLower)
.data(candleStickData);
$$.mainCandleStickShadowsLower.enter()
.append('path')
.attr('class', classCandleStickShadowLower);
$$.mainCandleStickShadowsLower.exit().transition().duration(durationForExit)
.style('opacity', 0)
.remove();
};
c3_chart_internal_fn.redrawCandleStick = function (drawCandleStick, withTransition) {
var $$ = this;
return [
(withTransition ? this.mainCandleStick.transition(Math.random().toString()) : this.mainCandleStick)
.attr('d', drawCandleStick)
.style('opacity', 1),
(withTransition ? this.mainCandleStickShadowsUpper.transition(Math.random().toString()) : this.mainCandleStickShadowsUpper)
.attr('d', $$.generateDrawCandleStickUpperShadow())
.style('opacity', 1),
(withTransition ? this.mainCandleStickShadowsLower.transition(Math.random().toString()) : this.mainCandleStickShadowsLower)
.attr('d', $$.generateDrawCandleStickLowerShadow())
.style('opacity', 1)
];
};
c3_chart_internal_fn.getCandleStickW = function (axis) {
var $$ = this, config = $$.config,
w = typeof config.candlestick_width === 'number' ? config.candlestick_width : axis.tickInterval() * config.candlestick_width_ratio;
return config.candlestick_width_max && w > config.candlestick_width_max ? config.candlestick_width_max : w;
};
c3_chart_internal_fn.getCandleStick = function (i, id) {
var $$ = this;
return (id ? $$.main.selectAll('.' + CLASS.candleSticks + $$.getTargetSelectorSuffix(id)) : $$.main)
.selectAll('.' + CLASS.candleSticks + (isValue(i) ? '-' + i : ''));
};
c3_chart_internal_fn.expandCandleStick = function (i, id, reset) {
var $$ = this;
if (reset) { $$.unexpandCandleStick(); }
$$.getCandleStick(i, id).classed(CLASS.EXPANDED, true);
};
c3_chart_internal_fn.unexpandCandleStick = function (i) {
var $$ = this;
$$.getCandleStick(i).classed(CLASS.EXPANDED, false);
};
c3_chart_internal_fn.generateDrawCandleStick = function (candleStickIndices) {
var $$ = this,
getPoints = $$.generateGetCandleStickPoints(candleStickIndices);
return function (d, i) {
// 8 points describing candlestick chart
var points = getPoints(d, i);
if (points[0][1] - points[2][1] < 1) {
points[2][1] -= 1;
points[3][1] -= 1;
}
var path =
'M ' + points[0][0] + ',' + points[0][1] + ' ' +
'L' + points[1][0] + ',' + points[1][1] + ' ' +
'L' + points[2][0] + ',' + points[2][1] + ' ' +
'L' + points[3][0] + ',' + points[3][1] + ' ' +
'z';
return path;
};
};
c3_chart_internal_fn.generateDrawCandleStickUpperShadow = function (candleStickIndices) {
var $$ = this,
getPoints = $$.generateGetCandleStickPoints(candleStickIndices);
return function (d, i) {
// 8 points describing candlestick chart
var points = getPoints(d, i);
var path =
'M ' + points[4][0] + ',' + points[4][1] + ' ' +
'L' + points[5][0] + ',' + points[5][1];
return path;
};
};
c3_chart_internal_fn.generateDrawCandleStickLowerShadow = function (candleStickIndices) {
var $$ = this,
getPoints = $$.generateGetCandleStickPoints(candleStickIndices);
return function (d, i) {
// 8 points describing candlestick chart
var points = getPoints(d, i);
var path =
'M ' + points[6][0] + ',' + points[6][1] + ' ' +
'L' + points[7][0] + ',' + points[7][1];
return path;
};
};
c3_chart_internal_fn.generateGetCandleStickPoints = function (candleStickIndices, isSub) {
var $$ = this,
axis = isSub ? $$.subXAxis : $$.xAxis,
candleStickW = $$.getCandleStickW(axis),
candleStickX = $$.getShapeX(candleStickW, 1, {data: 0}, false),
candleStickY = $$.getShapeY(false),
candleStickValues = $$.candleStickValues;
return function (d, i) {
var value = candleStickValues[i];
var inc = value.csValue.start < value.csValue.end;
var min = value.csValue.min,
max = value.csValue.max,
end = inc ? value.csValue.end : value.csValue.start,
start = inc ? value.csValue.start : value.csValue.end;
var posX = candleStickX({id: 'cs-data', x: value.x}),
posYMax = candleStickY({id: 'cs-data', x: value.x, value: max}),
posYMin = candleStickY({id: 'cs-data', x: value.x, value: min}),
posYStart = candleStickY({id: 'cs-data', x: value.x, value: start}),
posYEnd = candleStickY({id: 'cs-data', x: value.x, value: end});
// 8 points that make a candle stick
return [
// Body
[posX, posYEnd],
[posX + candleStickW, posYEnd],
[posX + candleStickW, posYStart],
[posX, posYStart],
// Upper shadow
[posX + candleStickW / 2, posYMax],
[posX + candleStickW / 2, posYEnd],
// Lower shadow
[posX + candleStickW / 2, posYStart],
[posX + candleStickW / 2, posYMin]
];
};
};
c3_chart_internal_fn.isWithinCandleStick = function (that) {
var mouse = this.d3.mouse(that),
box = that.getBoundingClientRect(),
seg0 = that.pathSegList.getItem(0), seg1 = that.pathSegList.getItem(1),
x = Math.min(seg0.x, seg1.x), y = Math.min(seg0.y, seg1.y),
w = box.width, h = box.height, offset = 2,
sx = x - offset, ex = x + w + offset, sy = y + h + offset, ey = y - offset;
return sx < mouse[0] && mouse[0] < ex && ey < mouse[1] && mouse[1] < sy;
};
c3_chart_internal_fn.initText = function () {
var $$ = this;
$$.main.select('.' + CLASS.chart).append("g")
@ -3521,6 +3806,9 @@
c3_chart_internal_fn.hasArcType = function (targets) {
return this.hasType('pie', targets) || this.hasType('donut', targets) || this.hasType('gauge', targets);
};
c3_chart_internal_fn.hasCandleStickType = function (targets) {
return this.hasType('candle-stick', targets);
};
c3_chart_internal_fn.isLineType = function (d) {
var config = this.config, id = isString(d) ? d : d.id;
return !config.data_types[id] || ['line', 'spline', 'area', 'area-spline', 'step', 'area-step'].indexOf(config.data_types[id]) >= 0;
@ -3557,6 +3845,10 @@
var id = isString(d) ? d : d.id;
return this.config.data_types[id] === 'donut';
};
c3_chart_internal_fn.isCandleStickType = function (d) {
var id = isString(d) ? d : d.id;
return this.config.data_types[id] === 'candle-stick';
};
c3_chart_internal_fn.isArcType = function (d) {
return this.isPieType(d) || this.isDonutType(d) || this.isGaugeType(d);
};
@ -3574,6 +3866,9 @@
c3_chart_internal_fn.barData = function (d) {
return this.isBarType(d) ? d.values : [];
};
c3_chart_internal_fn.candleStickData = function (d) {
return this.isCandleStickType(d) ? d.values : [];
};
c3_chart_internal_fn.lineOrScatterData = function (d) {
return this.isLineType(d) || this.isScatterType(d) ? d.values : [];
};
@ -5841,6 +6136,8 @@
chartLines: 'c3-chart-lines',
chartBar: 'c3-chart-bar',
chartBars: 'c3-chart-bars',
chartCandleStick: 'c3-chart-candle-stick',
chartCandleSticks: 'c3-chart-candle-sticks',
chartText: 'c3-chart-text',
chartTexts: 'c3-chart-texts',
chartArc: 'c3-chart-arc',
@ -5876,6 +6173,10 @@
circles: 'c3-circles',
arc: 'c3-arc',
arcs: 'c3-arcs',
candleStick: 'c3-candle-stick',
candleStickShadowUpper: 'c3-candle-stick-shadow-upper',
candleStickShadowLower: 'c3-candle-stick-shadow-lower',
candleSticks: 'c3-candle-sticks',
area: 'c3-area',
areas: 'c3-areas',
empty: 'c3-empty',
@ -5950,6 +6251,18 @@
c3_chart_internal_fn.classArcs = function (d) {
return this.classShapes(d.data) + this.generateClass(CLASS.arcs, d.data.id);
};
c3_chart_internal_fn.classCandleStick = function (d) {
return this.classShapes(d) + this.generateClass(CLASS.candleStick, d.id);
};
c3_chart_internal_fn.classCandleStickShadowUpper = function (d) {
return this.classShapes(d) + this.generateClass(CLASS.candleStickShadowUpper, d.id);
};
c3_chart_internal_fn.classCandleStickShadowLower = function (d) {
return this.classShapes(d) + this.generateClass(CLASS.candleStickShadowLower, d.id);
};
c3_chart_internal_fn.classCandleSticks = function (d) {
return this.classShapes(d) + this.generateClass(CLASS.candleSticks, d.id);
};
c3_chart_internal_fn.classArea = function (d) {
return this.classShape(d) + this.generateClass(CLASS.area, d.id);
};
@ -5991,6 +6304,9 @@
c3_chart_internal_fn.classChartArc = function (d) {
return CLASS.chartArc + this.classTarget(d.data.id);
};
c3_chart_internal_fn.classChartCandleStick = function (d) {
return CLASS.chartCandleStick + this.classTarget(d.id);
};
c3_chart_internal_fn.getTargetSelectorSuffix = function (targetId) {
return targetId || targetId === 0 ? ('-' + targetId).replace(/[\s?!@#$%^&*()_=+,.<>'":;\[\]\/|~`{}\\]/g, '-') : '';
};

10
c3.min.js vendored

File diff suppressed because one or more lines are too long

17
htdocs/samples/chart_bar.html

@ -1,18 +1,20 @@
<html>
<head>
<link href="/css/c3.css" rel="stylesheet" type="text/css">
<link href="/c3.css" rel="stylesheet" type="text/css">
</head>
<body>
<div id="chart"></div>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script src="/js/c3.js"></script>
<script src="http://d3js.org/d3.v3.js" charset="utf-8"></script>
<script src="/c3.js"></script>
<script>
var chart = c3.generate({
data: {
x: 'date',
xFormat : '%Y%m%d',
columns: [
['data1', 1030, 1200, 1100, 1400, 1150, 1250],
['data2', 2130, 2100, 2140, 2200, 2150, 1850]
['date', '20130101', '20130102', '20130103', '20130104', '20130105', '20130106', '20130107', '20130108'],
['max', 110, 80, 110, 110, 55, 55, 110, 80],
// ['data1', 30, 200, 100, 400, 150, 250],
// ['data2', 130, 100, 140, 200, 150, 50]
],
@ -23,7 +25,10 @@
},
axis: {
x: {
type: 'categorized'
type: 'timeseries',
tick: {
format: '%Y-%m-%d'
}
}
},
bar: {

44
htdocs/samples/chart_candlestick.html

@ -0,0 +1,44 @@
<html>
<head>
<link rel="stylesheet" type="text/css" href="c3.css">
</head>
<body>
<div id="chart"></div>
<script src="http://d3js.org/d3.v3.js" charset="utf-8"></script>
<script src="c3.js"></script>
<script>
var chart = c3.generate({
bindto: '#chart',
data: {
x: 'date',
xFormat : '%Y%m%d',
columns: [
['date', '20130101', '20130102', '20130103', '20130104', '20130105', '20130106', '20130107', '20130108', '20130109'],
['min', 10, 10, 30, 10, 55, 10, 55, 30, 10,],
['start', 30, 30, 30, 55, 55, 55, 55, 30, 80],
['end', 80, 80, 80, 55, 55, 55, 55, 80, 30],
['max', 110, 80, 110, 110, 55, 55, 110, 80, 110]
],
type: 'candle-stick'
},
candlestick: {
data: {
min: 'min',
max: 'max',
start: 'start',
end: 'end'
}
},
axis: {
x: {
type: 'timeseries',
tick: {
format: '%Y-%m-%d'
}
}
}
});
</script>
</body>
</html>

31
htdocs/samples/simple.html

@ -9,14 +9,35 @@
<script src="/js/c3.js"></script>
<script>
var chart = c3.generate({
bindto: '#chart',
data: {
x: 'date',
xFormat : '%Y%m%d',
columns: [
['data1', 30, 200, 100, 400, 150, 250],
['data2', 50, 20, 10, 40, 15, 25]
['date', '20130101', '20130102', '20130103', '20130104', '20130105', '20130106'],
['min', 10, 20, 25],
['max', 80, 50, 40],
['start', 30, 10, 25],
['end', 100, 90, 80]
],
onclick: function (d, element) { console.log("onclick", d, element); },
onmouseover: function (d) { console.log("onmouseover", d); },
onmouseout: function (d) { console.log("onmouseout", d); },
type: 'candle-stick'
},
bar: {
zerobased: false
},
candlestick: {
min: 'min',
max: 'max',
start: 'start',
end: 'end'
},
axis: {
x: {
type: 'timeseries',
tick: {
format: '%Y-%m-%d'
}
}
}
});
</script>

21
src/class.js

@ -5,6 +5,8 @@ var CLASS = c3_chart_internal_fn.CLASS = {
chartLines: 'c3-chart-lines',
chartBar: 'c3-chart-bar',
chartBars: 'c3-chart-bars',
chartCandleStick: 'c3-chart-candle-stick',
chartCandleSticks: 'c3-chart-candle-sticks',
chartText: 'c3-chart-text',
chartTexts: 'c3-chart-texts',
chartArc: 'c3-chart-arc',
@ -40,6 +42,10 @@ var CLASS = c3_chart_internal_fn.CLASS = {
circles: 'c3-circles',
arc: 'c3-arc',
arcs: 'c3-arcs',
candleStick: 'c3-candle-stick',
candleStickShadowUpper: 'c3-candle-stick-shadow-upper',
candleStickShadowLower: 'c3-candle-stick-shadow-lower',
candleSticks: 'c3-candle-sticks',
area: 'c3-area',
areas: 'c3-areas',
empty: 'c3-empty',
@ -114,6 +120,18 @@ c3_chart_internal_fn.classArc = function (d) {
c3_chart_internal_fn.classArcs = function (d) {
return this.classShapes(d.data) + this.generateClass(CLASS.arcs, d.data.id);
};
c3_chart_internal_fn.classCandleStick = function (d) {
return this.classShapes(d) + this.generateClass(CLASS.candleStick, d.id);
};
c3_chart_internal_fn.classCandleStickShadowUpper = function (d) {
return this.classShapes(d) + this.generateClass(CLASS.candleStickShadowUpper, d.id);
};
c3_chart_internal_fn.classCandleStickShadowLower = function (d) {
return this.classShapes(d) + this.generateClass(CLASS.candleStickShadowLower, d.id);
};
c3_chart_internal_fn.classCandleSticks = function (d) {
return this.classShapes(d) + this.generateClass(CLASS.candleSticks, d.id);
};
c3_chart_internal_fn.classArea = function (d) {
return this.classShape(d) + this.generateClass(CLASS.area, d.id);
};
@ -155,6 +173,9 @@ c3_chart_internal_fn.classChartBar = function (d) {
c3_chart_internal_fn.classChartArc = function (d) {
return CLASS.chartArc + this.classTarget(d.data.id);
};
c3_chart_internal_fn.classChartCandleStick = function (d) {
return CLASS.chartCandleStick + this.classTarget(d.id);
};
c3_chart_internal_fn.getTargetSelectorSuffix = function (targetId) {
return targetId || targetId === 0 ? ('-' + targetId).replace(/[\s?!@#$%^&*()_=+,.<>'":;\[\]\/|~`{}\\]/g, '-') : '';
};

10
src/config.js

@ -120,7 +120,7 @@ c3_chart_internal_fn.getDefaultConfig = function () {
axis_y_label: {},
axis_y_tick_format: undefined,
axis_y_tick_outer: true,
axis_y_tick_values: null,
axis_y_tick_values: null,
axis_y_tick_rotate: 0,
axis_y_tick_count: undefined,
axis_y_tick_time_value: undefined,
@ -166,6 +166,14 @@ c3_chart_internal_fn.getDefaultConfig = function () {
bar_width_ratio: 0.6,
bar_width_max: undefined,
bar_zerobased: true,
// candlestick
candlestick_width: undefined,
candlestick_width_ratio: 0.6,
candlestick_width_max: undefined,
candlestick_data_min: 'min',
candlestick_data_max: 'max',
candlestick_data_start: 'start',
candlestick_data_end: 'end',
// area
area_zerobased: true,
// pie

18
src/core.js

@ -176,6 +176,7 @@ c3_chart_internal_fn.initChartElements = function () {
if (this.initArc) { this.initArc(); }
if (this.initGauge) { this.initGauge(); }
if (this.initText) { this.initText(); }
if (this.initCandleStick) { this.initCandleStick(); }
};
c3_chart_internal_fn.initWithData = function (data) {
@ -446,6 +447,9 @@ c3_chart_internal_fn.updateTargets = function (targets) {
//-- Arc --//
if ($$.hasArcType() && $$.updateTargetsForArc) { $$.updateTargetsForArc(targets); }
//-- Candle stick --//
if ($$.hasCandleStickType() && $$.updateTargetsForArc) { $$.updateTargetsForCandleStick(targets); }
/*-- Sub --*/
if ($$.updateTargetsForSubchart) { $$.updateTargetsForSubchart(targets); }
@ -462,12 +466,15 @@ c3_chart_internal_fn.showTargets = function () {
c3_chart_internal_fn.redraw = function (options, transitions) {
var $$ = this, main = $$.main, d3 = $$.d3, config = $$.config;
var areaIndices = $$.getShapeIndices($$.isAreaType), barIndices = $$.getShapeIndices($$.isBarType), lineIndices = $$.getShapeIndices($$.isLineType);
var areaIndices = $$.getShapeIndices($$.isAreaType),
barIndices = $$.getShapeIndices($$.isBarType),
lineIndices = $$.getShapeIndices($$.isLineType),
candleStickIndicies = $$.getShapeIndices($$.isCandleStickType);
var withY, withSubchart, withTransition, withTransitionForExit, withTransitionForAxis,
withTransform, withUpdateXDomain, withUpdateOrgXDomain, withTrimXDomain, withLegend,
withEventRect, withDimension, withUpdateXAxis;
var hideAxis = $$.hasArcType();
var drawArea, drawBar, drawLine, xForText, yForText;
var drawArea, drawBar, drawCandleStick, drawLine, xForText, yForText;
var duration, durationForExit, durationForAxis;
var waitForDraw, flow;
var targetsToShow = $$.filterTargetsToShow($$.data.targets), tickValues, i, intervalForCulling, xDomainForZoom;
@ -561,6 +568,7 @@ c3_chart_internal_fn.redraw = function (options, transitions) {
// setup drawer - MEMO: these must be called after axis updated
drawArea = $$.generateDrawArea ? $$.generateDrawArea(areaIndices, false) : undefined;
drawBar = $$.generateDrawBar ? $$.generateDrawBar(barIndices) : undefined;
drawCandleStick = $$.generateDrawCandleStick ? $$.generateDrawCandleStick(candleStickIndicies) : undefined;
drawLine = $$.generateDrawLine ? $$.generateDrawLine(lineIndices, false) : undefined;
xForText = $$.generateXYForText(areaIndices, barIndices, lineIndices, true);
yForText = $$.generateXYForText(areaIndices, barIndices, lineIndices, false);
@ -591,6 +599,9 @@ c3_chart_internal_fn.redraw = function (options, transitions) {
// bars
$$.updateBar(durationForExit);
// candlestick
$$.updateCandleStick(durationForExit);
// lines, areas and cricles
$$.updateLine(durationForExit);
$$.updateArea(durationForExit);
@ -637,6 +648,7 @@ c3_chart_internal_fn.redraw = function (options, transitions) {
flow: options.flow,
duration: options.flow.duration,
drawBar: drawBar,
drawCandleStick: drawCandleStick,
drawLine: drawLine,
drawArea: drawArea,
cx: cx,
@ -657,6 +669,7 @@ c3_chart_internal_fn.redraw = function (options, transitions) {
$$.redrawBar(drawBar, true),
$$.redrawLine(drawLine, true),
$$.redrawArea(drawArea, true),
$$.redrawCandleStick(drawCandleStick, true),
$$.redrawCircle(cx, cy, true),
$$.redrawText(xForText, yForText, options.flow, true),
$$.redrawRegion(true),
@ -686,6 +699,7 @@ c3_chart_internal_fn.redraw = function (options, transitions) {
$$.redrawBar(drawBar);
$$.redrawLine(drawLine);
$$.redrawArea(drawArea);
$$.redrawCandleStick(drawCandleStick),
$$.redrawCircle(cx, cy);
$$.redrawText(xForText, yForText, options.flow);
$$.redrawRegion();

2
src/domain.js

@ -160,7 +160,7 @@ c3_chart_internal_fn.getXDomainPadding = function (domain) {
maxDataCount, padding, paddingLeft, paddingRight;
if ($$.isCategorized()) {
padding = 0;
} else if ($$.hasType('bar')) {
} else if ($$.hasType('bar') || $$.hasType('candle-stick')) {
maxDataCount = $$.getMaxDataCount();
padding = maxDataCount > 1 ? (diff / (maxDataCount - 1)) / 2 : 0.5;
} else {

262
src/shape.candlestick.js

@ -0,0 +1,262 @@
// Candle stick chart initialization
c3_chart_internal_fn.initCandleStick = function() {
var $$ = this;
$$.main.select('.' + CLASS.chart).append("g")
.attr('class', CLASS.chartCandleSticks);
};
// Convert candlestick data
c3_chart_internal_fn.convertTargetsForCandleStick = function (targets) {
var $$ = this,
config = $$.config,
minValues = (targets.filter(function (t) { return t.id === config.candlestick_data_min; })[0] || {}).values,
maxValues = (targets.filter(function (t) { return t.id === config.candlestick_data_max; })[0] || {}).values,
startValues = (targets.filter(function (t) { return t.id === config.candlestick_data_start; })[0] || {}).values,
endValues = (targets.filter(function (t) { return t.id === config.candlestick_data_end; })[0] || {}).values;
if (!minValues) {
throw new Error('No min values found at ' + config.candlestick_data_min + ' data property!');
}
if (!maxValues) {
throw new Error('No max values found at ' + config.candlestick_data_max + ' data property!');
}
if (!startValues) {
throw new Error('No start values found at ' + config.candlestick_data_start + ' data property!');
}
if (!endValues) {
throw new Error('No end values found at ' + config.candlestick_data_end + ' data property!');
}
var dataNum = Math.max(minValues.length, maxValues.length, startValues.length, endValues.length);
if (minValues.length !== dataNum) {
throw new Error('Not enough min values! Required ' + dataNum + ' values');
}
if (maxValues.length !== dataNum) {
throw new Error('Not enough max values! Required ' + dataNum + ' values');
}
if (startValues.length !== dataNum) {
throw new Error('Not enough start values! Required ' + dataNum + ' values');
}
if (endValues.length !== dataNum) {
throw new Error('Not enough end values! Required ' + dataNum + ' values');
}
// Saving data to internal var
var candleStickValues = minValues.map(function(min, index) {
return {
id: 'cs-data',
index: index,
value: min.value,
csValue: {
min: min.value,
max: maxValues[index].value,
start: startValues[index].value,
end: endValues[index].value
},
x: min.x
};
});
$$.config.data_types['cs-data'] = 'candle-stick';
$$.candleStickValues = candleStickValues;
return $$.candleStickValues;
};
// Updating candle stick chart containers
c3_chart_internal_fn.updateTargetsForCandleStick = function (targets) {
var $$ = this,
mainCandleStickUpdate,
mainCandleStickEnter,
classChartCandleStick = $$.classChartCandleStick.bind($$),
classFocus = $$.classFocus.bind($$),
classCandleStick = $$.classCandleStick.bind($$),
classCandleSticks = $$.classCandleSticks.bind($$);
var candleStickValues = $$.convertTargetsForCandleStick(targets);
mainCandleStickUpdate = $$.main.select('.' + CLASS.chartCandleSticks).selectAll('.' + CLASS.chartCandleStick)
.data(candleStickValues.length > 0 ? [{id: 'cs-data', values: candleStickValues}] : null)
.attr('class', function (d) { return classCandleStick(d) + classFocus(d); });
mainCandleStickEnter = mainCandleStickUpdate.enter().append('g')
.attr('class', classChartCandleStick)
.style('opacity', 0)
.style('pointer-events', 'none');
// Candlestick for data
mainCandleStickEnter.append('g')
.attr('class', classCandleSticks);
};
// Updating one candle stick
c3_chart_internal_fn.updateCandleStick = function (durationForExit) {
var $$ = this,
candleStickData = $$.candleStickData.bind($$),
classCandleStick = $$.classCandleStick.bind($$),
classCandleStickShadowUpper = $$.classCandleStickShadowUpper.bind($$),
classCandleStickShadowLower = $$.classCandleStickShadowLower.bind($$);
$$.mainCandleStick = $$.main.selectAll('.' + CLASS.candleSticks)
.selectAll('.' + CLASS.candleStick)
.data(candleStickData);
$$.mainCandleStick.enter().append('path')
.attr('class', classCandleStick)
.style('fill', function(d) {
return !!d.csValue && d.csValue.start < d.csValue.end ? 'green' : !!d.csValue && d.csValue.start > d.csValue.end ? 'red' : 'gray';
});
$$.mainCandleStick.exit().transition().duration(durationForExit)
.style('opacity', 0)
.remove();
$$.mainCandleStickShadowsUpper = $$.main.selectAll('.' + CLASS.candleSticks)
.selectAll('.' + CLASS.candleStickShadowUpper)
.data(candleStickData);
$$.mainCandleStickShadowsUpper.enter()
.append('path')
.attr('class', classCandleStickShadowUpper);
$$.mainCandleStickShadowsUpper.exit().transition().duration(durationForExit)
.style('opacity', 0)
.remove();
$$.mainCandleStickShadowsLower = $$.main.selectAll('.' + CLASS.candleSticks)
.selectAll('.' + CLASS.candleStickShadowLower)
.data(candleStickData);
$$.mainCandleStickShadowsLower.enter()
.append('path')
.attr('class', classCandleStickShadowLower);
$$.mainCandleStickShadowsLower.exit().transition().duration(durationForExit)
.style('opacity', 0)
.remove();
};
c3_chart_internal_fn.redrawCandleStick = function (drawCandleStick, withTransition) {
var $$ = this;
return [
(withTransition ? this.mainCandleStick.transition(Math.random().toString()) : this.mainCandleStick)
.attr('d', drawCandleStick)
.style('opacity', 1),
(withTransition ? this.mainCandleStickShadowsUpper.transition(Math.random().toString()) : this.mainCandleStickShadowsUpper)
.attr('d', $$.generateDrawCandleStickUpperShadow())
.style('opacity', 1),
(withTransition ? this.mainCandleStickShadowsLower.transition(Math.random().toString()) : this.mainCandleStickShadowsLower)
.attr('d', $$.generateDrawCandleStickLowerShadow())
.style('opacity', 1)
];
};
c3_chart_internal_fn.getCandleStickW = function (axis) {
var $$ = this, config = $$.config,
w = typeof config.candlestick_width === 'number' ? config.candlestick_width : axis.tickInterval() * config.candlestick_width_ratio;
return config.candlestick_width_max && w > config.candlestick_width_max ? config.candlestick_width_max : w;
};
c3_chart_internal_fn.getCandleStick = function (i, id) {
var $$ = this;
return (id ? $$.main.selectAll('.' + CLASS.candleSticks + $$.getTargetSelectorSuffix(id)) : $$.main)
.selectAll('.' + CLASS.candleSticks + (isValue(i) ? '-' + i : ''));
};
c3_chart_internal_fn.expandCandleStick = function (i, id, reset) {
var $$ = this;
if (reset) { $$.unexpandCandleStick(); }
$$.getCandleStick(i, id).classed(CLASS.EXPANDED, true);
};
c3_chart_internal_fn.unexpandCandleStick = function (i) {
var $$ = this;
$$.getCandleStick(i).classed(CLASS.EXPANDED, false);
};
c3_chart_internal_fn.generateDrawCandleStick = function (candleStickIndices) {
var $$ = this,
getPoints = $$.generateGetCandleStickPoints(candleStickIndices);
return function (d, i) {
// 8 points describing candlestick chart
var points = getPoints(d, i);
if (points[0][1] - points[2][1] < 1) {
points[2][1] -= 1;
points[3][1] -= 1;
}
var path =
'M ' + points[0][0] + ',' + points[0][1] + ' ' +
'L' + points[1][0] + ',' + points[1][1] + ' ' +
'L' + points[2][0] + ',' + points[2][1] + ' ' +
'L' + points[3][0] + ',' + points[3][1] + ' ' +
'z';
return path;
};
};
c3_chart_internal_fn.generateDrawCandleStickUpperShadow = function (candleStickIndices) {
var $$ = this,
getPoints = $$.generateGetCandleStickPoints(candleStickIndices);
return function (d, i) {
// 8 points describing candlestick chart
var points = getPoints(d, i);
var path =
'M ' + points[4][0] + ',' + points[4][1] + ' ' +
'L' + points[5][0] + ',' + points[5][1];
return path;
};
};
c3_chart_internal_fn.generateDrawCandleStickLowerShadow = function (candleStickIndices) {
var $$ = this,
getPoints = $$.generateGetCandleStickPoints(candleStickIndices);
return function (d, i) {
// 8 points describing candlestick chart
var points = getPoints(d, i);
var path =
'M ' + points[6][0] + ',' + points[6][1] + ' ' +
'L' + points[7][0] + ',' + points[7][1];
return path;
};
};
c3_chart_internal_fn.generateGetCandleStickPoints = function (candleStickIndices, isSub) {
var $$ = this,
axis = isSub ? $$.subXAxis : $$.xAxis,
candleStickW = $$.getCandleStickW(axis),
candleStickX = $$.getShapeX(candleStickW, 1, {data: 0}, false),
candleStickY = $$.getShapeY(false),
candleStickValues = $$.candleStickValues;
return function (d, i) {
var value = candleStickValues[i];
var inc = value.csValue.start < value.csValue.end;
var min = value.csValue.min,
max = value.csValue.max,
end = inc ? value.csValue.end : value.csValue.start,
start = inc ? value.csValue.start : value.csValue.end;
var posX = candleStickX({id: 'cs-data', x: value.x}),
posYMax = candleStickY({id: 'cs-data', x: value.x, value: max}),
posYMin = candleStickY({id: 'cs-data', x: value.x, value: min}),
posYStart = candleStickY({id: 'cs-data', x: value.x, value: start}),
posYEnd = candleStickY({id: 'cs-data', x: value.x, value: end});
// 8 points that make a candle stick
return [
// Body
[posX, posYEnd],
[posX + candleStickW, posYEnd],
[posX + candleStickW, posYStart],
[posX, posYStart],
// Upper shadow
[posX + candleStickW / 2, posYMax],
[posX + candleStickW / 2, posYEnd],
// Lower shadow
[posX + candleStickW / 2, posYStart],
[posX + candleStickW / 2, posYMin]
];
};
};
c3_chart_internal_fn.isWithinCandleStick = function (that) {
var mouse = this.d3.mouse(that),
box = that.getBoundingClientRect(),
seg0 = that.pathSegList.getItem(0), seg1 = that.pathSegList.getItem(1),
x = Math.min(seg0.x, seg1.x), y = Math.min(seg0.y, seg1.y),
w = box.width, h = box.height, offset = 2,
sx = x - offset, ex = x + w + offset, sy = y + h + offset, ey = y - offset;
return sx < mouse[0] && mouse[0] < ex && ey < mouse[1] && mouse[1] < sy;
};

10
src/type.js

@ -30,6 +30,9 @@ c3_chart_internal_fn.hasType = function (type, targets) {
c3_chart_internal_fn.hasArcType = function (targets) {
return this.hasType('pie', targets) || this.hasType('donut', targets) || this.hasType('gauge', targets);
};
c3_chart_internal_fn.hasCandleStickType = function (targets) {
return this.hasType('candle-stick', targets);
};
c3_chart_internal_fn.isLineType = function (d) {
var config = this.config, id = isString(d) ? d : d.id;
return !config.data_types[id] || ['line', 'spline', 'area', 'area-spline', 'step', 'area-step'].indexOf(config.data_types[id]) >= 0;
@ -66,6 +69,10 @@ c3_chart_internal_fn.isDonutType = function (d) {
var id = isString(d) ? d : d.id;
return this.config.data_types[id] === 'donut';
};
c3_chart_internal_fn.isCandleStickType = function (d) {
var id = isString(d) ? d : d.id;
return this.config.data_types[id] === 'candle-stick';
};
c3_chart_internal_fn.isArcType = function (d) {
return this.isPieType(d) || this.isDonutType(d) || this.isGaugeType(d);
};
@ -83,6 +90,9 @@ c3_chart_internal_fn.arcData = function (d) {
c3_chart_internal_fn.barData = function (d) {
return this.isBarType(d) ? d.values : [];
};
c3_chart_internal_fn.candleStickData = function (d) {
return this.isCandleStickType(d) ? d.values : [];
};
c3_chart_internal_fn.lineOrScatterData = function (d) {
return this.isLineType(d) || this.isScatterType(d) ? d.values : [];
};

Loading…
Cancel
Save