in bars stage

This commit is contained in:
Prateeksha Singh 2018-02-26 15:26:34 +05:30
parent 84c8c17d72
commit 4ca6166c8d
12 changed files with 263 additions and 461 deletions

View File

@ -232,25 +232,6 @@ function getStringWidth(string, charWidth) {
return (string+"").length * charWidth; return (string+"").length * charWidth;
} }
function getBarHeightAndYAttr(yTop, zeroLine) {
let height, y;
if (yTop <= zeroLine) {
height = zeroLine - yTop;
y = yTop;
// In case of invisible bars
if(height === 0) {
height = totalHeight * MIN_BAR_PERCENT_HEIGHT;
y -= height;
}
} else {
height = yTop - zeroLine;
y = zeroLine;
}
return [height, y];
}
function equilizeNoOfElements(array1, array2, function equilizeNoOfElements(array1, array2,
extra_count=array2.length - array1.length) { extra_count=array2.length - array1.length) {
@ -284,7 +265,7 @@ function equilizeNoOfElements(array1, array2,
// } // }
const UNIT_ANIM_DUR = 350; const UNIT_ANIM_DUR = 350;
const PATH_ANIM_DUR = 350;
const MARKER_LINE_ANIM_DUR = UNIT_ANIM_DUR; const MARKER_LINE_ANIM_DUR = UNIT_ANIM_DUR;
const REPLACE_ALL_NEW_DUR = 250; const REPLACE_ALL_NEW_DUR = 250;
@ -408,13 +389,7 @@ function makeSVGGroup(parent, className, transform='') {
}); });
} }
function wrapInSVGGroup(elements, className='') {
let g = createSVG('g', {
className: className
});
elements.forEach(e => g.appendChild(e));
return g;
}
function makePath(pathStr, className='', stroke='none', fill='none') { function makePath(pathStr, className='', stroke='none', fill='none') {
return createSVG('path', { return createSVG('path', {
@ -663,79 +638,6 @@ function yRegion(y1, y2, width, label) {
return region; return region;
} }
class AxisChartRenderer {
constructor(state) {
this.refreshState(state);
}
refreshState(state) {
this.totalHeight = state.totalHeight;
this.totalWidth = state.totalWidth;
this.zeroLine = state.zeroLine;
this.unitWidth = state.unitWidth;
this.xAxisMode = state.xAxisMode;
this.yAxisMode = state.yAxisMode;
}
setZeroline(zeroLine) {
this.zeroLine = zeroLine;
}
xMarker() {}
xRegion() {
return createSVG('rect', {
className: `bar mini`, // remove class
style: `fill: rgba(228, 234, 239, 0.49)`,
// 'data-point-index': index,
x: 0,
y: y2,
width: this.totalWidth,
height: y1 - y2
});
return region;
}
animatebar(bar, x, yTop, index, noOfDatasets) {
let start = x - this.avgUnitWidth/4;
let width = (this.avgUnitWidth/2)/noOfDatasets;
let [height, y] = getBarHeightAndYAttr(yTop, this.zeroLine, this.totalHeight);
x = start + (width * index);
return [bar, {width: width, height: height, x: x, y: y}, UNIT_ANIM_DUR, STD_EASING];
// bar.animate({height: args.newHeight, y: yTop}, UNIT_ANIM_DUR, mina.easein);
}
animatedot(dot, x, yTop) {
return [dot, {cx: x, cy: yTop}, UNIT_ANIM_DUR, STD_EASING];
// dot.animate({cy: yTop}, UNIT_ANIM_DUR, mina.easein);
}
animatepath(paths, pathStr) {
let pathComponents = [];
const animPath = [paths[0], {d:"M"+pathStr}, PATH_ANIM_DUR, STD_EASING];
pathComponents.push(animPath);
if(paths[1]) {
let regStartPt = `0,${this.zeroLine}L`;
let regEndPt = `L${this.totalWidth}, ${this.zeroLine}`;
const animRegion = [
paths[1],
{d:"M" + regStartPt + pathStr + regEndPt},
PATH_ANIM_DUR,
STD_EASING
];
pathComponents.push(animRegion);
}
return pathComponents;
}
}
const PRESET_COLOR_MAP = { const PRESET_COLOR_MAP = {
'light-blue': '#7cd6fd', 'light-blue': '#7cd6fd',
'blue': '#5e64ff', 'blue': '#5e64ff',
@ -1047,18 +949,10 @@ class BaseChart {
let valid = this.checkData(data); let valid = this.checkData(data);
if(!valid) return false; if(!valid) return false;
if(!this.config.animate) {
this.data = data; this.data = data;
} else {
[this.data, this.firstUpdateData] =
this.getFirstUpdateData(data);
}
return true; return true;
} }
checkData() {}
getFirstUpdateData() {}
setup() { setup() {
if(this.validate()) { if(this.validate()) {
this._setup(); this._setup();
@ -1145,7 +1039,6 @@ class BaseChart {
update(data=this.data) { update(data=this.data) {
this.prepareData(data); this.prepareData(data);
this.calc(); // builds state this.calc(); // builds state
this.refreshRenderer();
this.render(); this.render();
} }
@ -1155,13 +1048,10 @@ class BaseChart {
calc() {} // builds state calc() {} // builds state
refreshRenderer() {
this.renderer = {};
}
render(animate=true) { render(animate=true) {
this.refreshComponents(); // Can decouple to this.refreshComponents() first to save animation timeout
this.elementsToAnimate = [].concat.apply([], this.components.map(c => c.update(animate))); this.elementsToAnimate = [].concat.apply([],
this.components.map(c => c.update(animate)));
if(this.elementsToAnimate) { if(this.elementsToAnimate) {
runSMILAnimation(this.chartWrapper, this.svg, this.elementsToAnimate); runSMILAnimation(this.chartWrapper, this.svg, this.elementsToAnimate);
} }
@ -1172,8 +1062,6 @@ class BaseChart {
// } // }
} }
refreshComponents() {}
makeChartArea() { makeChartArea() {
this.svg = makeSVGContainer( this.svg = makeSVGContainer(
this.chartWrapper, this.chartWrapper,
@ -1285,18 +1173,15 @@ class ChartComponent$1 {
data, data,
// called on update // called on update
preMake,
makeElements, makeElements,
postMake, postMake,
getData, getData,
animateElements animateElements
}) { }) {
this.parent = parent; this.parent = parent;
this.layerClass = layerClass;
this.layerTransform = layerTransform; this.layerTransform = layerTransform;
this.constants = constants; this.constants = constants;
this.preMake = preMake;
this.makeElements = makeElements; this.makeElements = makeElements;
this.postMake = postMake; this.postMake = postMake;
this.getData = getData; this.getData = getData;
@ -1304,7 +1189,11 @@ class ChartComponent$1 {
this.animateElements = animateElements; this.animateElements = animateElements;
this.store = []; this.store = [];
this.layer = makeSVGGroup(this.parent, this.layerClass, this.layerTransform);
layerClass = typeof(layerClass) === 'function'
? layerClass() : layerClass;
this.layer = makeSVGGroup(this.parent, layerClass, this.layerTransform);
this.data = data; this.data = data;
@ -1312,7 +1201,7 @@ class ChartComponent$1 {
} }
refresh(data) { refresh(data) {
this.data = data; this.data = data || this.getData();
} }
make() { make() {
@ -1332,6 +1221,7 @@ class ChartComponent$1 {
} }
update(animate = true) { update(animate = true) {
this.refresh();
let animateElements = []; let animateElements = [];
if(animate) { if(animate) {
animateElements = this.animateElements(this.data); animateElements = this.animateElements(this.data);
@ -1347,14 +1237,14 @@ class ChartComponent$1 {
let componentConfigs = { let componentConfigs = {
yAxis: { yAxis: {
layerClass: 'y axis', layerClass: 'y axis',
makeElements: function(data) { makeElements(data) {
return data.positions.map((position, i) => return data.positions.map((position, i) =>
yLine(position, data.labels[i], this.constants.width, yLine(position, data.labels[i], this.constants.width,
{mode: this.constants.mode, pos: this.constants.pos}) {mode: this.constants.mode, pos: this.constants.pos})
); );
}, },
animateElements: function(newData) { animateElements(newData) {
let newPos = newData.positions; let newPos = newData.positions;
let newLabels = newData.labels; let newLabels = newData.labels;
let oldPos = this.oldData.positions; let oldPos = this.oldData.positions;
@ -1378,14 +1268,14 @@ let componentConfigs = {
xAxis: { xAxis: {
layerClass: 'x axis', layerClass: 'x axis',
makeElements: function(data) { makeElements(data) {
return data.positions.map((position, i) => return data.positions.map((position, i) =>
xLine(position, data.labels[i], this.constants.height, xLine(position, data.labels[i], this.constants.height,
{mode: this.constants.mode, pos: this.constants.pos}) {mode: this.constants.mode, pos: this.constants.pos})
); );
}, },
animateElements: function(newData) { animateElements(newData) {
let newPos = newData.positions; let newPos = newData.positions;
let newLabels = newData.labels; let newLabels = newData.labels;
let oldPos = this.oldData.positions; let oldPos = this.oldData.positions;
@ -1409,13 +1299,13 @@ let componentConfigs = {
yMarkers: { yMarkers: {
layerClass: 'y-markers', layerClass: 'y-markers',
makeElements: function(data) { makeElements(data) {
return data.map(marker => return data.map(marker =>
yMarker(marker.position, marker.label, this.constants.width, yMarker(marker.position, marker.label, this.constants.width,
{pos:'right', mode: 'span', lineType: 'dashed'}) {pos:'right', mode: 'span', lineType: 'dashed'})
); );
}, },
animateElements: function(newData) { animateElements(newData) {
[this.oldData, newData] = equilizeNoOfElements(this.oldData, newData); [this.oldData, newData] = equilizeNoOfElements(this.oldData, newData);
let newPos = newData.map(d => d.position); let newPos = newData.map(d => d.position);
@ -1441,13 +1331,13 @@ let componentConfigs = {
yRegions: { yRegions: {
layerClass: 'y-regions', layerClass: 'y-regions',
makeElements: function(data) { makeElements(data) {
return data.map(region => return data.map(region =>
yRegion(region.start, region.end, this.constants.width, yRegion(region.start, region.end, this.constants.width,
region.label) region.label)
); );
}, },
animateElements: function(newData) { animateElements(newData) {
[this.oldData, newData] = equilizeNoOfElements(this.oldData, newData); [this.oldData, newData] = equilizeNoOfElements(this.oldData, newData);
let newPos = newData.map(d => d.end); let newPos = newData.map(d => d.end);
@ -1478,8 +1368,87 @@ let componentConfigs = {
} }
}, },
dataUnits: { barGraph: {
// // opt:[
// 'barGraph',
// this.drawArea,
// {
// controller: barController,
// index: index,
// color: this.colors[index],
// valuesOverPoints: this.valuesOverPoints,
// stacked: this.barOptions && this.barOptions.stacked,
// spaceRatio: 0.5,
// minHeight: this.height * MIN_BAR_PERCENT_HEIGHT
// },
// {
// barsWidth: this.state.unitWidth * (1 - spaceRatio),
// barWidth: barsWidth/(stacked ? 1 : this.state.noOfDatasets),
// },
// function() {
// let s = this.state;
// return {
// barsWidth: this.state.unitWidth * (1 - spaceRatio),
// barWidth: barsWidth/(stacked ? 1 : this.state.noOfDatasets),
// positions: s.xAxisPositions,
// labels: s.xAxisLabels,
// }
// }.bind(this)
// ],
layerClass() { return 'y-regions' + this.constants.index; },
makeElements(data) {
let c = this.constants;
return data.yPositions.map((y, j) =>
barController.draw(
data.xPositions[j],
y,
color,
(c.valuesOverPoints ? (c.stacked ? data.cumulativeYs[j] : data.values[j]) : ''),
j,
y - (data.cumulativePositions ? data.cumulativePositions[j] : y)
)
);
},
postMake() {
if((!this.constants.stacked)) {
this.layer.setAttribute('transform',
`translate(${unitRenderer.consts.width * index}, 0)`);
}
},
animateElements(newData) {
[this.oldData, newData] = equilizeNoOfElements(this.oldData, newData);
let newPos = newData.map(d => d.end);
let newLabels = newData.map(d => d.label);
let newStarts = newData.map(d => d.start);
let oldPos = this.oldData.map(d => d.end);
let oldLabels = this.oldData.map(d => d.label);
let oldStarts = this.oldData.map(d => d.start);
this.render(oldPos.map((pos, i) => {
return {
start: oldStarts[i],
end: oldPos[i],
label: newLabels[i]
}
}));
let animateElements = [];
this.store.map((rectGroup, i) => {
animateElements = animateElements.concat(animateRegion(
rectGroup, newStarts[i], newPos[i], oldPos[i]
));
});
return animateElements;
}
},
lineGraph: {
} }
}; };
@ -1494,139 +1463,6 @@ function getComponent(name, parent, constants, initData, getData) {
return new ChartComponent$1(config); return new ChartComponent$1(config);
} }
const MIN_BAR_PERCENT_HEIGHT$1 = 0.01;
class AxisChartController {
constructor(meta) {
// TODO: make configurable passing args
this.meta = meta || {};
this.setupArgs();
}
setupArgs() {
this.consts = {};
}
setup() {}
refreshMeta(meta) {
this.meta = Object.assign((this.meta || {}), meta);
}
draw() {}
animate() {}
}
class BarChartController extends AxisChartController {
constructor(meta) {
super(meta);
}
setupArgs() {
this.consts = {
spaceRatio: 0.5,
minHeight: this.meta.totalHeight * MIN_BAR_PERCENT_HEIGHT$1
};
}
refreshMeta(meta) {
if(meta) {
super.refreshMeta(meta);
}
let m = this.meta;
this.consts.barsWidth = m.unitWidth - m.unitWidth * this.consts.spaceRatio;
this.consts.width = this.consts.barsWidth / (m.options && m.options.stacked
? m.options.stacked : m.noOfDatasets);
}
draw(x, yTop, color, label='', index=0, offset=0) {
let [height, y] = getBarHeightAndYAttr(yTop, this.meta.zeroLine);
let rect = createSVG('rect', {
className: `bar mini`,
style: `fill: ${color}`,
'data-point-index': index,
x: x - this.consts.barsWidth/2,
y: y - offset,
width: this.consts.width,
height: height || this.consts.minHeight
});
if(!label && !label.length) {
return rect;
} else {
let text = createSVG('text', {
className: 'data-point-value',
x: x,
y: y - offset,
dy: (FONT_SIZE / 2 * -1) + 'px',
'font-size': FONT_SIZE + 'px',
'text-anchor': 'middle',
innerHTML: label
});
return wrapInSVGGroup([rect, text]);
}
}
animate(bar, x, yTop, index, noOfDatasets) {
let start = x - this.meta.unitWidth/4;
let width = (this.meta.unitWidth/2)/noOfDatasets;
let [height, y] = getBarHeightAndYAttr(yTop, this.meta.zeroLine, this.meta.totalHeight);
x = start + (width * index);
return [bar, {width: width, height: height, x: x, y: y}, UNIT_ANIM_DUR, STD_EASING];
// bar.animate({height: args.newHeight, y: yTop}, UNIT_ANIM_DUR, mina.easein);
}
}
class LineChartController extends AxisChartController {
constructor(meta) {
super(meta);
}
setupArgs() {
this.consts = {
radius: this.meta.dotSize || 4
};
}
draw(x, y, color, label='', index=0) {
let dot = createSVG('circle', {
style: `fill: ${color}`,
'data-point-index': index,
cx: x,
cy: y,
r: this.consts.radius
});
if(!label && !label.length) {
return dot;
} else {
let text = createSVG('text', {
className: 'data-point-value',
x: x,
y: y,
dy: (FONT_SIZE / 2 * -1 - this.consts.radius) + 'px',
'font-size': FONT_SIZE + 'px',
'text-anchor': 'middle',
innerHTML: label
});
return wrapInSVGGroup([dot, text]);
}
}
animate(dot, x, yTop) {
return [dot, {cx: x, cy: yTop}, UNIT_ANIM_DUR, STD_EASING];
// dot.animate({cy: yTop}, UNIT_ANIM_DUR, mina.easein);
}
}
function getPaths(yList, xList, color, heatline=false, regionFill=false) { function getPaths(yList, xList, color, heatline=false, regionFill=false) {
let pointsList = yList.map((y, i) => (xList[i] + ',' + y)); let pointsList = yList.map((y, i) => (xList[i] + ',' + y));
let pointsStr = pointsList.join("L"); let pointsStr = pointsList.join("L");
@ -1953,10 +1789,8 @@ class AxisChart extends BaseChart {
this.xAxisMode = args.xAxisMode || 'span'; this.xAxisMode = args.xAxisMode || 'span';
this.yAxisMode = args.yAxisMode || 'span'; this.yAxisMode = args.yAxisMode || 'span';
this.setupUnitRenderer();
this.zeroLine = this.height; this.zeroLine = this.height;
this.preSetup(); this.setPrimitiveData();
this.setup(); this.setup();
} }
@ -1967,15 +1801,14 @@ class AxisChart extends BaseChart {
this.config.yAxisMode = args.yAxisMode; this.config.yAxisMode = args.yAxisMode;
} }
preSetup() {} setPrimitiveData() {
// Define data and stuff
this.setObservers();
}
setupUnitRenderer() { setObservers() {
// TODO: this is empty // go through each component and check the keys in this.state it depends on
let options = this.rawChartArgs.options; // set an observe() on each of those keys for that component
this.unitRenderers = {
bar: new BarChartController(options),
line: new LineChartController(options)
};
} }
setHorizontalMargin() { setHorizontalMargin() {
@ -1987,10 +1820,6 @@ class AxisChart extends BaseChart {
return true; return true;
} }
getFirstUpdateData(data) {
//
}
setupConstants() { setupConstants() {
this.state = { this.state = {
xAxisLabels: [], xAxisLabels: [],
@ -2005,18 +1834,19 @@ class AxisChart extends BaseChart {
} }
}); });
this.prepareYAxis(); // Prepare Y Axis
this.state.yAxis = {
labels: [],
positions: []
};
} }
prepareData(data) { prepareData(data) {
let s = this.state; let s = this.state;
s.xAxisLabels = data.labels || []; s.xAxisLabels = data.labels || [];
s.datasetLength = s.xAxisLabels.length; s.datasetLength = s.xAxisLabels.length;
let zeroArray = new Array(s.datasetLength).fill(0); let zeroArray = new Array(s.datasetLength).fill(0);
s.datasets = data.datasets; // whole dataset info too s.datasets = data.datasets; // whole dataset info too
if(!data.datasets) { if(!data.datasets) {
// default // default
@ -2049,13 +1879,6 @@ class AxisChart extends BaseChart {
s.yRegions = data.yRegions; s.yRegions = data.yRegions;
} }
prepareYAxis() {
this.state.yAxis = {
labels: [],
positions: []
};
}
calc() { calc() {
let s = this.state; let s = this.state;
@ -2068,8 +1891,6 @@ class AxisChart extends BaseChart {
this.calcYMaximums(); this.calcYMaximums();
this.calcYRegions(); this.calcYRegions();
// should be state
this.configUnits();
} }
setYAxis() { setYAxis() {
@ -2154,8 +1975,6 @@ class AxisChart extends BaseChart {
} }
} }
configUnits() {}
// Default, as per bar, and mixed. Only line will be a special case // Default, as per bar, and mixed. Only line will be a special case
setUnitWidthAndXOffset() { setUnitWidthAndXOffset() {
this.state.unitWidth = this.width/(this.state.datasetLength); this.state.unitWidth = this.width/(this.state.datasetLength);
@ -2179,12 +1998,6 @@ class AxisChart extends BaseChart {
return [].concat(...this.state.datasets.map(d => d[key])); return [].concat(...this.state.datasets.map(d => d[key]));
} }
calcIntermedState() {
//
}
setupValues() {}
initComponents() { initComponents() {
this.componentConfigs = [ this.componentConfigs = [
[ [
@ -2266,7 +2079,7 @@ class AxisChart extends BaseChart {
function() { function() {
return this.state.yMarkers || []; return this.state.yMarkers || [];
}.bind(this) }.bind(this)
], ]
]; ];
} }
setupComponents() { setupComponents() {
@ -2276,10 +2089,6 @@ class AxisChart extends BaseChart {
.map(args => getComponent(...args)); .map(args => getComponent(...args));
} }
refreshComponents() {
this.components.forEach(comp => comp.refresh(comp.getData()));
}
getChartComponents() { getChartComponents() {
let dataUnitsComponents = []; let dataUnitsComponents = [];
// this.state is not defined at this stage // this.state is not defined at this stage
@ -2299,9 +2108,8 @@ class AxisChart extends BaseChart {
getDataUnitComponent(index, unitRenderer) { getDataUnitComponent(index, unitRenderer) {
return new ChartComponent({ return new ChartComponent({
layerClass: 'dataset-units dataset-' + index, layerClass: 'dataset-units dataset-' + index,
setData: () => {},
preMake: () => { },
makeElements: () => { makeElements: () => {
// yPositions, xPostions, color, valuesOverPoints,
let d = this.state.datasets[index]; let d = this.state.datasets[index];
return d.positions.map((y, j) => { return d.positions.map((y, j) => {
@ -2405,41 +2213,6 @@ class AxisChart extends BaseChart {
}); });
} }
refreshRenderer() {
// These args are basically the current state of the chart,
// with constant and alive params mixed
let state = {
totalHeight: this.height,
totalWidth: this.width,
xAxisMode: this.config.xAxisMode,
yAxisMode: this.config.yAxisMode,
zeroLine: this.state.zeroLine,
unitWidth: this.state.unitWidth,
};
if(!this.renderer) {
this.renderer = new AxisChartRenderer(state);
} else {
this.renderer.refreshState(state);
}
let meta = {
totalHeight: this.height,
totalWidth: this.width,
zeroLine: this.state.zeroLine,
unitWidth: this.state.unitWidth,
noOfDatasets: this.state.noOfDatasets,
};
meta = Object.assign(meta, this.rawChartArgs.options);
Object.keys(this.unitRenderers).map(key => {
meta.options = this[key + 'Options'];
this.unitRenderers[key].refreshMeta(meta);
});
}
bindTooltip() { bindTooltip() {
// TODO: could be in tooltip itself, as it is a given functionality for its parent // TODO: could be in tooltip itself, as it is a given functionality for its parent
this.chartWrapper.addEventListener('mousemove', (e) => { this.chartWrapper.addEventListener('mousemove', (e) => {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -9,6 +9,7 @@ import { Animator, translateHoriLine } from '../utils/animate';
import { runSMILAnimation } from '../utils/animation'; import { runSMILAnimation } from '../utils/animation';
import { getRealIntervals, calcChartIntervals, getIntervalSize, getValueRange, getZeroIndex } from '../utils/intervals'; import { getRealIntervals, calcChartIntervals, getIntervalSize, getValueRange, getZeroIndex } from '../utils/intervals';
import { floatTwo, fillArray } from '../utils/helpers'; import { floatTwo, fillArray } from '../utils/helpers';
import { MIN_BAR_PERCENT_HEIGHT } from '../utils/constants';
export default class AxisChart extends BaseChart { export default class AxisChart extends BaseChart {
constructor(args) { constructor(args) {
@ -24,10 +25,8 @@ export default class AxisChart extends BaseChart {
this.xAxisMode = args.xAxisMode || 'span'; this.xAxisMode = args.xAxisMode || 'span';
this.yAxisMode = args.yAxisMode || 'span'; this.yAxisMode = args.yAxisMode || 'span';
this.setupUnitRenderer();
this.zeroLine = this.height; this.zeroLine = this.height;
this.preSetup(); this.setPrimitiveData();
this.setup(); this.setup();
} }
@ -38,15 +37,14 @@ export default class AxisChart extends BaseChart {
this.config.yAxisMode = args.yAxisMode; this.config.yAxisMode = args.yAxisMode;
} }
preSetup() {} setPrimitiveData() {
// Define data and stuff
this.setObservers();
}
setupUnitRenderer() { setObservers() {
// TODO: this is empty // go through each component and check the keys in this.state it depends on
let options = this.rawChartArgs.options; // set an observe() on each of those keys for that component
this.unitRenderers = {
bar: new BarChartController(options),
line: new LineChartController(options)
};
} }
setHorizontalMargin() { setHorizontalMargin() {
@ -58,10 +56,6 @@ export default class AxisChart extends BaseChart {
return true; return true;
} }
getFirstUpdateData(data) {
//
}
setupConstants() { setupConstants() {
this.state = { this.state = {
xAxisLabels: [], xAxisLabels: [],
@ -76,18 +70,19 @@ export default class AxisChart extends BaseChart {
} }
}); });
this.prepareYAxis(); // Prepare Y Axis
this.state.yAxis = {
labels: [],
positions: []
};
} }
prepareData(data) { prepareData(data) {
let s = this.state; let s = this.state;
s.xAxisLabels = data.labels || []; s.xAxisLabels = data.labels || [];
s.datasetLength = s.xAxisLabels.length; s.datasetLength = s.xAxisLabels.length;
let zeroArray = new Array(s.datasetLength).fill(0); let zeroArray = new Array(s.datasetLength).fill(0);
s.datasets = data.datasets; // whole dataset info too s.datasets = data.datasets; // whole dataset info too
if(!data.datasets) { if(!data.datasets) {
// default // default
@ -120,13 +115,6 @@ export default class AxisChart extends BaseChart {
s.yRegions = data.yRegions; s.yRegions = data.yRegions;
} }
prepareYAxis() {
this.state.yAxis = {
labels: [],
positions: []
};
}
calc() { calc() {
let s = this.state; let s = this.state;
@ -139,8 +127,6 @@ export default class AxisChart extends BaseChart {
this.calcYMaximums(); this.calcYMaximums();
this.calcYRegions(); this.calcYRegions();
// should be state
this.configUnits();
} }
setYAxis() { setYAxis() {
@ -225,8 +211,6 @@ export default class AxisChart extends BaseChart {
} }
} }
configUnits() {}
// Default, as per bar, and mixed. Only line will be a special case // Default, as per bar, and mixed. Only line will be a special case
setUnitWidthAndXOffset() { setUnitWidthAndXOffset() {
this.state.unitWidth = this.width/(this.state.datasetLength); this.state.unitWidth = this.width/(this.state.datasetLength);
@ -250,12 +234,6 @@ export default class AxisChart extends BaseChart {
return [].concat(...this.state.datasets.map(d => d[key])); return [].concat(...this.state.datasets.map(d => d[key]));
} }
calcIntermedState() {
//
}
setupValues() {}
initComponents() { initComponents() {
this.componentConfigs = [ this.componentConfigs = [
[ [
@ -337,7 +315,7 @@ export default class AxisChart extends BaseChart {
function() { function() {
return this.state.yMarkers || []; return this.state.yMarkers || [];
}.bind(this) }.bind(this)
], ]
]; ];
} }
setupComponents() { setupComponents() {
@ -347,10 +325,6 @@ export default class AxisChart extends BaseChart {
.map(args => getComponent(...args)); .map(args => getComponent(...args));
} }
refreshComponents() {
this.components.forEach(comp => comp.refresh(comp.getData()));
}
getChartComponents() { getChartComponents() {
let dataUnitsComponents = [] let dataUnitsComponents = []
// this.state is not defined at this stage // this.state is not defined at this stage
@ -370,9 +344,8 @@ export default class AxisChart extends BaseChart {
getDataUnitComponent(index, unitRenderer) { getDataUnitComponent(index, unitRenderer) {
return new ChartComponent({ return new ChartComponent({
layerClass: 'dataset-units dataset-' + index, layerClass: 'dataset-units dataset-' + index,
setData: () => {},
preMake: () => { },
makeElements: () => { makeElements: () => {
// yPositions, xPostions, color, valuesOverPoints,
let d = this.state.datasets[index]; let d = this.state.datasets[index];
return d.positions.map((y, j) => { return d.positions.map((y, j) => {
@ -476,41 +449,6 @@ export default class AxisChart extends BaseChart {
}); });
} }
refreshRenderer() {
// These args are basically the current state of the chart,
// with constant and alive params mixed
let state = {
totalHeight: this.height,
totalWidth: this.width,
xAxisMode: this.config.xAxisMode,
yAxisMode: this.config.yAxisMode,
zeroLine: this.state.zeroLine,
unitWidth: this.state.unitWidth,
};
if(!this.renderer) {
this.renderer = new AxisChartRenderer(state);
} else {
this.renderer.refreshState(state);
}
let meta = {
totalHeight: this.height,
totalWidth: this.width,
zeroLine: this.state.zeroLine,
unitWidth: this.state.unitWidth,
noOfDatasets: this.state.noOfDatasets,
};
meta = Object.assign(meta, this.rawChartArgs.options);
Object.keys(this.unitRenderers).map(key => {
meta.options = this[key + 'Options'];
this.unitRenderers[key].refreshMeta(meta);
});
}
bindTooltip() { bindTooltip() {
// TODO: could be in tooltip itself, as it is a given functionality for its parent // TODO: could be in tooltip itself, as it is a given functionality for its parent
this.chartWrapper.addEventListener('mousemove', (e) => { this.chartWrapper.addEventListener('mousemove', (e) => {

View File

@ -107,18 +107,10 @@ export default class BaseChart {
let valid = this.checkData(data); let valid = this.checkData(data);
if(!valid) return false; if(!valid) return false;
if(!this.config.animate) {
this.data = data; this.data = data;
} else {
[this.data, this.firstUpdateData] =
this.getFirstUpdateData(data);
}
return true; return true;
} }
checkData() {}
getFirstUpdateData() {}
setup() { setup() {
if(this.validate()) { if(this.validate()) {
this._setup(); this._setup();
@ -205,7 +197,6 @@ export default class BaseChart {
update(data=this.data) { update(data=this.data) {
this.prepareData(data); this.prepareData(data);
this.calc(); // builds state this.calc(); // builds state
this.refreshRenderer();
this.render(); this.render();
} }
@ -215,13 +206,10 @@ export default class BaseChart {
calc() {} // builds state calc() {} // builds state
refreshRenderer() {
this.renderer = {};
}
render(animate=true) { render(animate=true) {
this.refreshComponents(); // Can decouple to this.refreshComponents() first to save animation timeout
this.elementsToAnimate = [].concat.apply([], this.components.map(c => c.update(animate))); this.elementsToAnimate = [].concat.apply([],
this.components.map(c => c.update(animate)));
if(this.elementsToAnimate) { if(this.elementsToAnimate) {
runSMILAnimation(this.chartWrapper, this.svg, this.elementsToAnimate); runSMILAnimation(this.chartWrapper, this.svg, this.elementsToAnimate);
} }
@ -232,8 +220,6 @@ export default class BaseChart {
// } // }
} }
refreshComponents() {}
makeChartArea() { makeChartArea() {
this.svg = makeSVGContainer( this.svg = makeSVGContainer(
this.chartWrapper, this.chartWrapper,

View File

@ -12,18 +12,15 @@ class ChartComponent {
data, data,
// called on update // called on update
preMake,
makeElements, makeElements,
postMake, postMake,
getData, getData,
animateElements animateElements
}) { }) {
this.parent = parent; this.parent = parent;
this.layerClass = layerClass;
this.layerTransform = layerTransform; this.layerTransform = layerTransform;
this.constants = constants; this.constants = constants;
this.preMake = preMake;
this.makeElements = makeElements; this.makeElements = makeElements;
this.postMake = postMake; this.postMake = postMake;
this.getData = getData; this.getData = getData;
@ -31,7 +28,11 @@ class ChartComponent {
this.animateElements = animateElements; this.animateElements = animateElements;
this.store = []; this.store = [];
this.layer = makeSVGGroup(this.parent, this.layerClass, this.layerTransform);
layerClass = typeof(layerClass) === 'function'
? layerClass() : layerClass;
this.layer = makeSVGGroup(this.parent, layerClass, this.layerTransform);
this.data = data; this.data = data;
@ -39,7 +40,7 @@ class ChartComponent {
} }
refresh(data) { refresh(data) {
this.data = data; this.data = data || this.getData();
} }
make() { make() {
@ -59,6 +60,7 @@ class ChartComponent {
} }
update(animate = true) { update(animate = true) {
this.refresh();
let animateElements = [] let animateElements = []
if(animate) { if(animate) {
animateElements = this.animateElements(this.data); animateElements = this.animateElements(this.data);
@ -74,14 +76,14 @@ class ChartComponent {
let componentConfigs = { let componentConfigs = {
yAxis: { yAxis: {
layerClass: 'y axis', layerClass: 'y axis',
makeElements: function(data) { makeElements(data) {
return data.positions.map((position, i) => return data.positions.map((position, i) =>
yLine(position, data.labels[i], this.constants.width, yLine(position, data.labels[i], this.constants.width,
{mode: this.constants.mode, pos: this.constants.pos}) {mode: this.constants.mode, pos: this.constants.pos})
); );
}, },
animateElements: function(newData) { animateElements(newData) {
let newPos = newData.positions; let newPos = newData.positions;
let newLabels = newData.labels; let newLabels = newData.labels;
let oldPos = this.oldData.positions; let oldPos = this.oldData.positions;
@ -105,14 +107,14 @@ let componentConfigs = {
xAxis: { xAxis: {
layerClass: 'x axis', layerClass: 'x axis',
makeElements: function(data) { makeElements(data) {
return data.positions.map((position, i) => return data.positions.map((position, i) =>
xLine(position, data.labels[i], this.constants.height, xLine(position, data.labels[i], this.constants.height,
{mode: this.constants.mode, pos: this.constants.pos}) {mode: this.constants.mode, pos: this.constants.pos})
); );
}, },
animateElements: function(newData) { animateElements(newData) {
let newPos = newData.positions; let newPos = newData.positions;
let newLabels = newData.labels; let newLabels = newData.labels;
let oldPos = this.oldData.positions; let oldPos = this.oldData.positions;
@ -136,13 +138,13 @@ let componentConfigs = {
yMarkers: { yMarkers: {
layerClass: 'y-markers', layerClass: 'y-markers',
makeElements: function(data) { makeElements(data) {
return data.map(marker => return data.map(marker =>
yMarker(marker.position, marker.label, this.constants.width, yMarker(marker.position, marker.label, this.constants.width,
{pos:'right', mode: 'span', lineType: 'dashed'}) {pos:'right', mode: 'span', lineType: 'dashed'})
); );
}, },
animateElements: function(newData) { animateElements(newData) {
[this.oldData, newData] = equilizeNoOfElements(this.oldData, newData); [this.oldData, newData] = equilizeNoOfElements(this.oldData, newData);
let newPos = newData.map(d => d.position); let newPos = newData.map(d => d.position);
@ -168,13 +170,13 @@ let componentConfigs = {
yRegions: { yRegions: {
layerClass: 'y-regions', layerClass: 'y-regions',
makeElements: function(data) { makeElements(data) {
return data.map(region => return data.map(region =>
yRegion(region.start, region.end, this.constants.width, yRegion(region.start, region.end, this.constants.width,
region.label) region.label)
); );
}, },
animateElements: function(newData) { animateElements(newData) {
[this.oldData, newData] = equilizeNoOfElements(this.oldData, newData); [this.oldData, newData] = equilizeNoOfElements(this.oldData, newData);
let newPos = newData.map(d => d.end); let newPos = newData.map(d => d.end);
@ -205,8 +207,87 @@ let componentConfigs = {
} }
}, },
dataUnits: { barGraph: {
// // opt:[
// 'barGraph',
// this.drawArea,
// {
// controller: barController,
// index: index,
// color: this.colors[index],
// valuesOverPoints: this.valuesOverPoints,
// stacked: this.barOptions && this.barOptions.stacked,
// spaceRatio: 0.5,
// minHeight: this.height * MIN_BAR_PERCENT_HEIGHT
// },
// {
// barsWidth: this.state.unitWidth * (1 - spaceRatio),
// barWidth: barsWidth/(stacked ? 1 : this.state.noOfDatasets),
// },
// function() {
// let s = this.state;
// return {
// barsWidth: this.state.unitWidth * (1 - spaceRatio),
// barWidth: barsWidth/(stacked ? 1 : this.state.noOfDatasets),
// positions: s.xAxisPositions,
// labels: s.xAxisLabels,
// }
// }.bind(this)
// ],
layerClass() { return 'y-regions' + this.constants.index; },
makeElements(data) {
let c = this.constants;
return data.yPositions.map((y, j) =>
barController.draw(
data.xPositions[j],
y,
color,
(c.valuesOverPoints ? (c.stacked ? data.cumulativeYs[j] : data.values[j]) : ''),
j,
y - (data.cumulativePositions ? data.cumulativePositions[j] : y)
)
);
},
postMake() {
if((!this.constants.stacked)) {
this.layer.setAttribute('transform',
`translate(${unitRenderer.consts.width * index}, 0)`);
}
},
animateElements(newData) {
[this.oldData, newData] = equilizeNoOfElements(this.oldData, newData);
let newPos = newData.map(d => d.end);
let newLabels = newData.map(d => d.label);
let newStarts = newData.map(d => d.start);
let oldPos = this.oldData.map(d => d.end);
let oldLabels = this.oldData.map(d => d.label);
let oldStarts = this.oldData.map(d => d.start);
this.render(oldPos.map((pos, i) => {
return {
start: oldStarts[i],
end: oldPos[i],
label: newLabels[i]
}
}));
let animateElements = [];
this.store.map((rectGroup, i) => {
animateElements = animateElements.concat(animateRegion(
rectGroup, newStarts[i], newPos[i], oldPos[i]
));
});
return animateElements;
}
},
lineGraph: {
} }
} }

View File

@ -0,0 +1 @@
export const MIN_BAR_PERCENT_HEIGHT = 0.01;

View File

@ -61,3 +61,26 @@ export function fillArray(array, count, element, start=false) {
export function getStringWidth(string, charWidth) { export function getStringWidth(string, charWidth) {
return (string+"").length * charWidth; return (string+"").length * charWidth;
} }
function observe(obj, componentNames) {
let components = this.components.get(name);
fn = function() {
components.map();
}
bindChange(obj, fn)
}
// observe(s.yAxis, ['yAxis', 'barGraph'])
export function bindChange(obj, fn) {
var proxied = new Proxy(obj, {
set: function(target, prop, value) {
fn();
return Reflect.set(target, prop, value);
}
});
// proxied.bar = 2;
// ==> {type: 'set', target: <obj>, prop: 'bar', value: 2}
}