[animate] y axes

This commit is contained in:
Prateeksha Singh 2018-02-25 01:35:49 +05:30
parent e6014e7e82
commit b11a78aa31
19 changed files with 571 additions and 561 deletions

View File

@ -290,6 +290,23 @@ const REPLACE_ALL_NEW_DUR = 250;
const STD_EASING = 'easein'; const STD_EASING = 'easein';
function translate(unit, oldCoord, newCoord, duration) {
return [
unit,
{transform: newCoord.join(', ')},
duration,
STD_EASING,
"translate",
{transform: oldCoord.join(', ')}
];
}
function translateHoriLine(yLine, newY, oldY) {
return translate(yLine, [0, oldY], [0, newY], MARKER_LINE_ANIM_DUR);
}
const AXIS_TICK_LENGTH = 6; const AXIS_TICK_LENGTH = 6;
const LABEL_MARGIN = 4; const LABEL_MARGIN = 4;
const FONT_SIZE = 10; const FONT_SIZE = 10;
@ -478,8 +495,8 @@ function makeHoriLine(y, label, x1, x2, options={}) {
className: className, className: className,
x1: x1, x1: x1,
x2: x2, x2: x2,
y1: y, y1: 0,
y2: y, y2: 0,
styles: { styles: {
stroke: options.stroke stroke: options.stroke
} }
@ -487,7 +504,7 @@ function makeHoriLine(y, label, x1, x2, options={}) {
let text = createSVG('text', { let text = createSVG('text', {
x: x1 < x2 ? x1 - LABEL_MARGIN : x1 + LABEL_MARGIN, x: x1 < x2 ? x1 - LABEL_MARGIN : x1 + LABEL_MARGIN,
y: y, y: 0,
dy: (FONT_SIZE / 2 - 2) + 'px', dy: (FONT_SIZE / 2 - 2) + 'px',
'font-size': FONT_SIZE + 'px', 'font-size': FONT_SIZE + 'px',
'text-anchor': x1 < x2 ? 'end' : 'start', 'text-anchor': x1 < x2 ? 'end' : 'start',
@ -495,6 +512,7 @@ function makeHoriLine(y, label, x1, x2, options={}) {
}); });
let line = createSVG('g', { let line = createSVG('g', {
transform: `translate(0, ${y})`,
'stroke-opacity': 1 'stroke-opacity': 1
}); });
@ -508,6 +526,31 @@ function makeHoriLine(y, label, x1, x2, options={}) {
return line; return line;
} }
function yLine(y, label, width, options={}) {
if(!options.pos) options.pos = 'left';
if(!options.offset) options.offset = 0;
if(!options.mode) options.mode = 'span';
if(!options.stroke) options.stroke = BASE_LINE_COLOR;
if(!options.className) options.className = '';
let x1 = -1 * AXIS_TICK_LENGTH;
let x2 = options.mode === 'span' ? width + AXIS_TICK_LENGTH : 0;
if(options.mode === 'tick' && options.pos === 'right') {
x1 = width + AXIS_TICK_LENGTH;
x2 = width;
}
x1 += options.offset;
x2 += options.offset;
return makeHoriLine(y, label, x1, x2, {
stroke: options.stroke,
className: options.className,
lineType: options.lineType
});
}
class AxisChartRenderer { class AxisChartRenderer {
constructor(state) { constructor(state) {
this.refreshState(state); this.refreshState(state);
@ -529,7 +572,7 @@ class AxisChartRenderer {
xLine(x, label, options={}) { xLine(x, label, options={}) {
if(!options.pos) options.pos = 'bottom'; if(!options.pos) options.pos = 'bottom';
if(!options.offset) options.offset = 0; if(!options.offset) options.offset = 0;
if(!options.mode) options.mode = this.xAxisMode; if(!options.mode) options.mode = 'span';
if(!options.stroke) options.stroke = BASE_LINE_COLOR; if(!options.stroke) options.stroke = BASE_LINE_COLOR;
if(!options.className) options.className = ''; if(!options.className) options.className = '';
@ -560,30 +603,7 @@ class AxisChartRenderer {
}); });
} }
yLine(y, label, options={}) {
if(!options.pos) options.pos = 'left';
if(!options.offset) options.offset = 0;
if(!options.mode) options.mode = this.yAxisMode;
if(!options.stroke) options.stroke = BASE_LINE_COLOR;
if(!options.className) options.className = '';
let x1 = -1 * AXIS_TICK_LENGTH;
let x2 = options.mode === 'span' ? this.totalWidth + AXIS_TICK_LENGTH : 0;
if(options.mode === 'tick' && options.pos === 'right') {
x1 = this.totalWidth + AXIS_TICK_LENGTH;
x2 = this.totalWidth;
}
x1 += options.offset;
x2 += options.offset;
return makeHoriLine(y, label, x1, x2, {
stroke: options.stroke,
className: options.className,
lineType: options.lineType
});
}
xMarker() {} xMarker() {}
@ -713,25 +733,6 @@ class AxisChartRenderer {
return pathComponents; return pathComponents;
} }
translate(unit, oldCoord, newCoord, duration) {
return [
unit,
{transform: newCoord.join(', ')},
duration,
STD_EASING,
"translate",
{transform: oldCoord.join(', ')}
];
}
translateVertLine(xLine, newX, oldX) {
return this.translate(xLine, [oldX, 0], [newX, 0], MARKER_LINE_ANIM_DUR);
}
translateHoriLine(yLine, newY, oldY) {
return this.translate(yLine, [0, oldY], [0, newY], MARKER_LINE_ANIM_DUR);
}
} }
const PRESET_COLOR_MAP = { const PRESET_COLOR_MAP = {
@ -1066,11 +1067,15 @@ class BaseChart {
_setup() { _setup() {
this.bindWindowEvents(); this.bindWindowEvents();
this.setupConstants(); this.setupConstants();
this.setupComponents();
this.setMargins(); this.setMargins();
this.makeContainer(); this.makeContainer();
this.makeTooltip(); // without binding this.makeTooltip(); // without binding
this.calcWidth();
this.makeChartArea();
this.setupComponents();
this.draw(true); this.draw(true);
} }
@ -1113,31 +1118,13 @@ class BaseChart {
bindTooltip() {} bindTooltip() {}
draw(init=false) { draw(init=false) {
// difference from update(): draw the whole object due to groudbreaking event (init, resize, etc.) this.components.forEach(c => c.make()); // or c.build()
// (draw everything, layers, groups, units)
this.calcWidth();
// refresh conponent with chart
this.refresh(this.data);
this.makeChartArea();
this.setComponentParent();
this.makeComponentLayers();
this.renderLegend(); this.renderLegend();
this.setupNavigation(init); this.setupNavigation(init);
// first time plain render, so no rerender // TODO: remove timeout and decrease post animate time in chart component
this.renderComponents(); setTimeout(() => {this.update();}, 1000);
this.renderConstants();
if(this.config.animate) this.update(this.firstUpdateData);
}
update(data) {
this.refresh(data);
this.reRender();
} }
calcWidth() { calcWidth() {
@ -1153,15 +1140,34 @@ class BaseChart {
this.width = this.baseWidth - (this.translateXLeft + this.translateXRight); this.width = this.baseWidth - (this.translateXLeft + this.translateXRight);
} }
refresh(data) { //?? refresh? update(data=this.data) {
this.oldState = this.state ? JSON.parse(JSON.stringify(this.state)) : {};
this.intermedState = {}; // use this for the extra position problems?
this.prepareData(data); this.prepareData(data);
this.reCalc(); this.calc(); // builds state
this.refreshRenderer(); this.refreshRenderer();
this.render();
} }
prepareData() {}
renderConstants() {}
calc() {} // builds state
refreshRenderer() {
this.renderer = {};
}
render(animate=true) {
this.refreshComponents();
this.elementsToAnimate = [].concat.apply([], this.components.map(c => c.update(animate)));
console.log(this.elementsToAnimate);
if(this.elementsToAnimate) {
runSMILAnimation(this.chartWrapper, this.svg, this.elementsToAnimate);
}
}
refreshComponents() {}
makeChartArea() { makeChartArea() {
this.svg = makeSVGContainer( this.svg = makeSVGContainer(
this.chartWrapper, this.chartWrapper,
@ -1185,41 +1191,6 @@ class BaseChart {
); );
} }
prepareData() {}
renderConstants() {}
reCalc() {}
// Will update values(state)
// Will recalc specific parts depending on the update
refreshRenderer() {
this.renderer = {};
}
reRender(animate=true) {
if(!animate) {
this.renderComponents();
return;
}
this.elementsToAnimate = [];
this.loadAnimatedComponents();
runSMILAnimation(this.chartWrapper, this.svg, this.elementsToAnimate);
setTimeout(() => {
this.renderComponents();
}, 400);
// TODO: should be max anim duration required
// (opt, should not redraw if still in animate?)
}
// convenient component array abstractions
setComponentParent() { this.components.forEach(c => c.setupParent(this.drawArea)); };
makeComponentLayers() { this.components.forEach(c => c.makeLayer()); }
renderComponents() { this.components.forEach(c => c.render()); }
loadAnimatedComponents() { this.components.forEach(c => c.loadAnimatedComponents()); }
refreshComponents() { this.components.forEach(c => c.refresh(this.state, this.rawChartArgs)); }
renderLegend() {} renderLegend() {}
setupNavigation(init=false) { setupNavigation(init=false) {
@ -1299,66 +1270,107 @@ class BaseChart {
const Y_AXIS_MARGIN = 60; const Y_AXIS_MARGIN = 60;
class ChartComponent { class ChartComponent$1 {
constructor({ constructor({
layerClass = '', layerClass = '',
layerTransform = '', layerTransform = '',
initData, parent,
constants,
data,
// called on update // called on update
setData,
preMake, preMake,
make, makeElements,
postMake, postMake,
animate animateElements
}) { }) {
this.parent = parent;
this.layerClass = layerClass; this.layerClass = layerClass;
this.layerTransform = layerTransform; this.layerTransform = layerTransform;
this.constants = constants;
this.initData = initData;
this.setData = setData;
this.preMake = preMake; this.preMake = preMake;
this.make = make; this.makeElements = makeElements;
this.postMake = postMake; this.postMake = postMake;
this.animate = animate; this.animateElements = animateElements;
this.layer = undefined;
this.store = []; this.store = [];
this.layer = makeSVGGroup(this.parent, this.layerClass, this.layerTransform);
this.data = data;
this.make();
} }
refresh(state, args) { refresh(data) {
this.meta = Object.assign((this.meta || {}), args); this.data = data;
this.state = state;
} }
make() {
render() {
this.data = this.setData(); // The only without this function?
this.preMake && this.preMake(); this.preMake && this.preMake();
this.store = this.make(); this.render(this.data);
this.postMake && this.postMake();
this.oldData = this.data;
}
render(data) {
this.store = this.makeElements(data);
this.layer.textContent = ''; this.layer.textContent = '';
this.store.forEach(element => { this.store.forEach(element => {
this.layer.appendChild(element); this.layer.appendChild(element);
}); });
this.postMake && this.postMake();
} }
setupParent(parent) { update(animate = true) {
this.parent = parent; let animateElements = [];
if(animate) {
animateElements = this.animateElements(this.data);
}
// TODO: Can we remove this?
setTimeout(() => {
this.make();
}, 1400);
return animateElements;
} }
}
loadAnimatedComponents() { function getYAxisComponent(parent, constants, initData) {
this.animate(this.store); return new ChartComponent$1({
} parent: parent,
layerClass: 'y axis',
constants: constants,
data: initData,
makeElements: function(data) {
return data.positions.map((position, i) =>
yLine(position, data.labels[i], this.constants.width,
{mode: this.constants.mode, pos: this.constants.pos})
);
},
makeLayer() { animateElements: function(newData) {
this.layer = makeSVGGroup(this.parent, this.layerClass, this.layerTransform); let newPos = newData.positions;
} let newLabels = newData.labels;
let oldPos = this.oldData.positions;
let oldLabels = this.oldData.labels;
[oldPos, newPos] = equilizeNoOfElements(oldPos, newPos);
[oldLabels, newLabels] = equilizeNoOfElements(oldLabels, newLabels);
this.render({
positions: oldPos,
labels: newLabels
});
return this.store.map((line, i) => {
return translateHoriLine(
line, newPos[i], oldPos[i]
);
});
}
})
} }
const MIN_BAR_PERCENT_HEIGHT$1 = 0.01; const MIN_BAR_PERCENT_HEIGHT$1 = 0.01;
@ -1612,7 +1624,7 @@ function normalize(x) {
return [sig * man, exp]; return [sig * man, exp];
} }
function getRangeIntervals(max, min=0) { function getChartRangeIntervals(max, min=0) {
let upperBound = Math.ceil(max); let upperBound = Math.ceil(max);
let lowerBound = Math.floor(min); let lowerBound = Math.floor(min);
let range = upperBound - lowerBound; let range = upperBound - lowerBound;
@ -1650,19 +1662,19 @@ function getRangeIntervals(max, min=0) {
return intervals; return intervals;
} }
function getIntervals(maxValue, minValue=0) { function getChartIntervals(maxValue, minValue=0) {
let [normalMaxValue, exponent] = normalize(maxValue); let [normalMaxValue, exponent] = normalize(maxValue);
let normalMinValue = minValue ? minValue/Math.pow(10, exponent): 0; let normalMinValue = minValue ? minValue/Math.pow(10, exponent): 0;
// Allow only 7 significant digits // Allow only 7 significant digits
normalMaxValue = normalMaxValue.toFixed(6); normalMaxValue = normalMaxValue.toFixed(6);
let intervals = getRangeIntervals(normalMaxValue, normalMinValue); let intervals = getChartRangeIntervals(normalMaxValue, normalMinValue);
intervals = intervals.map(value => value * Math.pow(10, exponent)); intervals = intervals.map(value => value * Math.pow(10, exponent));
return intervals; return intervals;
} }
function calcIntervals(values, withMinimum=false) { function calcChartIntervals(values, withMinimum=false) {
//*** Where the magic happens *** //*** Where the magic happens ***
// Calculates best-fit y intervals from given values // Calculates best-fit y intervals from given values
@ -1675,7 +1687,7 @@ function calcIntervals(values, withMinimum=false) {
let exponent = 0, intervals = []; // eslint-disable-line no-unused-vars let exponent = 0, intervals = []; // eslint-disable-line no-unused-vars
function getPositiveFirstIntervals(maxValue, absMinValue) { function getPositiveFirstIntervals(maxValue, absMinValue) {
let intervals = getIntervals(maxValue); let intervals = getChartIntervals(maxValue);
let intervalSize = intervals[1] - intervals[0]; let intervalSize = intervals[1] - intervals[0];
@ -1693,9 +1705,9 @@ function calcIntervals(values, withMinimum=false) {
if(maxValue >= 0 && minValue >= 0) { if(maxValue >= 0 && minValue >= 0) {
exponent = normalize(maxValue)[1]; exponent = normalize(maxValue)[1];
if(!withMinimum) { if(!withMinimum) {
intervals = getIntervals(maxValue); intervals = getChartIntervals(maxValue);
} else { } else {
intervals = getIntervals(maxValue, minValue); intervals = getChartIntervals(maxValue, minValue);
} }
} }
@ -1733,9 +1745,9 @@ function calcIntervals(values, withMinimum=false) {
exponent = normalize(pseudoMaxValue)[1]; exponent = normalize(pseudoMaxValue)[1];
if(!withMinimum) { if(!withMinimum) {
intervals = getIntervals(pseudoMaxValue); intervals = getChartIntervals(pseudoMaxValue);
} else { } else {
intervals = getIntervals(pseudoMaxValue, pseudoMinValue); intervals = getChartIntervals(pseudoMaxValue, pseudoMinValue);
} }
intervals = intervals.reverse().map(d => d * (-1)); intervals = intervals.reverse().map(d => d * (-1));
@ -1765,6 +1777,18 @@ function getZeroIndex(yPts) {
return zeroIndex; return zeroIndex;
} }
function getRealIntervals(max, noOfIntervals, min = 0, asc = 1) {
let range = max - min;
let part = range * 1.0 / noOfIntervals;
let intervals = [];
for(var i = 0; i <= noOfIntervals; i++) {
intervals.push(min + part * i);
}
return asc ? intervals : intervals.reverse();
}
function getIntervalSize(orderedArray) { function getIntervalSize(orderedArray) {
return orderedArray[1] - orderedArray[0]; return orderedArray[1] - orderedArray[0];
} }
@ -1805,6 +1829,9 @@ class AxisChart extends BaseChart {
this.lineOptions = args.lineOptions; this.lineOptions = args.lineOptions;
this.type = args.type || 'line'; this.type = args.type || 'line';
this.xAxisMode = args.xAxisMode || 'span';
this.yAxisMode = args.yAxisMode || 'span';
this.setupUnitRenderer(); this.setupUnitRenderer();
this.zeroLine = this.height; this.zeroLine = this.height;
@ -1908,7 +1935,7 @@ class AxisChart extends BaseChart {
}; };
} }
reCalc() { calc() {
let s = this.state; let s = this.state;
s.xAxisLabels = this.data.labels; s.xAxisLabels = this.data.labels;
@ -1940,7 +1967,7 @@ class AxisChart extends BaseChart {
} }
calcYAxisParameters(yAxis, dataValues, withMinimum = 'false') { calcYAxisParameters(yAxis, dataValues, withMinimum = 'false') {
yAxis.labels = calcIntervals(dataValues, withMinimum); yAxis.labels = calcChartIntervals(dataValues, withMinimum);
const yPts = yAxis.labels; const yPts = yAxis.labels;
yAxis.scaleMultiplier = this.height / getValueRange(yPts); yAxis.scaleMultiplier = this.height / getValueRange(yPts);
@ -2040,78 +2067,63 @@ class AxisChart extends BaseChart {
// this.bind_units(units_array); // this.bind_units(units_array);
// } // }
this.yAxis = getYAxisComponent(
this.drawArea,
{
mode: this.yAxisMode,
width: this.width,
// pos: 'right'
},
{
positions: getRealIntervals(this.height, 4, 0, 0),
labels: getRealIntervals(this.height, 4, 0, 0).map(d => d + ""),
}
);
this.components = [ this.components = [
...this.getYAxesComponents(), this.yAxis
this.getXAxisComponents(), // this.getXAxisComponents(),
...this.getYRegions(), // ...this.getYRegions(),
...this.getXRegions(), // ...this.getXRegions(),
...this.getYMarkerLines(), // ...this.getYMarkerLines(),
// ...this.getXMarkerLines(), // // ...this.getXMarkerLines(),
...this.getChartComponents(), // ...this.getChartComponents(),
...this.getChartLabels(), // ...this.getChartLabels(),
]; ];
} }
getYAxesComponents() { refreshComponents() {
return [new ChartComponent({ this.refreshYAxis();
layerClass: 'y axis', }
setData: () => {
// let s = this.state;
// data = {}; refreshYAxis() {
let s = this.state;
this.yAxis.refresh({
// return data; positions: s.yAxis.positions,
}, labels: s.yAxis.labels,
initializeData: function() { });
this.axesPositions = this.state;
},
make: () => {
// positions, labels, renderer
let s = this.state;
return s.yAxis.positions.map((position, i) =>
this.renderer.yLine(position, s.yAxis.labels[i], {pos:'right'})
);
},
animate: (yLines) => {
// Equilize
let newY = this.state.yAxis.positions;
let oldY = this.oldState.yAxis.positions;
let extra = newY.length - oldY.length;
let lastLine = yLines[yLines.length - 1];
let parentNode = lastLine.parentNode;
[oldY, newY] = equilizeNoOfElements(oldY, newY);
// console.log(newY.slice(), oldY.slice());
if(extra > 0) {
for(var i = 0; i<extra; i++) {
let line = lastLine.cloneNode(true);
parentNode.appendChild(line);
yLines.push(line);
}
}
yLines.map((line, i) => {
// console.log(line, newY[i], oldY[i]);
this.elementsToAnimate.push(this.renderer.translateHoriLine(
line, newY[i], oldY[i]
));
});
}
})];
} }
getXAxisComponents() { getXAxisComponents() {
return new ChartComponent({ return new ChartComponent({
layerClass: 'x axis', layerClass: 'x axis',
setData: () => {}, setData: () => {
make: () => { let s = this.state;
let data = {
positions: s.xAxisPositions,
labels: s.xAxisLabels,
};
let constants = {
mode: this.xAxisMode,
height: this.height
};
return [data, constants];
},
makeElements: () => {
let s = this.state; let s = this.state;
// positions // positions
// TODO: xAxis Label spacing // TODO: xAxis Label spacing
return s.xAxisPositions.map((position, i) => return s.xAxisPositions.map((position, i) =>
this.renderer.xLine(position, s.xAxisLabels[i] xLine(position, s.xAxisLabels[i], this.constants.height
// , {pos:'top'} // , {pos:'top'}
) )
); );
@ -2168,7 +2180,7 @@ class AxisChart extends BaseChart {
layerClass: 'dataset-units dataset-' + index, layerClass: 'dataset-units dataset-' + index,
setData: () => {}, setData: () => {},
preMake: () => { }, preMake: () => { },
make: () => { makeElements: () => {
let d = this.state.datasets[index]; let d = this.state.datasets[index];
return d.positions.map((y, j) => { return d.positions.map((y, j) => {
@ -2230,7 +2242,7 @@ class AxisChart extends BaseChart {
return new ChartComponent({ return new ChartComponent({
layerClass: 'path dataset-path', layerClass: 'path dataset-path',
setData: () => {}, setData: () => {},
make: () => { makeElements: () => {
let d = this.state.datasets[index]; let d = this.state.datasets[index];
let color = this.colors[index]; let color = this.colors[index];
@ -2280,7 +2292,7 @@ class AxisChart extends BaseChart {
return new ChartComponent({ return new ChartComponent({
layerClass: 'y-markers', layerClass: 'y-markers',
setData: () => {}, setData: () => {},
make: () => { makeElements: () => {
let s = this.state; let s = this.state;
return s.yMarkers.map(marker => return s.yMarkers.map(marker =>
this.renderer.yMarker(marker.value, marker.name, this.renderer.yMarker(marker.value, marker.name,
@ -2301,7 +2313,7 @@ class AxisChart extends BaseChart {
return new ChartComponent({ return new ChartComponent({
layerClass: 'y-regions', layerClass: 'y-regions',
setData: () => {}, setData: () => {},
make: () => { makeElements: () => {
let s = this.state; let s = this.state;
return s.yRegions.map(region => return s.yRegions.map(region =>
this.renderer.yRegion(region.start, region.end, region.name) this.renderer.yRegion(region.start, region.end, region.name)
@ -2312,10 +2324,6 @@ class AxisChart extends BaseChart {
}); });
} }
getXRegions() {
return [];
}
refreshRenderer() { refreshRenderer() {
// These args are basically the current state of the chart, // These args are basically the current state of the chart,
// with constant and alive params mixed // with constant and alive params mixed
@ -2335,8 +2343,6 @@ class AxisChart extends BaseChart {
this.renderer.refreshState(state); this.renderer.refreshState(state);
} }
this.refreshComponents();
let meta = { let meta = {
totalHeight: this.height, totalHeight: this.height,
totalWidth: this.width, totalWidth: this.width,
@ -2778,40 +2784,42 @@ class PercentageChart extends BaseChart {
}); });
} }
bindTooltip() { calc() {}
this.slices.map((slice, i) => {
slice.addEventListener('mouseenter', () => {
let g_off = getOffset(this.chartWrapper), p_off = getOffset(slice);
let x = p_off.left - g_off.left + slice.offsetWidth/2; // bindTooltip() {
let y = p_off.top - g_off.top - 6; // this.slices.map((slice, i) => {
let title = (this.formatted_labels && this.formatted_labels.length>0 // slice.addEventListener('mouseenter', () => {
? this.formatted_labels[i] : this.labels[i]) + ': '; // let g_off = getOffset(this.chartWrapper), p_off = getOffset(slice);
let percent = (this.slice_totals[i]*100/this.grand_total).toFixed(1);
this.tip.set_values(x, y, title, percent + "%"); // let x = p_off.left - g_off.left + slice.offsetWidth/2;
this.tip.show_tip(); // let y = p_off.top - g_off.top - 6;
}); // let title = (this.formatted_labels && this.formatted_labels.length>0
}); // ? this.formatted_labels[i] : this.labels[i]) + ': ';
} // let percent = (this.slice_totals[i]*100/this.grand_total).toFixed(1);
renderLegend() { // this.tip.set_values(x, y, title, percent + "%");
let x_values = this.formatted_labels && this.formatted_labels.length > 0 // this.tip.show_tip();
? this.formatted_labels : this.labels; // });
this.legend_totals.map((d, i) => { // });
if(d) { // }
let stats = $$1.create('div', {
className: 'stats', // renderLegend() {
inside: this.statsWrapper // let x_values = this.formatted_labels && this.formatted_labels.length > 0
}); // ? this.formatted_labels : this.labels;
stats.innerHTML = `<span class="indicator"> // this.legend_totals.map((d, i) => {
<i style="background: ${this.colors[i]}"></i> // if(d) {
<span class="text-muted">${x_values[i]}:</span> // let stats = $.create('div', {
${d} // className: 'stats',
</span>`; // inside: this.statsWrapper
} // });
}); // stats.innerHTML = `<span class="indicator">
} // <i style="background: ${this.colors[i]}"></i>
// <span class="text-muted">${x_values[i]}:</span>
// ${d}
// </span>`;
// }
// });
// }
} }
const ANGLE_RATIO = Math.PI / 180; const ANGLE_RATIO = Math.PI / 180;

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

1
dist/frappe-charts.min.iife.js.map vendored Normal file

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

@ -53,6 +53,7 @@ let bar_composite_chart = new Chart ({
isNavigable: 1, isNavigable: 1,
isSeries: 1, isSeries: 1,
valuesOverPoints: 1, valuesOverPoints: 1,
yAxisMode: 'tick'
// regionFill: 1 // regionFill: 1
}); });

View File

@ -16,6 +16,7 @@ import pkg from './package.json';
export default [ export default [
{ {
input: 'src/js/chart.js', input: 'src/js/chart.js',
sourcemap: true,
output: [ output: [
{ {
file: 'docs/assets/js/frappe-charts.min.js', file: 'docs/assets/js/frappe-charts.min.js',

View File

@ -1,13 +1,13 @@
import BaseChart from './BaseChart'; import BaseChart from './BaseChart';
import { Y_AXIS_MARGIN } from '../utils/margins'; import { Y_AXIS_MARGIN } from '../utils/margins';
import { ChartComponent } from '../objects/ChartComponent'; import { getYAxisComponent } from '../objects/ChartComponents';
import { BarChartController, LineChartController, getPaths } from '../objects/AxisChartControllers'; import { BarChartController, LineChartController, getPaths } from '../objects/AxisChartControllers';
import { getOffset, fire } from '../utils/dom';
import { AxisChartRenderer } from '../utils/draw'; import { AxisChartRenderer } from '../utils/draw';
import { getOffset, fire } from '../utils/dom';
import { equilizeNoOfElements } from '../utils/draw-utils'; import { equilizeNoOfElements } from '../utils/draw-utils';
import { Animator } from '../utils/animate'; import { Animator, translateHoriLine } from '../utils/animate';
import { runSMILAnimation } from '../utils/animation'; import { runSMILAnimation } from '../utils/animation';
import { calcIntervals, 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';
export default class AxisChart extends BaseChart { export default class AxisChart extends BaseChart {
@ -21,6 +21,9 @@ export default class AxisChart extends BaseChart {
this.lineOptions = args.lineOptions; this.lineOptions = args.lineOptions;
this.type = args.type || 'line'; this.type = args.type || 'line';
this.xAxisMode = args.xAxisMode || 'span';
this.yAxisMode = args.yAxisMode || 'span';
this.setupUnitRenderer(); this.setupUnitRenderer();
this.zeroLine = this.height; this.zeroLine = this.height;
@ -124,7 +127,7 @@ export default class AxisChart extends BaseChart {
}; };
} }
reCalc() { calc() {
let s = this.state; let s = this.state;
s.xAxisLabels = this.data.labels; s.xAxisLabels = this.data.labels;
@ -156,7 +159,7 @@ export default class AxisChart extends BaseChart {
} }
calcYAxisParameters(yAxis, dataValues, withMinimum = 'false') { calcYAxisParameters(yAxis, dataValues, withMinimum = 'false') {
yAxis.labels = calcIntervals(dataValues, withMinimum); yAxis.labels = calcChartIntervals(dataValues, withMinimum);
const yPts = yAxis.labels; const yPts = yAxis.labels;
yAxis.scaleMultiplier = this.height / getValueRange(yPts); yAxis.scaleMultiplier = this.height / getValueRange(yPts);
@ -256,78 +259,63 @@ export default class AxisChart extends BaseChart {
// this.bind_units(units_array); // this.bind_units(units_array);
// } // }
this.yAxis = getYAxisComponent(
this.drawArea,
{
mode: this.yAxisMode,
width: this.width,
// pos: 'right'
},
{
positions: getRealIntervals(this.height, 4, 0, 0),
labels: getRealIntervals(this.height, 4, 0, 0).map(d => d + ""),
}
)
this.components = [ this.components = [
...this.getYAxesComponents(), this.yAxis
this.getXAxisComponents(), // this.getXAxisComponents(),
...this.getYRegions(), // ...this.getYRegions(),
...this.getXRegions(), // ...this.getXRegions(),
...this.getYMarkerLines(), // ...this.getYMarkerLines(),
// ...this.getXMarkerLines(), // // ...this.getXMarkerLines(),
...this.getChartComponents(), // ...this.getChartComponents(),
...this.getChartLabels(), // ...this.getChartLabels(),
]; ];
} }
getYAxesComponents() { refreshComponents() {
return [new ChartComponent({ this.refreshYAxis();
layerClass: 'y axis', }
setData: () => {
// let s = this.state;
// data = {}; refreshYAxis() {
let s = this.state;
this.yAxis.refresh({
// return data; positions: s.yAxis.positions,
}, labels: s.yAxis.labels,
initializeData: function() { });
this.axesPositions = this.state
},
make: () => {
// positions, labels, renderer
let s = this.state;
return s.yAxis.positions.map((position, i) =>
this.renderer.yLine(position, s.yAxis.labels[i], {pos:'right'})
);
},
animate: (yLines) => {
// Equilize
let newY = this.state.yAxis.positions;
let oldY = this.oldState.yAxis.positions;
let extra = newY.length - oldY.length;
let lastLine = yLines[yLines.length - 1];
let parentNode = lastLine.parentNode;
[oldY, newY] = equilizeNoOfElements(oldY, newY);
// console.log(newY.slice(), oldY.slice());
if(extra > 0) {
for(var i = 0; i<extra; i++) {
let line = lastLine.cloneNode(true);
parentNode.appendChild(line);
yLines.push(line);
}
}
yLines.map((line, i) => {
// console.log(line, newY[i], oldY[i]);
this.elementsToAnimate.push(this.renderer.translateHoriLine(
line, newY[i], oldY[i]
));
});
}
})];
} }
getXAxisComponents() { getXAxisComponents() {
return new ChartComponent({ return new ChartComponent({
layerClass: 'x axis', layerClass: 'x axis',
setData: () => {}, setData: () => {
make: () => { let s = this.state;
let data = {
positions: s.xAxisPositions,
labels: s.xAxisLabels,
};
let constants = {
mode: this.xAxisMode,
height: this.height
}
return [data, constants];
},
makeElements: () => {
let s = this.state; let s = this.state;
// positions // positions
// TODO: xAxis Label spacing // TODO: xAxis Label spacing
return s.xAxisPositions.map((position, i) => return s.xAxisPositions.map((position, i) =>
this.renderer.xLine(position, s.xAxisLabels[i] xLine(position, s.xAxisLabels[i], this.constants.height
// , {pos:'top'} // , {pos:'top'}
) )
); );
@ -384,7 +372,7 @@ export default class AxisChart extends BaseChart {
layerClass: 'dataset-units dataset-' + index, layerClass: 'dataset-units dataset-' + index,
setData: () => {}, setData: () => {},
preMake: () => { }, preMake: () => { },
make: () => { makeElements: () => {
let d = this.state.datasets[index]; let d = this.state.datasets[index];
return d.positions.map((y, j) => { return d.positions.map((y, j) => {
@ -446,7 +434,7 @@ export default class AxisChart extends BaseChart {
return new ChartComponent({ return new ChartComponent({
layerClass: 'path dataset-path', layerClass: 'path dataset-path',
setData: () => {}, setData: () => {},
make: () => { makeElements: () => {
let d = this.state.datasets[index]; let d = this.state.datasets[index];
let color = this.colors[index]; let color = this.colors[index];
@ -496,7 +484,7 @@ export default class AxisChart extends BaseChart {
return new ChartComponent({ return new ChartComponent({
layerClass: 'y-markers', layerClass: 'y-markers',
setData: () => {}, setData: () => {},
make: () => { makeElements: () => {
let s = this.state; let s = this.state;
return s.yMarkers.map(marker => return s.yMarkers.map(marker =>
this.renderer.yMarker(marker.value, marker.name, this.renderer.yMarker(marker.value, marker.name,
@ -517,7 +505,7 @@ export default class AxisChart extends BaseChart {
return new ChartComponent({ return new ChartComponent({
layerClass: 'y-regions', layerClass: 'y-regions',
setData: () => {}, setData: () => {},
make: () => { makeElements: () => {
let s = this.state; let s = this.state;
return s.yRegions.map(region => return s.yRegions.map(region =>
this.renderer.yRegion(region.start, region.end, region.name) this.renderer.yRegion(region.start, region.end, region.name)
@ -528,10 +516,6 @@ export default class AxisChart extends BaseChart {
}); });
} }
getXRegions() {
return [];
}
refreshRenderer() { refreshRenderer() {
// These args are basically the current state of the chart, // These args are basically the current state of the chart,
// with constant and alive params mixed // with constant and alive params mixed
@ -551,8 +535,6 @@ export default class AxisChart extends BaseChart {
this.renderer.refreshState(state); this.renderer.refreshState(state);
} }
this.refreshComponents();
let meta = { let meta = {
totalHeight: this.height, totalHeight: this.height,
totalWidth: this.width, totalWidth: this.width,

View File

@ -128,11 +128,15 @@ export default class BaseChart {
_setup() { _setup() {
this.bindWindowEvents(); this.bindWindowEvents();
this.setupConstants(); this.setupConstants();
this.setupComponents();
this.setMargins(); this.setMargins();
this.makeContainer(); this.makeContainer();
this.makeTooltip(); // without binding this.makeTooltip(); // without binding
this.calcWidth();
this.makeChartArea();
this.setupComponents();
this.draw(true); this.draw(true);
} }
@ -175,31 +179,13 @@ export default class BaseChart {
bindTooltip() {} bindTooltip() {}
draw(init=false) { draw(init=false) {
// difference from update(): draw the whole object due to groudbreaking event (init, resize, etc.) this.components.forEach(c => c.make()); // or c.build()
// (draw everything, layers, groups, units)
this.calcWidth();
// refresh conponent with chart
this.refresh(this.data);
this.makeChartArea();
this.setComponentParent();
this.makeComponentLayers();
this.renderLegend(); this.renderLegend();
this.setupNavigation(init); this.setupNavigation(init);
// first time plain render, so no rerender // TODO: remove timeout and decrease post animate time in chart component
this.renderComponents(); setTimeout(() => {this.update();}, 1000);
this.renderConstants();
if(this.config.animate) this.update(this.firstUpdateData);
}
update(data) {
this.refresh(data);
this.reRender();
} }
calcWidth() { calcWidth() {
@ -215,15 +201,34 @@ export default class BaseChart {
this.width = this.baseWidth - (this.translateXLeft + this.translateXRight); this.width = this.baseWidth - (this.translateXLeft + this.translateXRight);
} }
refresh(data) { //?? refresh? update(data=this.data) {
this.oldState = this.state ? JSON.parse(JSON.stringify(this.state)) : {};
this.intermedState = {}; // use this for the extra position problems?
this.prepareData(data); this.prepareData(data);
this.reCalc(); this.calc(); // builds state
this.refreshRenderer(); this.refreshRenderer();
this.render();
} }
prepareData() {}
renderConstants() {}
calc() {} // builds state
refreshRenderer() {
this.renderer = {};
}
render(animate=true) {
this.refreshComponents();
this.elementsToAnimate = [].concat.apply([], this.components.map(c => c.update(animate)));
console.log(this.elementsToAnimate);
if(this.elementsToAnimate) {
runSMILAnimation(this.chartWrapper, this.svg, this.elementsToAnimate);
}
}
refreshComponents() {}
makeChartArea() { makeChartArea() {
this.svg = makeSVGContainer( this.svg = makeSVGContainer(
this.chartWrapper, this.chartWrapper,
@ -247,41 +252,6 @@ export default class BaseChart {
); );
} }
prepareData() {}
renderConstants() {}
reCalc() {}
// Will update values(state)
// Will recalc specific parts depending on the update
refreshRenderer() {
this.renderer = {};
}
reRender(animate=true) {
if(!animate) {
this.renderComponents();
return;
}
this.elementsToAnimate = [];
this.loadAnimatedComponents();
runSMILAnimation(this.chartWrapper, this.svg, this.elementsToAnimate);
setTimeout(() => {
this.renderComponents();
}, 400);
// TODO: should be max anim duration required
// (opt, should not redraw if still in animate?)
}
// convenient component array abstractions
setComponentParent() { this.components.forEach(c => c.setupParent(this.drawArea)); };
makeComponentLayers() { this.components.forEach(c => c.makeLayer()); }
renderComponents() { this.components.forEach(c => c.render()); }
loadAnimatedComponents() { this.components.forEach(c => c.loadAnimatedComponents()); }
refreshComponents() { this.components.forEach(c => c.refresh(this.state, this.rawChartArgs)); }
renderLegend() {} renderLegend() {}
setupNavigation(init=false) { setupNavigation(init=false) {

View File

@ -1,5 +1,5 @@
import AxisChart from './AxisChart'; import AxisChart from './AxisChart';
import { ChartComponent } from '../objects/ChartComponent'; // import { ChartComponent } from '../objects/ChartComponents';
import { makeSVGGroup, makePath, makeGradient } from '../utils/draw'; import { makeSVGGroup, makePath, makeGradient } from '../utils/draw';
import { equilizeNoOfElements } from '../utils/draw-utils'; import { equilizeNoOfElements } from '../utils/draw-utils';

View File

@ -1,6 +1,6 @@
import AxisChart from './AxisChart'; import AxisChart from './AxisChart';
import { Y_AXIS_MARGIN } from '../utils/margins'; import { Y_AXIS_MARGIN } from '../utils/margins';
import { ChartComponent } from '../objects/ChartComponent'; // import { ChartComponent } from '../objects/ChartComponents';
import { floatTwo } from '../utils/helpers'; import { floatTwo } from '../utils/helpers';
export default class MultiAxisChart extends AxisChart { export default class MultiAxisChart extends AxisChart {

View File

@ -89,38 +89,40 @@ export default class PercentageChart extends BaseChart {
}); });
} }
bindTooltip() { calc() {}
this.slices.map((slice, i) => {
slice.addEventListener('mouseenter', () => {
let g_off = getOffset(this.chartWrapper), p_off = getOffset(slice);
let x = p_off.left - g_off.left + slice.offsetWidth/2; // bindTooltip() {
let y = p_off.top - g_off.top - 6; // this.slices.map((slice, i) => {
let title = (this.formatted_labels && this.formatted_labels.length>0 // slice.addEventListener('mouseenter', () => {
? this.formatted_labels[i] : this.labels[i]) + ': '; // let g_off = getOffset(this.chartWrapper), p_off = getOffset(slice);
let percent = (this.slice_totals[i]*100/this.grand_total).toFixed(1);
this.tip.set_values(x, y, title, percent + "%"); // let x = p_off.left - g_off.left + slice.offsetWidth/2;
this.tip.show_tip(); // let y = p_off.top - g_off.top - 6;
}); // let title = (this.formatted_labels && this.formatted_labels.length>0
}); // ? this.formatted_labels[i] : this.labels[i]) + ': ';
} // let percent = (this.slice_totals[i]*100/this.grand_total).toFixed(1);
renderLegend() { // this.tip.set_values(x, y, title, percent + "%");
let x_values = this.formatted_labels && this.formatted_labels.length > 0 // this.tip.show_tip();
? this.formatted_labels : this.labels; // });
this.legend_totals.map((d, i) => { // });
if(d) { // }
let stats = $.create('div', {
className: 'stats', // renderLegend() {
inside: this.statsWrapper // let x_values = this.formatted_labels && this.formatted_labels.length > 0
}); // ? this.formatted_labels : this.labels;
stats.innerHTML = `<span class="indicator"> // this.legend_totals.map((d, i) => {
<i style="background: ${this.colors[i]}"></i> // if(d) {
<span class="text-muted">${x_values[i]}:</span> // let stats = $.create('div', {
${d} // className: 'stats',
</span>`; // inside: this.statsWrapper
} // });
}); // stats.innerHTML = `<span class="indicator">
} // <i style="background: ${this.colors[i]}"></i>
// <span class="text-muted">${x_values[i]}:</span>
// ${d}
// </span>`;
// }
// });
// }
} }

View File

@ -1,63 +0,0 @@
import { makeSVGGroup } from '../utils/draw';
export class ChartComponent {
constructor({
layerClass = '',
layerTransform = '',
initData,
// called on update
setData,
preMake,
make,
postMake,
animate
}) {
this.layerClass = layerClass;
this.layerTransform = layerTransform;
this.initData = initData;
this.setData = setData;
this.preMake = preMake;
this.make = make;
this.postMake = postMake;
this.animate = animate;
this.layer = undefined;
this.store = [];
}
refresh(state, args) {
this.meta = Object.assign((this.meta || {}), args);
this.state = state;
}
render() {
this.data = this.setData(); // The only without this function?
this.preMake && this.preMake();
this.store = this.make();
this.layer.textContent = '';
this.store.forEach(element => {
this.layer.appendChild(element);
});
this.postMake && this.postMake();
}
setupParent(parent) {
this.parent = parent;
}
loadAnimatedComponents() {
this.animate(this.store);
}
makeLayer() {
this.layer = makeSVGGroup(this.parent, this.layerClass, this.layerTransform);
}
}

View File

@ -0,0 +1,109 @@
import { makeSVGGroup } from '../utils/draw';
import { yLine } from '../utils/draw';
import { equilizeNoOfElements } from '../utils/draw-utils';
import { Animator, translateHoriLine } from '../utils/animate';
class ChartComponent {
constructor({
layerClass = '',
layerTransform = '',
parent,
constants,
data,
// called on update
preMake,
makeElements,
postMake,
animateElements
}) {
this.parent = parent;
this.layerClass = layerClass;
this.layerTransform = layerTransform;
this.constants = constants;
this.preMake = preMake;
this.makeElements = makeElements;
this.postMake = postMake;
this.animateElements = animateElements;
this.store = [];
this.layer = makeSVGGroup(this.parent, this.layerClass, this.layerTransform);
this.data = data;
this.make();
}
refresh(data) {
this.data = data;
}
make() {
this.preMake && this.preMake();
this.render(this.data);
this.postMake && this.postMake();
this.oldData = this.data;
}
render(data) {
this.store = this.makeElements(data);
this.layer.textContent = '';
this.store.forEach(element => {
this.layer.appendChild(element);
});
}
update(animate = true) {
let animateElements = []
if(animate) {
animateElements = this.animateElements(this.data);
}
// TODO: Can we remove this?
setTimeout(() => {
this.make();
}, 1400);
return animateElements;
}
}
export function getYAxisComponent(parent, constants, initData) {
return new ChartComponent({
parent: parent,
layerClass: 'y axis',
constants: constants,
data: initData,
makeElements: function(data) {
return data.positions.map((position, i) =>
yLine(position, data.labels[i], this.constants.width,
{mode: this.constants.mode, pos: this.constants.pos})
);
},
animateElements: function(newData) {
let newPos = newData.positions;
let newLabels = newData.labels;
let oldPos = this.oldData.positions;
let oldLabels = this.oldData.labels;
let extra = newPos.length - oldPos.length;
[oldPos, newPos] = equilizeNoOfElements(oldPos, newPos);
[oldLabels, newLabels] = equilizeNoOfElements(oldLabels, newLabels);
this.render({
positions: oldPos,
labels: newLabels
});
return this.store.map((line, i) => {
return translateHoriLine(
line, newPos[i], oldPos[i]
);
});
}
})
}

View File

@ -7,6 +7,25 @@ export const REPLACE_ALL_NEW_DUR = 250;
export const STD_EASING = 'easein'; export const STD_EASING = 'easein';
export function translate(unit, oldCoord, newCoord, duration) {
return [
unit,
{transform: newCoord.join(', ')},
duration,
STD_EASING,
"translate",
{transform: oldCoord.join(', ')}
];
}
export function translateVertLine(xLine, newX, oldX) {
return translate(xLine, [oldX, 0], [newX, 0], MARKER_LINE_ANIM_DUR);
}
export function translateHoriLine(yLine, newY, oldY) {
return translate(yLine, [0, oldY], [0, newY], MARKER_LINE_ANIM_DUR);
}
export var Animator = (function() { export var Animator = (function() {
var Animator = function(totalHeight, totalWidth, zeroLine, avgUnitWidth) { var Animator = function(totalHeight, totalWidth, zeroLine, avgUnitWidth) {
// constants // constants
@ -54,25 +73,6 @@ export var Animator = (function() {
} }
return pathComponents; return pathComponents;
},
translate: function(obj, oldCoord, newCoord, duration) {
return [
{unit: obj, array: [0], index: 0},
{transform: newCoord.join(', ')},
duration,
STD_EASING,
"translate",
{transform: oldCoord.join(', ')}
];
},
translateVertLine: function(xLine, newX, oldX) {
return this.translate(xLine, [oldX, 0], [newX, 0], MARKER_LINE_ANIM_DUR);
},
translateHoriLine: function(yLine, newY, oldY) {
return this.translate(yLine, [0, oldY], [0, newY], MARKER_LINE_ANIM_DUR);
} }
}; };

View File

@ -208,8 +208,8 @@ function makeHoriLine(y, label, x1, x2, options={}) {
className: className, className: className,
x1: x1, x1: x1,
x2: x2, x2: x2,
y1: y, y1: 0,
y2: y, y2: 0,
styles: { styles: {
stroke: options.stroke stroke: options.stroke
} }
@ -217,7 +217,7 @@ function makeHoriLine(y, label, x1, x2, options={}) {
let text = createSVG('text', { let text = createSVG('text', {
x: x1 < x2 ? x1 - LABEL_MARGIN : x1 + LABEL_MARGIN, x: x1 < x2 ? x1 - LABEL_MARGIN : x1 + LABEL_MARGIN,
y: y, y: 0,
dy: (FONT_SIZE / 2 - 2) + 'px', dy: (FONT_SIZE / 2 - 2) + 'px',
'font-size': FONT_SIZE + 'px', 'font-size': FONT_SIZE + 'px',
'text-anchor': x1 < x2 ? 'end' : 'start', 'text-anchor': x1 < x2 ? 'end' : 'start',
@ -225,6 +225,7 @@ function makeHoriLine(y, label, x1, x2, options={}) {
}); });
let line = createSVG('g', { let line = createSVG('g', {
transform: `translate(0, ${y})`,
'stroke-opacity': 1 'stroke-opacity': 1
}); });
@ -238,6 +239,33 @@ function makeHoriLine(y, label, x1, x2, options={}) {
return line; return line;
} }
export function yLine(y, label, width, options={}) {
if(!options.pos) options.pos = 'left';
if(!options.offset) options.offset = 0;
if(!options.mode) options.mode = 'span';
if(!options.stroke) options.stroke = BASE_LINE_COLOR;
if(!options.className) options.className = '';
let x1 = -1 * AXIS_TICK_LENGTH;
let x2 = options.mode === 'span' ? width + AXIS_TICK_LENGTH : 0;
if(options.mode === 'tick' && options.pos === 'right') {
x1 = width + AXIS_TICK_LENGTH
x2 = width;
}
let offset = options.pos === 'left' ? -1 * options.offset : options.offset;
x1 += options.offset;
x2 += options.offset;
return makeHoriLine(y, label, x1, x2, {
stroke: options.stroke,
className: options.className,
lineType: options.lineType
});
}
export class AxisChartRenderer { export class AxisChartRenderer {
constructor(state) { constructor(state) {
this.refreshState(state); this.refreshState(state);
@ -259,7 +287,7 @@ export class AxisChartRenderer {
xLine(x, label, options={}) { xLine(x, label, options={}) {
if(!options.pos) options.pos = 'bottom'; if(!options.pos) options.pos = 'bottom';
if(!options.offset) options.offset = 0; if(!options.offset) options.offset = 0;
if(!options.mode) options.mode = this.xAxisMode; if(!options.mode) options.mode = 'span';
if(!options.stroke) options.stroke = BASE_LINE_COLOR; if(!options.stroke) options.stroke = BASE_LINE_COLOR;
if(!options.className) options.className = ''; if(!options.className) options.className = '';
@ -290,32 +318,7 @@ export class AxisChartRenderer {
}); });
} }
yLine(y, label, options={}) {
if(!options.pos) options.pos = 'left';
if(!options.offset) options.offset = 0;
if(!options.mode) options.mode = this.yAxisMode;
if(!options.stroke) options.stroke = BASE_LINE_COLOR;
if(!options.className) options.className = '';
let x1 = -1 * AXIS_TICK_LENGTH;
let x2 = options.mode === 'span' ? this.totalWidth + AXIS_TICK_LENGTH : 0;
if(options.mode === 'tick' && options.pos === 'right') {
x1 = this.totalWidth + AXIS_TICK_LENGTH
x2 = this.totalWidth;
}
let offset = options.pos === 'left' ? -1 * options.offset : options.offset;
x1 += options.offset;
x2 += options.offset;
return makeHoriLine(y, label, x1, x2, {
stroke: options.stroke,
className: options.className,
lineType: options.lineType
});
}
xMarker() {} xMarker() {}
@ -445,23 +448,4 @@ export class AxisChartRenderer {
return pathComponents; return pathComponents;
} }
translate(unit, oldCoord, newCoord, duration) {
return [
unit,
{transform: newCoord.join(', ')},
duration,
STD_EASING,
"translate",
{transform: oldCoord.join(', ')}
];
}
translateVertLine(xLine, newX, oldX) {
return this.translate(xLine, [oldX, 0], [newX, 0], MARKER_LINE_ANIM_DUR);
}
translateHoriLine(yLine, newY, oldY) {
return this.translate(yLine, [0, oldY], [0, newY], MARKER_LINE_ANIM_DUR);
}
} }

View File

@ -21,7 +21,7 @@ function normalize(x) {
return [sig * man, exp]; return [sig * man, exp];
} }
function getRangeIntervals(max, min=0) { function getChartRangeIntervals(max, min=0) {
let upperBound = Math.ceil(max); let upperBound = Math.ceil(max);
let lowerBound = Math.floor(min); let lowerBound = Math.floor(min);
let range = upperBound - lowerBound; let range = upperBound - lowerBound;
@ -59,19 +59,19 @@ function getRangeIntervals(max, min=0) {
return intervals; return intervals;
} }
function getIntervals(maxValue, minValue=0) { function getChartIntervals(maxValue, minValue=0) {
let [normalMaxValue, exponent] = normalize(maxValue); let [normalMaxValue, exponent] = normalize(maxValue);
let normalMinValue = minValue ? minValue/Math.pow(10, exponent): 0; let normalMinValue = minValue ? minValue/Math.pow(10, exponent): 0;
// Allow only 7 significant digits // Allow only 7 significant digits
normalMaxValue = normalMaxValue.toFixed(6); normalMaxValue = normalMaxValue.toFixed(6);
let intervals = getRangeIntervals(normalMaxValue, normalMinValue); let intervals = getChartRangeIntervals(normalMaxValue, normalMinValue);
intervals = intervals.map(value => value * Math.pow(10, exponent)); intervals = intervals.map(value => value * Math.pow(10, exponent));
return intervals; return intervals;
} }
export function calcIntervals(values, withMinimum=false) { export function calcChartIntervals(values, withMinimum=false) {
//*** Where the magic happens *** //*** Where the magic happens ***
// Calculates best-fit y intervals from given values // Calculates best-fit y intervals from given values
@ -84,7 +84,7 @@ export function calcIntervals(values, withMinimum=false) {
let exponent = 0, intervals = []; // eslint-disable-line no-unused-vars let exponent = 0, intervals = []; // eslint-disable-line no-unused-vars
function getPositiveFirstIntervals(maxValue, absMinValue) { function getPositiveFirstIntervals(maxValue, absMinValue) {
let intervals = getIntervals(maxValue); let intervals = getChartIntervals(maxValue);
let intervalSize = intervals[1] - intervals[0]; let intervalSize = intervals[1] - intervals[0];
@ -102,9 +102,9 @@ export function calcIntervals(values, withMinimum=false) {
if(maxValue >= 0 && minValue >= 0) { if(maxValue >= 0 && minValue >= 0) {
exponent = normalize(maxValue)[1]; exponent = normalize(maxValue)[1];
if(!withMinimum) { if(!withMinimum) {
intervals = getIntervals(maxValue); intervals = getChartIntervals(maxValue);
} else { } else {
intervals = getIntervals(maxValue, minValue); intervals = getChartIntervals(maxValue, minValue);
} }
} }
@ -142,9 +142,9 @@ export function calcIntervals(values, withMinimum=false) {
exponent = normalize(pseudoMaxValue)[1]; exponent = normalize(pseudoMaxValue)[1];
if(!withMinimum) { if(!withMinimum) {
intervals = getIntervals(pseudoMaxValue); intervals = getChartIntervals(pseudoMaxValue);
} else { } else {
intervals = getIntervals(pseudoMaxValue, pseudoMinValue); intervals = getChartIntervals(pseudoMaxValue, pseudoMinValue);
} }
intervals = intervals.reverse().map(d => d * (-1)); intervals = intervals.reverse().map(d => d * (-1));
@ -174,6 +174,18 @@ export function getZeroIndex(yPts) {
return zeroIndex; return zeroIndex;
} }
export function getRealIntervals(max, noOfIntervals, min = 0, asc = 1) {
let range = max - min;
let part = range * 1.0 / noOfIntervals;
let intervals = [];
for(var i = 0; i <= noOfIntervals; i++) {
intervals.push(min + part * i);
}
return asc ? intervals : intervals.reverse();
}
export function getIntervalSize(orderedArray) { export function getIntervalSize(orderedArray) {
return orderedArray[1] - orderedArray[0]; return orderedArray[1] - orderedArray[0];
} }