mirror of https://github.com/masayuki0812/c3.git
Quite good looking graph derived from d3.js
http://c3js.org
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
302 lines
11 KiB
302 lines
11 KiB
import CLASS from './class'; |
|
import { c3_chart_fn, c3_chart_internal_fn } from './core'; |
|
import { isValue, isDefined, diffDomain } from './util'; |
|
|
|
c3_chart_fn.flow = function (args) { |
|
var $$ = this.internal, |
|
targets, data, notfoundIds = [], orgDataCount = $$.getMaxDataCount(), |
|
dataCount, domain, baseTarget, baseValue, length = 0, tail = 0, diff, to; |
|
|
|
if (args.json) { |
|
data = $$.convertJsonToData(args.json, args.keys); |
|
} |
|
else if (args.rows) { |
|
data = $$.convertRowsToData(args.rows); |
|
} |
|
else if (args.columns) { |
|
data = $$.convertColumnsToData(args.columns); |
|
} |
|
else { |
|
return; |
|
} |
|
targets = $$.convertDataToTargets(data, true); |
|
|
|
// Update/Add data |
|
$$.data.targets.forEach(function (t) { |
|
var found = false, i, j; |
|
for (i = 0; i < targets.length; i++) { |
|
if (t.id === targets[i].id) { |
|
found = true; |
|
|
|
if (t.values[t.values.length - 1]) { |
|
tail = t.values[t.values.length - 1].index + 1; |
|
} |
|
length = targets[i].values.length; |
|
|
|
for (j = 0; j < length; j++) { |
|
targets[i].values[j].index = tail + j; |
|
if (!$$.isTimeSeries()) { |
|
targets[i].values[j].x = tail + j; |
|
} |
|
} |
|
t.values = t.values.concat(targets[i].values); |
|
|
|
targets.splice(i, 1); |
|
break; |
|
} |
|
} |
|
if (!found) { notfoundIds.push(t.id); } |
|
}); |
|
|
|
// Append null for not found targets |
|
$$.data.targets.forEach(function (t) { |
|
var i, j; |
|
for (i = 0; i < notfoundIds.length; i++) { |
|
if (t.id === notfoundIds[i]) { |
|
tail = t.values[t.values.length - 1].index + 1; |
|
for (j = 0; j < length; j++) { |
|
t.values.push({ |
|
id: t.id, |
|
index: tail + j, |
|
x: $$.isTimeSeries() ? $$.getOtherTargetX(tail + j) : tail + j, |
|
value: null |
|
}); |
|
} |
|
} |
|
} |
|
}); |
|
|
|
// Generate null values for new target |
|
if ($$.data.targets.length) { |
|
targets.forEach(function (t) { |
|
var i, missing = []; |
|
for (i = $$.data.targets[0].values[0].index; i < tail; i++) { |
|
missing.push({ |
|
id: t.id, |
|
index: i, |
|
x: $$.isTimeSeries() ? $$.getOtherTargetX(i) : i, |
|
value: null |
|
}); |
|
} |
|
t.values.forEach(function (v) { |
|
v.index += tail; |
|
if (!$$.isTimeSeries()) { |
|
v.x += tail; |
|
} |
|
}); |
|
t.values = missing.concat(t.values); |
|
}); |
|
} |
|
$$.data.targets = $$.data.targets.concat(targets); // add remained |
|
|
|
// check data count because behavior needs to change when it's only one |
|
dataCount = $$.getMaxDataCount(); |
|
baseTarget = $$.data.targets[0]; |
|
baseValue = baseTarget.values[0]; |
|
|
|
// Update length to flow if needed |
|
if (isDefined(args.to)) { |
|
length = 0; |
|
to = $$.isTimeSeries() ? $$.parseDate(args.to) : args.to; |
|
baseTarget.values.forEach(function (v) { |
|
if (v.x < to) { length++; } |
|
}); |
|
} else if (isDefined(args.length)) { |
|
length = args.length; |
|
} |
|
|
|
// If only one data, update the domain to flow from left edge of the chart |
|
if (!orgDataCount) { |
|
if ($$.isTimeSeries()) { |
|
if (baseTarget.values.length > 1) { |
|
diff = baseTarget.values[baseTarget.values.length - 1].x - baseValue.x; |
|
} else { |
|
diff = baseValue.x - $$.getXDomain($$.data.targets)[0]; |
|
} |
|
} else { |
|
diff = 1; |
|
} |
|
domain = [baseValue.x - diff, baseValue.x]; |
|
$$.updateXDomain(null, true, true, false, domain); |
|
} else if (orgDataCount === 1) { |
|
if ($$.isTimeSeries()) { |
|
diff = (baseTarget.values[baseTarget.values.length - 1].x - baseValue.x) / 2; |
|
domain = [new Date(+baseValue.x - diff), new Date(+baseValue.x + diff)]; |
|
$$.updateXDomain(null, true, true, false, domain); |
|
} |
|
} |
|
|
|
// Set targets |
|
$$.updateTargets($$.data.targets); |
|
|
|
// Redraw with new targets |
|
$$.redraw({ |
|
flow: { |
|
index: baseValue.index, |
|
length: length, |
|
duration: isValue(args.duration) ? args.duration : $$.config.transition_duration, |
|
done: args.done, |
|
orgDataCount: orgDataCount, |
|
}, |
|
withLegend: true, |
|
withTransition: orgDataCount > 1, |
|
withTrimXDomain: false, |
|
withUpdateXAxis: true, |
|
}); |
|
}; |
|
|
|
c3_chart_internal_fn.generateFlow = function (args) { |
|
var $$ = this, config = $$.config, d3 = $$.d3; |
|
|
|
return function () { |
|
var targets = args.targets, |
|
flow = args.flow, |
|
drawBar = args.drawBar, |
|
drawLine = args.drawLine, |
|
drawArea = args.drawArea, |
|
cx = args.cx, |
|
cy = args.cy, |
|
xv = args.xv, |
|
xForText = args.xForText, |
|
yForText = args.yForText, |
|
duration = args.duration; |
|
|
|
var translateX, scaleX = 1, transform, |
|
flowIndex = flow.index, |
|
flowLength = flow.length, |
|
flowStart = $$.getValueOnIndex($$.data.targets[0].values, flowIndex), |
|
flowEnd = $$.getValueOnIndex($$.data.targets[0].values, flowIndex + flowLength), |
|
orgDomain = $$.x.domain(), domain, |
|
durationForFlow = flow.duration || duration, |
|
done = flow.done || function () {}, |
|
wait = $$.generateWait(); |
|
|
|
var xgrid, |
|
xgridLines, |
|
mainRegion, |
|
mainText, |
|
mainBar, |
|
mainLine, |
|
mainArea, |
|
mainCircle; |
|
|
|
// set flag |
|
$$.flowing = true; |
|
|
|
// remove head data after rendered |
|
$$.data.targets.forEach(function (d) { |
|
d.values.splice(0, flowLength); |
|
}); |
|
|
|
// update x domain to generate axis elements for flow |
|
domain = $$.updateXDomain(targets, true, true); |
|
// update elements related to x scale |
|
if ($$.updateXGrid) { $$.updateXGrid(true); } |
|
|
|
xgrid = $$.xgrid || d3.selectAll([]); // xgrid needs to be obtained after updateXGrid |
|
xgridLines = $$.xgridLines || d3.selectAll([]); |
|
mainRegion = $$.mainRegion || d3.selectAll([]); |
|
mainText = $$.mainText || d3.selectAll([]); |
|
mainBar = $$.mainBar || d3.selectAll([]); |
|
mainLine = $$.mainLine || d3.selectAll([]); |
|
mainArea = $$.mainArea || d3.selectAll([]); |
|
mainCircle = $$.mainCircle || d3.selectAll([]); |
|
|
|
// generate transform to flow |
|
if (!flow.orgDataCount) { // if empty |
|
if ($$.data.targets[0].values.length !== 1) { |
|
translateX = $$.x(orgDomain[0]) - $$.x(domain[0]); |
|
} else { |
|
if ($$.isTimeSeries()) { |
|
flowStart = $$.getValueOnIndex($$.data.targets[0].values, 0); |
|
flowEnd = $$.getValueOnIndex($$.data.targets[0].values, $$.data.targets[0].values.length - 1); |
|
translateX = $$.x(flowStart.x) - $$.x(flowEnd.x); |
|
} else { |
|
translateX = diffDomain(domain) / 2; |
|
} |
|
} |
|
} else if (flow.orgDataCount === 1 || (flowStart && flowStart.x) === (flowEnd && flowEnd.x)) { |
|
translateX = $$.x(orgDomain[0]) - $$.x(domain[0]); |
|
} else { |
|
if ($$.isTimeSeries()) { |
|
translateX = ($$.x(orgDomain[0]) - $$.x(domain[0])); |
|
} else { |
|
translateX = ($$.x(flowStart.x) - $$.x(flowEnd.x)); |
|
} |
|
} |
|
scaleX = (diffDomain(orgDomain) / diffDomain(domain)); |
|
transform = 'translate(' + translateX + ',0) scale(' + scaleX + ',1)'; |
|
|
|
$$.hideXGridFocus(); |
|
|
|
var flowTransition = d3.transition().ease(d3.easeLinear).duration(durationForFlow); |
|
wait.add($$.xAxis($$.axes.x, flowTransition)); |
|
wait.add(mainBar.transition(flowTransition).attr('transform', transform)); |
|
wait.add(mainLine.transition(flowTransition).attr('transform', transform)); |
|
wait.add(mainArea.transition(flowTransition).attr('transform', transform)); |
|
wait.add(mainCircle.transition(flowTransition).attr('transform', transform)); |
|
wait.add(mainText.transition(flowTransition).attr('transform', transform)); |
|
wait.add(mainRegion.filter($$.isRegionOnX).transition(flowTransition).attr('transform', transform)); |
|
wait.add(xgrid.transition(flowTransition).attr('transform', transform)); |
|
wait.add(xgridLines.transition(flowTransition).attr('transform', transform)); |
|
wait(function () { |
|
var i, shapes = [], texts = []; |
|
|
|
// remove flowed elements |
|
if (flowLength) { |
|
for (i = 0; i < flowLength; i++) { |
|
shapes.push('.' + CLASS.shape + '-' + (flowIndex + i)); |
|
texts.push('.' + CLASS.text + '-' + (flowIndex + i)); |
|
} |
|
$$.svg.selectAll('.' + CLASS.shapes).selectAll(shapes).remove(); |
|
$$.svg.selectAll('.' + CLASS.texts).selectAll(texts).remove(); |
|
$$.svg.select('.' + CLASS.xgrid).remove(); |
|
} |
|
|
|
// draw again for removing flowed elements and reverting attr |
|
xgrid |
|
.attr('transform', null) |
|
.attr('x1', $$.xgridAttr.x1) |
|
.attr('x2', $$.xgridAttr.x2) |
|
.attr('y1', $$.xgridAttr.y1) |
|
.attr('y2', $$.xgridAttr.y2) |
|
.style("opacity", $$.xgridAttr.opacity); |
|
xgridLines |
|
.attr('transform', null); |
|
xgridLines.select('line') |
|
.attr("x1", config.axis_rotated ? 0 : xv) |
|
.attr("x2", config.axis_rotated ? $$.width : xv); |
|
xgridLines.select('text') |
|
.attr("x", config.axis_rotated ? $$.width : 0) |
|
.attr("y", xv); |
|
mainBar |
|
.attr('transform', null) |
|
.attr("d", drawBar); |
|
mainLine |
|
.attr('transform', null) |
|
.attr("d", drawLine); |
|
mainArea |
|
.attr('transform', null) |
|
.attr("d", drawArea); |
|
mainCircle |
|
.attr('transform', null) |
|
.attr("cx", cx) |
|
.attr("cy", cy); |
|
mainText |
|
.attr('transform', null) |
|
.attr('x', xForText) |
|
.attr('y', yForText) |
|
.style('fill-opacity', $$.opacityForText.bind($$)); |
|
mainRegion |
|
.attr('transform', null); |
|
mainRegion.filter($$.isRegionOnX) |
|
.attr("x", $$.regionX.bind($$)) |
|
.attr("width", $$.regionWidth.bind($$)); |
|
|
|
// callback for end of flow |
|
done(); |
|
|
|
$$.flowing = false; |
|
}); |
|
}; |
|
};
|
|
|