[animate] y axes
This commit is contained in:
parent
e6014e7e82
commit
b11a78aa31
504
dist/frappe-charts.esm.js
vendored
504
dist/frappe-charts.esm.js
vendored
@ -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;
|
||||||
|
|||||||
2
dist/frappe-charts.min.cjs.js
vendored
2
dist/frappe-charts.min.cjs.js
vendored
File diff suppressed because one or more lines are too long
2
dist/frappe-charts.min.esm.js
vendored
2
dist/frappe-charts.min.esm.js
vendored
File diff suppressed because one or more lines are too long
3
dist/frappe-charts.min.iife.js
vendored
3
dist/frappe-charts.min.iife.js
vendored
File diff suppressed because one or more lines are too long
1
dist/frappe-charts.min.iife.js.map
vendored
Normal file
1
dist/frappe-charts.min.iife.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
3
docs/assets/js/frappe-charts.min.js
vendored
3
docs/assets/js/frappe-charts.min.js
vendored
File diff suppressed because one or more lines are too long
1
docs/assets/js/frappe-charts.min.js.map
Normal file
1
docs/assets/js/frappe-charts.min.js.map
Normal file
File diff suppressed because one or more lines are too long
@ -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
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -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',
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
@ -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';
|
||||||
|
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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>`;
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
109
src/js/objects/ChartComponents.js
Normal file
109
src/js/objects/ChartComponents.js
Normal 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]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
@ -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);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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];
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user