animate x axis

This commit is contained in:
pratu16x7 2017-11-21 04:42:31 +05:30
parent af99b4a40b
commit 9b55957350
10 changed files with 556 additions and 670 deletions

View File

@ -115,14 +115,7 @@ function floatTwo(d) {
* @param {Array} arr1 First array
* @param {Array} arr2 Second array
*/
function arraysEqual(arr1, arr2) {
if(arr1.length !== arr2.length) return false;
let areEqual = true;
arr1.map((d, i) => {
if(arr2[i] !== d) areEqual = false;
});
return areEqual;
}
/**
* Shuffles array in place. ES6 version
@ -179,15 +172,47 @@ function getBarHeightAndYAttr(yTop, zeroLine, totalHeight) {
return [height, y];
}
function equilizeNoOfPositions(oldPos, newPos,
extra_count=newPos.length - oldPos.length) {
function equilizeNoOfElements(array1, array2,
extra_count=array2.length - array1.length) {
if(extra_count > 0) {
oldPos = fillArray(oldPos, extra_count);
array1 = fillArray(array1, extra_count);
} else {
newPos = fillArray(newPos, extra_count);
array2 = fillArray(array2, extra_count);
}
return [oldPos, newPos];
return [array1, array2];
}
function getXLineProps(total_height, mode) {
let start_at, height, text_start_at, axis_line_class = '';
if(mode === 'span') { // long spanning lines
start_at = -7;
height = total_height + 15;
text_start_at = total_height + 25;
} else if(mode === 'tick'){ // short label lines
start_at = total_height;
height = 6;
text_start_at = 9;
axis_line_class = 'x-axis-label';
}
return [start_at, height, text_start_at, axis_line_class];
}
function getYLineProps(total_width, mode, specific=false) {
if(specific) {
return[total_width, total_width + 5, 'specific-value', 0];
}
let width, text_end_at = -9, axis_line_class = '', start_at = 0;
if(mode === 'span') { // long spanning lines
width = total_width + 6;
start_at = -6;
} else if(mode === 'tick'){ // short label lines
width = -6;
axis_line_class = 'y-axis-label';
}
return [width, text_end_at, axis_line_class, start_at];
}
function $$2(expr, con) {
@ -323,7 +348,7 @@ function makeText(className, x, y, content) {
});
}
function makeXLine$1(height, textStartAt, point, labelClass, axisLineClass, xPos) {
function makeXLine(height, textStartAt, point, labelClass, axisLineClass, xPos) {
let line = createSVG('line', {
x1: 0,
x2: 0,
@ -1296,6 +1321,10 @@ class AxisChart extends BaseChart {
if(this.zero_line) this.old_zero_line = this.zero_line;
this.zero_line = this.height - (zero_index * interval_height);
if(!this.old_zero_line) this.old_zero_line = this.zero_line;
// Make positions arrays for y elements
this.y_axis_positions = this.y_axis_values.map(d => this.zero_line - d * this.multiplier);
this.yAnnotationPositions = this.specific_values.map(d => this.zero_line - d * this.multiplier);
}
setup_components() {
@ -1325,40 +1354,25 @@ class AxisChart extends BaseChart {
}
make_graph_components(init=false) {
this.make_y_axis();
this.make_x_axis();
this.makeYLines(this.y_axis_positions, this.y_axis_values);
this.makeXLines(this.x_axis_positions, this.x);
this.draw_graph(init);
this.make_y_specifics();
this.make_y_specifics(this.yAnnotationPositions, this.specific_values);
}
// make VERTICAL lines for x values
make_x_axis(animate=false) {
let char_width = 8;
let start_at, height, text_start_at, axis_line_class = '';
if(this.x_axis_mode === 'span') { // long spanning lines
start_at = -7;
height = this.height + 15;
text_start_at = this.height + 25;
} else if(this.x_axis_mode === 'tick'){ // short label lines
start_at = this.height;
height = 6;
text_start_at = 9;
axis_line_class = 'x-axis-label';
}
makeXLines(positions, values) {
let [start_at, height, text_start_at, axis_line_class] = getXLineProps(this.height, this.x_axis_mode);
this.x_axis_group.setAttribute('transform', `translate(0,${start_at})`);
if(animate) {
this.make_anim_x_axis(height, text_start_at, axis_line_class);
return;
}
let char_width = 8;
let allowed_space = this.avg_unit_width * 1.5;
let allowed_letters = allowed_space / 8;
this.xAxisLines = [];
this.x_axis_group.textContent = '';
this.x.map((point, i) => {
let space_taken = getStringWidth(point, char_width) + 2;
values.map((value, i) => {
let space_taken = getStringWidth(value, char_width) + 2;
if(space_taken > allowed_space) {
if(this.is_series) {
// Skip some axis lines if X axis is a series
@ -1370,34 +1384,32 @@ class AxisChart extends BaseChart {
return;
}
} else {
point = point.slice(0, allowed_letters-3) + " ...";
value = value.slice(0, allowed_letters-3) + " ...";
}
}
this.x_axis_group.appendChild(
makeXLine$1(
height,
text_start_at,
point,
'x-value-text',
axis_line_class,
this.x_axis_positions[i]
)
let xLine = makeXLine(
height,
text_start_at,
value,
'x-value-text',
axis_line_class,
positions[i]
);
this.xAxisLines.push(xLine);
this.x_axis_group.appendChild(xLine);
});
}
// make HORIZONTAL lines for y values
make_y_axis(animate=false) {
if(animate) {
this.make_anim_y_axis();
this.make_anim_y_specifics();
return;
}
makeYLines(positions, values) {
let [width, text_end_at, axis_line_class, start_at] = this.get_y_axis_line_props();
let [width, text_end_at, axis_line_class, start_at] = getYLineProps(
this.width, this.y_axis_mode);
this.yAxisLines = [];
this.y_axis_group.textContent = '';
this.y_axis_values.map((value, i) => {
values.map((value, i) => {
this.y_axis_group.appendChild(
makeYLine(
start_at,
@ -1406,27 +1418,30 @@ class AxisChart extends BaseChart {
value,
'y-value-text',
axis_line_class,
this.zero_line - value * this.multiplier,
positions[i],
(value === 0 && i !== 0) // Non-first Zero line
)
);
});
}
get_y_axis_line_props(specific=false) {
if(specific) {
return[this.width, this.width + 5, 'specific-value', 0];
}
let width, text_end_at = -9, axis_line_class = '', start_at = 0;
if(this.y_axis_mode === 'span') { // long spanning lines
width = this.width + 6;
start_at = -6;
} else if(this.y_axis_mode === 'tick'){ // short label lines
width = -6;
axis_line_class = 'y-axis-label';
}
return [width, text_end_at, axis_line_class, start_at];
make_y_specifics(positions, value_objs) {
this.specific_y_group.textContent = '';
value_objs.map((d, i) => {
this.specific_y_group.appendChild(
makeYLine(
0,
this.width,
this.width + 5,
d.title.toUpperCase(),
'specific-value',
'specific-value',
positions[i],
false,
d.line_type
)
);
});
}
draw_graph(init=false) {
@ -1463,13 +1478,13 @@ class AxisChart extends BaseChart {
});
setTimeout(() => {
this.update_values(data);
this.updateData(data);
}, 350);
}
setup_navigation(init) {
if(init) {
// Hack: defer nav till initial update_values
// Hack: defer nav till initial updateData
setTimeout(() => {
super.setup_navigation(init);
}, 500);
@ -1519,25 +1534,6 @@ class AxisChart extends BaseChart {
}
}
make_y_specifics() {
this.specific_y_group.textContent = '';
this.specific_values.map(d => {
this.specific_y_group.appendChild(
makeYLine(
0,
this.width,
this.width + 5,
d.title.toUpperCase(),
'specific-value',
'specific-value',
this.zero_line - d.value * this.multiplier,
false,
d.line_type
)
);
});
}
bind_tooltip() {
// TODO: could be in tooltip itself, as it is a given functionality for its parent
this.chart_wrapper.addEventListener('mousemove', (e) => {
@ -1598,7 +1594,7 @@ class AxisChart extends BaseChart {
});
// Remake y axis, animate
this.update_values();
this.updateData();
// Then make sum units, don't animate
this.sum_units = [];
@ -1623,7 +1619,7 @@ class AxisChart extends BaseChart {
this.y_sums = [];
this.sum_group.textContent = '';
this.sum_units = [];
this.update_values();
this.updateData();
}
show_averages() {
@ -1641,7 +1637,7 @@ class AxisChart extends BaseChart {
});
});
this.update_values();
this.updateData();
}
hide_averages() {
@ -1656,10 +1652,10 @@ class AxisChart extends BaseChart {
this.specific_values.splice(index, 1);
});
this.update_values();
this.updateData();
}
update_values(new_y, new_x) {
updateData(new_y, new_x) {
if(!new_x) {
new_x = this.x;
}
@ -1684,27 +1680,6 @@ class AxisChart extends BaseChart {
// Got the values? Now begin drawing
this.animator = new Animator(this.height, this.width, this.zero_line, this.avg_unit_width);
// Animate only if positions have changed
if(!arraysEqual(this.x_old_axis_positions, this.x_axis_positions)) {
this.make_x_axis(true);
setTimeout(() => {
if(!this.updating) this.make_x_axis();
}, 350);
}
if(!arraysEqual(this.y_old_axis_values, this.y_axis_values) ||
(this.old_specific_values &&
!arraysEqual(this.old_specific_values, this.specific_values))) {
this.make_y_axis(true);
setTimeout(() => {
if(!this.updating) {
this.make_y_axis();
this.make_y_specifics();
}
}, 350);
}
this.animate_graphs();
// Trigger animation with the animatable elements in this.elements_to_animate
@ -1713,26 +1688,6 @@ class AxisChart extends BaseChart {
this.updating = false;
}
add_data_point(y_point, x_point, index=this.x.length) {
let new_y = this.y.map(data_set => { return {values:data_set.values}; });
new_y.map((d, i) => { d.values.splice(index, 0, y_point[i]); });
let new_x = this.x.slice();
new_x.splice(index, 0, x_point);
this.update_values(new_y, new_x);
}
remove_data_point(index = this.x.length-1) {
if(this.x.length < 3) return;
let new_y = this.y.map(data_set => { return {values:data_set.values}; });
new_y.map((d) => { d.values.splice(index, 1); });
let new_x = this.x.slice();
new_x.splice(index, 1);
this.update_values(new_y, new_x);
}
run_animation() {
let anim_svg = runSVGAnimation(this.svg, this.elements_to_animate);
@ -1754,28 +1709,51 @@ class AxisChart extends BaseChart {
animate_graphs() {
this.y.map(d => {
// Pre-prep, equilize no of positions between old and new
let [old_x, new_x] = equilizeNoOfPositions(
let [old_x, new_x] = equilizeNoOfElements(
this.x_old_axis_positions.slice(),
this.x_axis_positions.slice()
);
let [old_y, new_y] = equilizeNoOfPositions(
let [old_y, new_y] = equilizeNoOfElements(
this.old_y_axis_tops[d.index].slice(),
d.y_tops.slice()
);
if(new_x.length - old_x.length > 0) {
let newYValues = this.y_old_axis_values.slice();
let [oldYAxis, newYAxis] = equilizeNoOfElements(
this.y_old_axis_values.slice(),
this.y_axis_values.slice()
);
let newXValues = this.x.slice();
let extra_points = this.x_axis_positions.slice().length - this.x_old_axis_positions.slice().length;
if(extra_points > 0) {
this.make_path && this.make_path(d, old_x, old_y, this.colors[d.index]);
this.make_new_units_for_dataset(old_x, old_y, this.colors[d.index], d.index, this.y.length);
this.makeXLines(old_x, newXValues);
}
// No Y extra check?
this.makeYLines(oldYAxis, newYValues);
if(extra_points !== 0) {
this.animateXLines(old_x, new_x);
}
d.path && this.animate_path(d, new_x, new_y);
this.animate_units(d, old_x, old_y, new_x, new_y);
this.animate_units(d, new_x, new_y);
this.animateYLines(oldYAxis, newYAxis);
});
// TODO: replace with real units
setTimeout(() => {
this.y.map(d => {
this.make_path && this.make_path(d, this.x_axis_positions, d.y_tops, this.colors[d.index]);
this.make_new_units(d);
this.makeYLines(this.y_axis_positions, this.y_axis_values);
this.makeXLines(this.x_axis_positions, this.x);
// this.make_y_specifics(this.yAnnotationPositions, this.specific_values);
});
}, 400);
}
@ -1786,7 +1764,7 @@ class AxisChart extends BaseChart {
.concat(this.animator['path'](d, newPointsList.join("L")));
}
animate_units(d, old_x, old_y, new_x, new_y) {
animate_units(d, new_x, new_y) {
let type = this.unit_args.type;
d.svg_units.map((unit, i) => {
@ -1801,169 +1779,173 @@ class AxisChart extends BaseChart {
});
}
make_anim_x_axis(height, text_start_at, axis_line_class) {
// Animate X AXIS to account for more or less axis lines
const old_pos = this.x_old_axis_positions;
const new_pos = this.x_axis_positions;
const old_vals = this.old_x_values;
const new_vals = this.x;
const last_line_pos = old_pos[old_pos.length - 1];
let add_and_animate_line = (value, old_pos, new_pos) => {
if(typeof new_pos === 'string') {
new_pos = parseInt(new_pos.substring(0, new_pos.length-1));
}
const x_line = makeXLine$1(
height,
text_start_at,
value, // new value
'x-value-text',
axis_line_class,
old_pos // old position
);
this.x_axis_group.appendChild(x_line);
this.elements_to_animate && this.elements_to_animate.push([
{unit: x_line, array: [0], index: 0},
{transform: `${ new_pos }, 0`},
animateXLines(oldX, newX) {
this.xAxisLines.map((xLine, i) => {
this.elements_to_animate.push([
{unit: xLine, array: [0], index: 0},
{transform: `${ newX[i] }, 0`},
350,
"easein",
"translate",
{transform: `${ old_pos }, 0`}
{transform: `${ oldX[i] }, 0`}
]);
};
this.x_axis_group.textContent = '';
this.make_new_axis_anim_lines(
old_pos,
new_pos,
old_vals,
new_vals,
last_line_pos,
add_and_animate_line
);
}
make_anim_y_axis() {
// Animate Y AXIS to account for more or less axis lines
const old_pos = this.y_old_axis_values.map(value =>
this.zero_line - value * this.multiplier);
const new_pos = this.y_axis_values.map(value =>
this.zero_line - value * this.multiplier);
const old_vals = this.y_old_axis_values;
const new_vals = this.y_axis_values;
const last_line_pos = old_pos[old_pos.length - 1];
this.y_axis_group.textContent = '';
this.make_new_axis_anim_lines(
old_pos,
new_pos,
old_vals,
new_vals,
last_line_pos,
this.add_and_animate_y_line.bind(this),
this.y_axis_group
);
}
make_anim_y_specifics() {
this.specific_y_group.textContent = '';
this.specific_values.map((d) => {
this.add_and_animate_y_line(
d.title,
this.old_zero_line - d.value * this.old_multiplier,
this.zero_line - d.value * this.multiplier,
0,
this.specific_y_group,
d.line_type,
true
);
});
}
make_new_axis_anim_lines(old_pos, new_pos, old_vals, new_vals, last_line_pos, add_and_animate_line, group) {
let superimposed_positions, superimposed_values;
let no_of_extras = new_vals.length - old_vals.length;
if(no_of_extras > 0) {
// More axis are needed
// First make only the superimposed (same position) ones
// Add in the extras at the end later
superimposed_positions = new_pos.slice(0, old_pos.length);
superimposed_values = new_vals.slice(0, old_vals.length);
} else {
// Axis have to be reduced
// Fake it by moving all current extra axis to the last position
// You'll need filler positions and values in the new arrays
const filler_vals = new Array(Math.abs(no_of_extras)).fill("");
superimposed_values = new_vals.concat(filler_vals);
const filler_pos = new Array(Math.abs(no_of_extras)).fill(last_line_pos + "F");
superimposed_positions = new_pos.concat(filler_pos);
}
superimposed_values.map((value, i) => {
add_and_animate_line(value, old_pos[i], superimposed_positions[i], i, group);
animateYLines(oldY, newY) {
this.yAxisLines.map((yLine, i) => {
this.elements_to_animate.push([
{unit: yLine, array: [0], index: 0},
{transform: `0, ${ newY[i] }`},
350,
"easein",
"translate",
{transform: `0, ${ oldY[i] }`}
]);
});
if(no_of_extras > 0) {
// Add in extra axis in the end
// and then animate to new positions
const extra_values = new_vals.slice(old_vals.length);
const extra_positions = new_pos.slice(old_pos.length);
extra_values.map((value, i) => {
add_and_animate_line(value, last_line_pos, extra_positions[i], i, group);
});
}
}
add_and_animate_y_line(value, old_pos, new_pos, i, group, type, specific=false) {
let filler = false;
if(typeof new_pos === 'string') {
new_pos = parseInt(new_pos.substring(0, new_pos.length-1));
filler = true;
}
let new_props = {transform: `0, ${ new_pos }`};
let old_props = {transform: `0, ${ old_pos }`};
animateYAnnotations() {
if(filler) {
new_props['stroke-opacity'] = 0;
// old_props['stroke-opacity'] = 1;
}
}
let [width, text_end_at, axis_line_class, start_at] = this.get_y_axis_line_props(specific);
let axis_label_class = !specific ? 'y-value-text' : 'specific-value';
value = !specific ? value : (value+"").toUpperCase();
const y_line = makeYLine(
start_at,
width,
text_end_at,
value,
axis_label_class,
axis_line_class,
old_pos, // old position
(value === 0 && i !== 0), // Non-first Zero line
type
);
// make_anim_y_axis() {
// // Animate Y AXIS to account for more or less axis lines
group.appendChild(y_line);
// const old_pos = this.y_old_axis_values.map(value =>
// this.zero_line - value * this.multiplier);
// const new_pos = this.y_axis_values.map(value =>
// this.zero_line - value * this.multiplier);
this.elements_to_animate && this.elements_to_animate.push([
{unit: y_line, array: [0], index: 0},
new_props,
350,
"easein",
"translate",
old_props
]);
// const old_vals = this.y_old_axis_values;
// const new_vals = this.y_axis_values;
// const last_line_pos = old_pos[old_pos.length - 1];
// this.y_axis_group.textContent = '';
// this.make_new_axis_anim_lines(
// old_pos,
// new_pos,
// old_vals,
// new_vals,
// last_line_pos,
// this.add_and_animate_y_line.bind(this),
// this.y_axis_group
// );
// }
// make_anim_y_specifics() {
// this.specific_y_group.textContent = '';
// this.specific_values.map((d) => {
// this.add_and_animate_y_line(
// d.title,
// this.old_zero_line - d.value * this.old_multiplier,
// this.zero_line - d.value * this.multiplier,
// 0,
// this.specific_y_group,
// d.line_type,
// true
// );
// });
// }
// make_new_axis_anim_lines(old_pos, new_pos, old_vals, new_vals, last_line_pos, add_and_animate_line, group) {
// let superimposed_positions, superimposed_values;
// let no_of_extras = new_vals.length - old_vals.length;
// if(no_of_extras > 0) {
// // More axis are needed
// // First make only the superimposed (same position) ones
// // Add in the extras at the end later
// superimposed_positions = new_pos.slice(0, old_pos.length);
// superimposed_values = new_vals.slice(0, old_vals.length);
// } else {
// // Axis have to be reduced
// // Fake it by moving all current extra axis to the last position
// // You'll need filler positions and values in the new arrays
// const filler_vals = new Array(Math.abs(no_of_extras)).fill("");
// superimposed_values = new_vals.concat(filler_vals);
// const filler_pos = new Array(Math.abs(no_of_extras)).fill(last_line_pos + "F");
// superimposed_positions = new_pos.concat(filler_pos);
// }
// superimposed_values.map((value, i) => {
// add_and_animate_line(value, old_pos[i], superimposed_positions[i], i, group);
// });
// if(no_of_extras > 0) {
// // Add in extra axis in the end
// // and then animate to new positions
// const extra_values = new_vals.slice(old_vals.length);
// const extra_positions = new_pos.slice(old_pos.length);
// extra_values.map((value, i) => {
// add_and_animate_line(value, last_line_pos, extra_positions[i], i, group);
// });
// }
// }
// add_and_animate_y_line(value, old_pos, new_pos, i, group, type, specific=false) {
// let filler = false;
// if(typeof new_pos === 'string') {
// new_pos = parseInt(new_pos.substring(0, new_pos.length-1));
// filler = true;
// }
// let new_props = {transform: `0, ${ new_pos }`};
// let old_props = {transform: `0, ${ old_pos }`};
// if(filler) {
// new_props['stroke-opacity'] = 0;
// // old_props['stroke-opacity'] = 1;
// }
// let [width, text_end_at, axis_line_class, start_at] = getYLineProps(
// this.width, this.y_axis_mode, specific);
// let axis_label_class = !specific ? 'y-value-text' : 'specific-value';
// value = !specific ? value : (value+"").toUpperCase();
// const y_line = makeYLine(
// start_at,
// width,
// text_end_at,
// value,
// axis_label_class,
// axis_line_class,
// old_pos, // old position
// (value === 0 && i !== 0), // Non-first Zero line
// type
// );
// group.appendChild(y_line);
// this.elements_to_animate && this.elements_to_animate.push([
// {unit: y_line, array: [0], index: 0},
// new_props,
// 350,
// "easein",
// "translate",
// old_props
// ]);
// }
add_data_point(y_point, x_point, index=this.x.length) {
let new_y = this.y.map(data_set => { return {values:data_set.values}; });
new_y.map((d, i) => { d.values.splice(index, 0, y_point[i]); });
let new_x = this.x.slice();
new_x.splice(index, 0, x_point);
this.updateData(new_y, new_x);
}
remove_data_point(index = this.x.length-1) {
if(this.x.length < 3) return;
let new_y = this.y.map(data_set => { return {values:data_set.values}; });
new_y.map((d) => { d.values.splice(index, 1); });
let new_x = this.x.slice();
new_x.splice(index, 1);
this.updateData(new_y, new_x);
}
getDataPoint(index=this.current_index) {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -65,7 +65,7 @@ let line_composite_chart = new Chart ({
});
bar_composite_chart.parent.addEventListener('data-select', (e) => {
line_composite_chart.update_values([more_line_data[e.index]]);
line_composite_chart.updateData([more_line_data[e.index]]);
});
@ -233,7 +233,7 @@ let chart_update_buttons = document.querySelector('.chart-update-buttons');
chart_update_buttons.querySelector('[data-update="random"]').addEventListener("click", (e) => {
shuffle(update_data_all_indices);
update_chart.update_values(
update_chart.updateData(
[{values: get_update_data(update_data_all_values)}],
update_data_all_labels.slice(0, 10)
);

View File

@ -115,7 +115,7 @@
Update Values
</h6>
<pre><code class="hljs javascript"> // Update entire datasets
chart.update_values(
chart.updateData(
[
{values: new_dataset_1_values},
{values: new_dataset_2_values}

View File

@ -1,6 +1,6 @@
import { offset } from '../utils/dom';
import { UnitRenderer, makeXLine, makeYLine } from '../utils/draw';
import { equilizeNoOfPositions } from '../utils/draw-utils';
import { equilizeNoOfElements, getXLineProps, getYLineProps } from '../utils/draw-utils';
import { Animator } from '../utils/animate';
import { runSVGAnimation } from '../utils/animation';
import { calcIntervals } from '../utils/intervals';
@ -99,6 +99,10 @@ export default class AxisChart extends BaseChart {
if(this.zero_line) this.old_zero_line = this.zero_line;
this.zero_line = this.height - (zero_index * interval_height);
if(!this.old_zero_line) this.old_zero_line = this.zero_line;
// Make positions arrays for y elements
this.y_axis_positions = this.y_axis_values.map(d => this.zero_line - d * this.multiplier);
this.yAnnotationPositions = this.specific_values.map(d => this.zero_line - d * this.multiplier);
}
setup_components() {
@ -128,40 +132,25 @@ export default class AxisChart extends BaseChart {
}
make_graph_components(init=false) {
this.make_y_axis();
this.make_x_axis();
this.makeYLines(this.y_axis_positions, this.y_axis_values);
this.makeXLines(this.x_axis_positions, this.x);
this.draw_graph(init);
this.make_y_specifics();
this.make_y_specifics(this.yAnnotationPositions, this.specific_values);
}
// make VERTICAL lines for x values
make_x_axis(animate=false) {
let char_width = 8;
let start_at, height, text_start_at, axis_line_class = '';
if(this.x_axis_mode === 'span') { // long spanning lines
start_at = -7;
height = this.height + 15;
text_start_at = this.height + 25;
} else if(this.x_axis_mode === 'tick'){ // short label lines
start_at = this.height;
height = 6;
text_start_at = 9;
axis_line_class = 'x-axis-label';
}
makeXLines(positions, values) {
let [start_at, height, text_start_at, axis_line_class] = getXLineProps(this.height, this.x_axis_mode);
this.x_axis_group.setAttribute('transform', `translate(0,${start_at})`);
if(animate) {
this.make_anim_x_axis(height, text_start_at, axis_line_class);
return;
}
let char_width = 8;
let allowed_space = this.avg_unit_width * 1.5;
let allowed_letters = allowed_space / 8;
this.xAxisLines = [];
this.x_axis_group.textContent = '';
this.x.map((point, i) => {
let space_taken = getStringWidth(point, char_width) + 2;
values.map((value, i) => {
let space_taken = getStringWidth(value, char_width) + 2;
if(space_taken > allowed_space) {
if(this.is_series) {
// Skip some axis lines if X axis is a series
@ -173,34 +162,32 @@ export default class AxisChart extends BaseChart {
return;
}
} else {
point = point.slice(0, allowed_letters-3) + " ...";
value = value.slice(0, allowed_letters-3) + " ...";
}
}
this.x_axis_group.appendChild(
makeXLine(
height,
text_start_at,
point,
'x-value-text',
axis_line_class,
this.x_axis_positions[i]
)
let xLine = makeXLine(
height,
text_start_at,
value,
'x-value-text',
axis_line_class,
positions[i]
);
this.xAxisLines.push(xLine);
this.x_axis_group.appendChild(xLine);
});
}
// make HORIZONTAL lines for y values
make_y_axis(animate=false) {
if(animate) {
this.make_anim_y_axis();
this.make_anim_y_specifics();
return;
}
makeYLines(positions, values) {
let [width, text_end_at, axis_line_class, start_at] = this.get_y_axis_line_props();
let [width, text_end_at, axis_line_class, start_at] = getYLineProps(
this.width, this.y_axis_mode);
this.yAxisLines = [];
this.y_axis_group.textContent = '';
this.y_axis_values.map((value, i) => {
values.map((value, i) => {
this.y_axis_group.appendChild(
makeYLine(
start_at,
@ -209,27 +196,30 @@ export default class AxisChart extends BaseChart {
value,
'y-value-text',
axis_line_class,
this.zero_line - value * this.multiplier,
positions[i],
(value === 0 && i !== 0) // Non-first Zero line
)
);
});
}
get_y_axis_line_props(specific=false) {
if(specific) {
return[this.width, this.width + 5, 'specific-value', 0];
}
let width, text_end_at = -9, axis_line_class = '', start_at = 0;
if(this.y_axis_mode === 'span') { // long spanning lines
width = this.width + 6;
start_at = -6;
} else if(this.y_axis_mode === 'tick'){ // short label lines
width = -6;
axis_line_class = 'y-axis-label';
}
return [width, text_end_at, axis_line_class, start_at];
make_y_specifics(positions, value_objs) {
this.specific_y_group.textContent = '';
value_objs.map((d, i) => {
this.specific_y_group.appendChild(
makeYLine(
0,
this.width,
this.width + 5,
d.title.toUpperCase(),
'specific-value',
'specific-value',
positions[i],
false,
d.line_type
)
);
});
}
draw_graph(init=false) {
@ -266,13 +256,13 @@ export default class AxisChart extends BaseChart {
});
setTimeout(() => {
this.update_values(data);
this.updateData(data);
}, 350);
}
setup_navigation(init) {
if(init) {
// Hack: defer nav till initial update_values
// Hack: defer nav till initial updateData
setTimeout(() => {
super.setup_navigation(init);
}, 500);
@ -322,25 +312,6 @@ export default class AxisChart extends BaseChart {
}
}
make_y_specifics() {
this.specific_y_group.textContent = '';
this.specific_values.map(d => {
this.specific_y_group.appendChild(
makeYLine(
0,
this.width,
this.width + 5,
d.title.toUpperCase(),
'specific-value',
'specific-value',
this.zero_line - d.value * this.multiplier,
false,
d.line_type
)
);
});
}
bind_tooltip() {
// TODO: could be in tooltip itself, as it is a given functionality for its parent
this.chart_wrapper.addEventListener('mousemove', (e) => {
@ -401,7 +372,7 @@ export default class AxisChart extends BaseChart {
});
// Remake y axis, animate
this.update_values();
this.updateData();
// Then make sum units, don't animate
this.sum_units = [];
@ -426,7 +397,7 @@ export default class AxisChart extends BaseChart {
this.y_sums = [];
this.sum_group.textContent = '';
this.sum_units = [];
this.update_values();
this.updateData();
}
show_averages() {
@ -444,7 +415,7 @@ export default class AxisChart extends BaseChart {
});
});
this.update_values();
this.updateData();
}
hide_averages() {
@ -459,10 +430,10 @@ export default class AxisChart extends BaseChart {
this.specific_values.splice(index, 1);
});
this.update_values();
this.updateData();
}
update_values(new_y, new_x) {
updateData(new_y, new_x) {
if(!new_x) {
new_x = this.x;
}
@ -487,27 +458,6 @@ export default class AxisChart extends BaseChart {
// Got the values? Now begin drawing
this.animator = new Animator(this.height, this.width, this.zero_line, this.avg_unit_width);
// Animate only if positions have changed
if(!arraysEqual(this.x_old_axis_positions, this.x_axis_positions)) {
this.make_x_axis(true);
setTimeout(() => {
if(!this.updating) this.make_x_axis();
}, 350);
}
if(!arraysEqual(this.y_old_axis_values, this.y_axis_values) ||
(this.old_specific_values &&
!arraysEqual(this.old_specific_values, this.specific_values))) {
this.make_y_axis(true);
setTimeout(() => {
if(!this.updating) {
this.make_y_axis();
this.make_y_specifics();
}
}, 350);
}
this.animate_graphs();
// Trigger animation with the animatable elements in this.elements_to_animate
@ -516,26 +466,6 @@ export default class AxisChart extends BaseChart {
this.updating = false;
}
add_data_point(y_point, x_point, index=this.x.length) {
let new_y = this.y.map(data_set => { return {values:data_set.values}; });
new_y.map((d, i) => { d.values.splice(index, 0, y_point[i]); });
let new_x = this.x.slice();
new_x.splice(index, 0, x_point);
this.update_values(new_y, new_x);
}
remove_data_point(index = this.x.length-1) {
if(this.x.length < 3) return;
let new_y = this.y.map(data_set => { return {values:data_set.values}; });
new_y.map((d) => { d.values.splice(index, 1); });
let new_x = this.x.slice();
new_x.splice(index, 1);
this.update_values(new_y, new_x);
}
run_animation() {
let anim_svg = runSVGAnimation(this.svg, this.elements_to_animate);
@ -557,28 +487,51 @@ export default class AxisChart extends BaseChart {
animate_graphs() {
this.y.map(d => {
// Pre-prep, equilize no of positions between old and new
let [old_x, new_x] = equilizeNoOfPositions(
let [old_x, new_x] = equilizeNoOfElements(
this.x_old_axis_positions.slice(),
this.x_axis_positions.slice()
);
let [old_y, new_y] = equilizeNoOfPositions(
let [old_y, new_y] = equilizeNoOfElements(
this.old_y_axis_tops[d.index].slice(),
d.y_tops.slice()
);
if(new_x.length - old_x.length > 0) {
let newYValues = this.y_old_axis_values.slice();
let [oldYAxis, newYAxis] = equilizeNoOfElements(
this.y_old_axis_values.slice(),
this.y_axis_values.slice()
);
let newXValues = this.x.slice();
let extra_points = this.x_axis_positions.slice().length - this.x_old_axis_positions.slice().length;
if(extra_points > 0) {
this.make_path && this.make_path(d, old_x, old_y, this.colors[d.index]);
this.make_new_units_for_dataset(old_x, old_y, this.colors[d.index], d.index, this.y.length);
this.makeXLines(old_x, newXValues);
}
// No Y extra check?
this.makeYLines(oldYAxis, newYValues);
if(extra_points !== 0) {
this.animateXLines(old_x, new_x);
}
d.path && this.animate_path(d, new_x, new_y);
this.animate_units(d, old_x, old_y, new_x, new_y);
this.animate_units(d, new_x, new_y);
this.animateYLines(oldYAxis, newYAxis);
});
// TODO: replace with real units
setTimeout(() => {
this.y.map(d => {
this.make_path && this.make_path(d, this.x_axis_positions, d.y_tops, this.colors[d.index]);
this.make_new_units(d);
this.makeYLines(this.y_axis_positions, this.y_axis_values);
this.makeXLines(this.x_axis_positions, this.x);
// this.make_y_specifics(this.yAnnotationPositions, this.specific_values);
});
}, 400);
}
@ -589,7 +542,7 @@ export default class AxisChart extends BaseChart {
.concat(this.animator['path'](d, newPointsList.join("L")));
}
animate_units(d, old_x, old_y, new_x, new_y) {
animate_units(d, new_x, new_y) {
let type = this.unit_args.type;
d.svg_units.map((unit, i) => {
@ -604,169 +557,173 @@ export default class AxisChart extends BaseChart {
});
}
make_anim_x_axis(height, text_start_at, axis_line_class) {
// Animate X AXIS to account for more or less axis lines
const old_pos = this.x_old_axis_positions;
const new_pos = this.x_axis_positions;
const old_vals = this.old_x_values;
const new_vals = this.x;
const last_line_pos = old_pos[old_pos.length - 1];
let add_and_animate_line = (value, old_pos, new_pos) => {
if(typeof new_pos === 'string') {
new_pos = parseInt(new_pos.substring(0, new_pos.length-1));
}
const x_line = makeXLine(
height,
text_start_at,
value, // new value
'x-value-text',
axis_line_class,
old_pos // old position
);
this.x_axis_group.appendChild(x_line);
this.elements_to_animate && this.elements_to_animate.push([
{unit: x_line, array: [0], index: 0},
{transform: `${ new_pos }, 0`},
animateXLines(oldX, newX) {
this.xAxisLines.map((xLine, i) => {
this.elements_to_animate.push([
{unit: xLine, array: [0], index: 0},
{transform: `${ newX[i] }, 0`},
350,
"easein",
"translate",
{transform: `${ old_pos }, 0`}
{transform: `${ oldX[i] }, 0`}
]);
};
this.x_axis_group.textContent = '';
this.make_new_axis_anim_lines(
old_pos,
new_pos,
old_vals,
new_vals,
last_line_pos,
add_and_animate_line
);
}
make_anim_y_axis() {
// Animate Y AXIS to account for more or less axis lines
const old_pos = this.y_old_axis_values.map(value =>
this.zero_line - value * this.multiplier);
const new_pos = this.y_axis_values.map(value =>
this.zero_line - value * this.multiplier);
const old_vals = this.y_old_axis_values;
const new_vals = this.y_axis_values;
const last_line_pos = old_pos[old_pos.length - 1];
this.y_axis_group.textContent = '';
this.make_new_axis_anim_lines(
old_pos,
new_pos,
old_vals,
new_vals,
last_line_pos,
this.add_and_animate_y_line.bind(this),
this.y_axis_group
);
}
make_anim_y_specifics() {
this.specific_y_group.textContent = '';
this.specific_values.map((d) => {
this.add_and_animate_y_line(
d.title,
this.old_zero_line - d.value * this.old_multiplier,
this.zero_line - d.value * this.multiplier,
0,
this.specific_y_group,
d.line_type,
true
);
});
}
make_new_axis_anim_lines(old_pos, new_pos, old_vals, new_vals, last_line_pos, add_and_animate_line, group) {
let superimposed_positions, superimposed_values;
let no_of_extras = new_vals.length - old_vals.length;
if(no_of_extras > 0) {
// More axis are needed
// First make only the superimposed (same position) ones
// Add in the extras at the end later
superimposed_positions = new_pos.slice(0, old_pos.length);
superimposed_values = new_vals.slice(0, old_vals.length);
} else {
// Axis have to be reduced
// Fake it by moving all current extra axis to the last position
// You'll need filler positions and values in the new arrays
const filler_vals = new Array(Math.abs(no_of_extras)).fill("");
superimposed_values = new_vals.concat(filler_vals);
const filler_pos = new Array(Math.abs(no_of_extras)).fill(last_line_pos + "F");
superimposed_positions = new_pos.concat(filler_pos);
}
superimposed_values.map((value, i) => {
add_and_animate_line(value, old_pos[i], superimposed_positions[i], i, group);
animateYLines(oldY, newY) {
this.yAxisLines.map((yLine, i) => {
this.elements_to_animate.push([
{unit: yLine, array: [0], index: 0},
{transform: `0, ${ newY[i] }`},
350,
"easein",
"translate",
{transform: `0, ${ oldY[i] }`}
]);
});
if(no_of_extras > 0) {
// Add in extra axis in the end
// and then animate to new positions
const extra_values = new_vals.slice(old_vals.length);
const extra_positions = new_pos.slice(old_pos.length);
extra_values.map((value, i) => {
add_and_animate_line(value, last_line_pos, extra_positions[i], i, group);
});
}
}
add_and_animate_y_line(value, old_pos, new_pos, i, group, type, specific=false) {
let filler = false;
if(typeof new_pos === 'string') {
new_pos = parseInt(new_pos.substring(0, new_pos.length-1));
filler = true;
}
let new_props = {transform: `0, ${ new_pos }`};
let old_props = {transform: `0, ${ old_pos }`};
animateYAnnotations() {
if(filler) {
new_props['stroke-opacity'] = 0;
// old_props['stroke-opacity'] = 1;
}
}
let [width, text_end_at, axis_line_class, start_at] = this.get_y_axis_line_props(specific);
let axis_label_class = !specific ? 'y-value-text' : 'specific-value';
value = !specific ? value : (value+"").toUpperCase();
const y_line = makeYLine(
start_at,
width,
text_end_at,
value,
axis_label_class,
axis_line_class,
old_pos, // old position
(value === 0 && i !== 0), // Non-first Zero line
type
);
// make_anim_y_axis() {
// // Animate Y AXIS to account for more or less axis lines
group.appendChild(y_line);
// const old_pos = this.y_old_axis_values.map(value =>
// this.zero_line - value * this.multiplier);
// const new_pos = this.y_axis_values.map(value =>
// this.zero_line - value * this.multiplier);
this.elements_to_animate && this.elements_to_animate.push([
{unit: y_line, array: [0], index: 0},
new_props,
350,
"easein",
"translate",
old_props
]);
// const old_vals = this.y_old_axis_values;
// const new_vals = this.y_axis_values;
// const last_line_pos = old_pos[old_pos.length - 1];
// this.y_axis_group.textContent = '';
// this.make_new_axis_anim_lines(
// old_pos,
// new_pos,
// old_vals,
// new_vals,
// last_line_pos,
// this.add_and_animate_y_line.bind(this),
// this.y_axis_group
// );
// }
// make_anim_y_specifics() {
// this.specific_y_group.textContent = '';
// this.specific_values.map((d) => {
// this.add_and_animate_y_line(
// d.title,
// this.old_zero_line - d.value * this.old_multiplier,
// this.zero_line - d.value * this.multiplier,
// 0,
// this.specific_y_group,
// d.line_type,
// true
// );
// });
// }
// make_new_axis_anim_lines(old_pos, new_pos, old_vals, new_vals, last_line_pos, add_and_animate_line, group) {
// let superimposed_positions, superimposed_values;
// let no_of_extras = new_vals.length - old_vals.length;
// if(no_of_extras > 0) {
// // More axis are needed
// // First make only the superimposed (same position) ones
// // Add in the extras at the end later
// superimposed_positions = new_pos.slice(0, old_pos.length);
// superimposed_values = new_vals.slice(0, old_vals.length);
// } else {
// // Axis have to be reduced
// // Fake it by moving all current extra axis to the last position
// // You'll need filler positions and values in the new arrays
// const filler_vals = new Array(Math.abs(no_of_extras)).fill("");
// superimposed_values = new_vals.concat(filler_vals);
// const filler_pos = new Array(Math.abs(no_of_extras)).fill(last_line_pos + "F");
// superimposed_positions = new_pos.concat(filler_pos);
// }
// superimposed_values.map((value, i) => {
// add_and_animate_line(value, old_pos[i], superimposed_positions[i], i, group);
// });
// if(no_of_extras > 0) {
// // Add in extra axis in the end
// // and then animate to new positions
// const extra_values = new_vals.slice(old_vals.length);
// const extra_positions = new_pos.slice(old_pos.length);
// extra_values.map((value, i) => {
// add_and_animate_line(value, last_line_pos, extra_positions[i], i, group);
// });
// }
// }
// add_and_animate_y_line(value, old_pos, new_pos, i, group, type, specific=false) {
// let filler = false;
// if(typeof new_pos === 'string') {
// new_pos = parseInt(new_pos.substring(0, new_pos.length-1));
// filler = true;
// }
// let new_props = {transform: `0, ${ new_pos }`};
// let old_props = {transform: `0, ${ old_pos }`};
// if(filler) {
// new_props['stroke-opacity'] = 0;
// // old_props['stroke-opacity'] = 1;
// }
// let [width, text_end_at, axis_line_class, start_at] = getYLineProps(
// this.width, this.y_axis_mode, specific);
// let axis_label_class = !specific ? 'y-value-text' : 'specific-value';
// value = !specific ? value : (value+"").toUpperCase();
// const y_line = makeYLine(
// start_at,
// width,
// text_end_at,
// value,
// axis_label_class,
// axis_line_class,
// old_pos, // old position
// (value === 0 && i !== 0), // Non-first Zero line
// type
// );
// group.appendChild(y_line);
// this.elements_to_animate && this.elements_to_animate.push([
// {unit: y_line, array: [0], index: 0},
// new_props,
// 350,
// "easein",
// "translate",
// old_props
// ]);
// }
add_data_point(y_point, x_point, index=this.x.length) {
let new_y = this.y.map(data_set => { return {values:data_set.values}; });
new_y.map((d, i) => { d.values.splice(index, 0, y_point[i]); });
let new_x = this.x.slice();
new_x.splice(index, 0, x_point);
this.updateData(new_y, new_x);
}
remove_data_point(index = this.x.length-1) {
if(this.x.length < 3) return;
let new_y = this.y.map(data_set => { return {values:data_set.values}; });
new_y.map((d) => { d.values.splice(index, 1); });
let new_x = this.x.slice();
new_x.splice(index, 1);
this.updateData(new_y, new_x);
}
getDataPoint(index=this.current_index) {

View File

@ -1,90 +1,5 @@
import { getBarHeightAndYAttr } from './draw-utils';
let add_and_animate_line = (value, old_pos, new_pos) => {
if(typeof new_pos === 'string') {
new_pos = parseInt(new_pos.substring(0, new_pos.length-1));
}
const x_line = makeXLine(
height,
text_start_at,
value, // new value
'x-value-text',
axis_line_class,
old_pos // old position
);
this.x_axis_group.appendChild(x_line);
this.elements_to_animate && this.elements_to_animate.push([
{unit: x_line, array: [0], index: 0},
{transform: `${ new_pos }, 0`},
350,
"easein",
"translate",
{transform: `${ old_pos }, 0`}
]);
};
export function getAnimXLine(height, text_start_at, axis_line_class) {
// Animate X AXIS to account for more or less axis lines
const old_pos = this.x_old_axis_positions;
const new_pos = this.x_axis_positions;
const old_vals = this.old_x_values;
const new_vals = this.x;
const last_line_pos = old_pos[old_pos.length - 1];
this.x_axis_group.textContent = '';
this.make_new_axis_anim_lines(
old_pos,
new_pos,
old_vals,
new_vals,
last_line_pos,
add_and_animate_line
);
}
export function make_new_axis_anim_lines(old_pos, new_pos, old_vals, new_vals, last_line_pos, add_and_animate_line, group) {
let superimposed_positions, superimposed_values;
let no_of_extras = new_vals.length - old_vals.length;
if(no_of_extras > 0) {
// More axis are needed
// First make only the superimposed (same position) ones
// Add in the extras at the end later
superimposed_positions = new_pos.slice(0, old_pos.length);
superimposed_values = new_vals.slice(0, old_vals.length);
} else {
// Axis have to be reduced
// Fake it by moving all current extra axis to the last position
// You'll need filler positions and values in the new arrays
const filler_vals = new Array(Math.abs(no_of_extras)).fill("");
superimposed_values = new_vals.concat(filler_vals);
const filler_pos = new Array(Math.abs(no_of_extras)).fill(last_line_pos + "F");
superimposed_positions = new_pos.concat(filler_pos);
}
superimposed_values.map((value, i) => {
add_and_animate_line(value, old_pos[i], superimposed_positions[i], i, group);
});
if(no_of_extras > 0) {
// Add in extra axis in the end
// and then animate to new positions
const extra_values = new_vals.slice(old_vals.length);
const extra_positions = new_pos.slice(old_pos.length);
extra_values.map((value, i) => {
add_and_animate_line(value, last_line_pos, extra_positions[i], i, group);
});
}
}
export function getAnimYLine() {}
export var Animator = (function() {
var Animator = function(totalHeight, totalWidth, zeroLine, avgUnitWidth) {
// constants

View File

@ -24,13 +24,45 @@ export function getBarHeightAndYAttr(yTop, zeroLine, totalHeight) {
return [height, y];
}
export function equilizeNoOfPositions(oldPos, newPos,
extra_count=newPos.length - oldPos.length) {
export function equilizeNoOfElements(array1, array2,
extra_count=array2.length - array1.length) {
if(extra_count > 0) {
oldPos = fillArray(oldPos, extra_count);
array1 = fillArray(array1, extra_count);
} else {
newPos = fillArray(newPos, extra_count);
array2 = fillArray(array2, extra_count);
}
return [oldPos, newPos];
return [array1, array2];
}
export function getXLineProps(total_height, mode) {
let start_at, height, text_start_at, axis_line_class = '';
if(mode === 'span') { // long spanning lines
start_at = -7;
height = total_height + 15;
text_start_at = total_height + 25;
} else if(mode === 'tick'){ // short label lines
start_at = total_height;
height = 6;
text_start_at = 9;
axis_line_class = 'x-axis-label';
}
return [start_at, height, text_start_at, axis_line_class];
}
export function getYLineProps(total_width, mode, specific=false) {
if(specific) {
return[total_width, total_width + 5, 'specific-value', 0];
}
let width, text_end_at = -9, axis_line_class = '', start_at = 0;
if(mode === 'span') { // long spanning lines
width = total_width + 6;
start_at = -6;
} else if(mode === 'tick'){ // short label lines
width = -6;
axis_line_class = 'y-axis-label';
}
return [width, text_end_at, axis_line_class, start_at];
}