[refactor] axis lines via renderer

This commit is contained in:
pratu16x7 2017-11-29 23:13:30 +05:30
parent c7f753ea12
commit 863bd45724
17 changed files with 840 additions and 912 deletions

File diff suppressed because it is too large Load Diff

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

@ -151,8 +151,8 @@ let plot_chart_args = {
is_series: 1,
show_dots: 0,
heatline: 1,
x_axis_mode: 'tick',
y_axis_mode: 'span'
xAxisMode: 'tick',
yAxisMode: 'span'
};
new Chart(plot_chart_args);

View File

@ -159,9 +159,9 @@
Plot Trends
</h6>
<pre><code class="hljs javascript"> ...
x_axis_mode: 'tick', // for short label ticks
xAxisMode: 'tick', // for short label ticks
// or 'span' for long spanning vertical axis lines
y_axis_mode: 'span', // for long horizontal lines, or 'tick'
yAxisMode: 'span', // for long horizontal lines, or 'tick'
is_series: 1, // to allow for skipping of X values
...</code></pre>
<div id="chart-trends" class="border"></div>

View File

@ -1,11 +1,11 @@
import { offset } from '../utils/dom';
import { UnitRenderer, makeXLine, makeYLine } from '../utils/draw';
import { equilizeNoOfElements, getXLineProps, getYLineProps } from '../utils/draw-utils';
import BaseChart from './BaseChart';
import { get_offset, fire } from '../utils/dom';
import { AxisChartRenderer } from '../utils/draw';
import { equilizeNoOfElements } from '../utils/draw-utils';
import { Animator } from '../utils/animate';
import { runSMILAnimation } from '../utils/animation';
import { calcIntervals } from '../utils/intervals';
import { floatTwo, getStringWidth } from '../utils/helpers';
import BaseChart from './BaseChart';
import { floatTwo } from '../utils/helpers';
export default class AxisChart extends BaseChart {
constructor(args) {
@ -34,25 +34,35 @@ export default class AxisChart extends BaseChart {
this.xAxisLabels = ['0', '5', '10'];
}
// this should be inherent in BaseChart
getRenderer() {
// These args are basically the current state/config of the chart,
// with constant and alive params mixed
return new AxisChartRenderer(this.height, this.width,
this.zero_line, this.avg_unit_width, this.xAxisMode, this.yAxisMode);
}
setupComponents() {
let self = this;
let renderer = this.getRenderer();
this.yAxis = {
layerClass: 'y axis',
layer: undefined,
make: self.makeYLines,
makeArgs: [self.yAxisPositions, self.yAxisLabels,
self.width, self.y_axis_mode],
makeArgs: [renderer, self.yAxisPositions, self.yAxisLabels],
store: [],
animate: self.animateYLines,
// indexed: 1
// indexed: 1 // ?? As per datasets?
};
this.xAxis = {
layerClass: 'x axis',
layer: undefined,
make: self.makeXLines,
// Need avg_unit_width here
makeArgs: [self.xPositions, self.xAxisLabels,
self.height, self.x_axis_mode, 200, self.is_series],
// TODO: better context of renderer
// TODO: will implement series skip with avgUnitWidth and isSeries later
makeArgs: [renderer, self.xPositions, self.xAxisLabels],
store: [],
animate: self.animateXLines
};
@ -175,87 +185,14 @@ export default class AxisChart extends BaseChart {
// if(!this.oldYAnnotationPositions) this.oldYAnnotationPositions = this.yAnnotationPositions;
}
// setupLayers() {
// super.setupLayers();
makeXLines(renderer, positions, values) {
// TODO: draw as per condition
// // For markers
// this.y_axis_group = this.makeLayer('y axis');
// this.x_axis_group = this.makeLayer('x axis');
// this.specific_y_group = this.makeLayer('specific axis');
// // For Aggregation
// // this.sumGroup = this.makeLayer('data-points');
// // this.averageGroup = this.makeLayer('chart-area');
// this.setupPreUnitLayers && this.setupPreUnitLayers();
// // For Graph points
// this.svg_units_groups = [];
// this.y.map((d, i) => {
// this.svg_units_groups[i] = this.makeLayer(
// 'data-points data-points-' + i);
// });
// }
// renderComponents(init) {
// this.makeYLines(this.yAxisPositions, this.yAxisLabels);
// this.makeXLines(this.xPositions, this.xAxisLabels);
// this.draw_graph(init);
// // this.make_y_specifics(this.yAnnotationPositions, this.specific_values);
// }
makeXLines(positions, values, total_height, mode, avg_unit_width, is_series) {
let [startAt, height, text_start_at,
axis_line_class] = getXLineProps(total_height, mode);
let char_width = 8;
let allowed_space = avg_unit_width * 1.5;
let allowed_letters = allowed_space / 8;
return values.map((value, i) => {
let space_taken = getStringWidth(value, char_width) + 2;
if(space_taken > allowed_space) {
if(is_series) {
// Skip some axis lines if X axis is a series
let skips = 1;
while((space_taken/skips)*2 > allowed_space) {
skips++;
}
if(i % skips !== 0) {
return;
}
} else {
value = value.slice(0, allowed_letters-3) + " ...";
}
}
return makeXLine(
positions[i],
startAt,
height,
text_start_at,
value,
'x-value-text',
axis_line_class
);
});
return positions.map((position, i) => renderer.xLine(position, values[i]));
}
makeYLines(positions, values, totalWidth, mode) {
let [width, text_end_at, axis_line_class,
start_at] = getYLineProps(totalWidth, mode);
return values.map((value, i) => {
return makeYLine(
start_at,
width,
text_end_at,
value,
'y-value-text',
axis_line_class,
positions[i],
(value === 0 && i !== 0) // Non-first Zero line
);
});
makeYLines(renderer, positions, values) {
return positions.map((position, i) => renderer.yLine(position, values[i]));
}
draw_graph(init=false) {
@ -328,10 +265,10 @@ export default class AxisChart extends BaseChart {
units_group.textContent = '';
units_array.length = 0;
let unit_renderer = new UnitRenderer(this.height, this.zero_line, this.avg_unit_width);
let unit_AxisChartRenderer = new AxisChartRenderer(this.height, this.zero_line, this.avg_unit_width);
y_values.map((y, i) => {
let data_unit = unit_renderer[unit.type](
let data_unit = unit_AxisChartRenderer[unit.type](
x_values[i],
y,
unit.args,
@ -352,9 +289,9 @@ export default class AxisChart extends BaseChart {
bind_tooltip() {
// TODO: could be in tooltip itself, as it is a given functionality for its parent
this.chart_wrapper.addEventListener('mousemove', (e) => {
let o = offset(this.chart_wrapper);
let relX = e.pageX - o.left - this.translate_x;
let relY = e.pageY - o.top - this.translate_y;
let offset = get_offset(this.chart_wrapper);
let relX = e.pageX - offset.left - this.translate_x;
let relY = e.pageY - offset.top - this.translate_y;
if(relY < this.height + this.translate_y * 2) {
this.mapTooltipXPosition(relX);
@ -566,7 +503,7 @@ export default class AxisChart extends BaseChart {
if(index >= this.xAxisLabels.length) index = this.xAxisLabels.length - 1;
if(index === this.current_index) return;
this.current_index = index;
$.fire(this.parent, "data-select", this.getDataPoint());
fire(this.parent, "data-select", this.getDataPoint());
}
set_avg_unit_width_and_x_offset() {

View File

@ -5,8 +5,8 @@ export default class BarChart extends AxisChart {
super(args);
this.type = 'bar';
this.x_axis_mode = args.x_axis_mode || 'tick';
this.y_axis_mode = args.y_axis_mode || 'span';
this.xAxisMode = args.xAxisMode || 'tick';
this.yAxisMode = args.yAxisMode || 'span';
this.setup();
}

View File

@ -126,7 +126,7 @@ export default class BaseChart {
}
draw(init=false) {
// (everything, layers, groups, units)
// (draw everything, layers, groups, units)
this.setWidth();
// these both dependent on width >.<, how can this be decoupled
@ -163,15 +163,15 @@ export default class BaseChart {
setWidth() {
let special_values_width = 0;
let char_width = 8;
this.specific_values.map(val => {
let str_width = getStringWidth((val.title + ""), char_width);
if(str_width > special_values_width) {
special_values_width = str_width - 40;
}
});
this.base_width = getElementContentWidth(this.parent) - special_values_width;
this.width = this.base_width - this.translate_x * 2;
// let char_width = 8;
// this.specific_values.map(val => {
// let str_width = getStringWidth((val.title + ""), char_width);
// if(str_width > special_values_width) {
// special_values_width = str_width - 40;
// }
// });
this.baseWidth = getElementContentWidth(this.parent) - special_values_width;
this.width = this.baseWidth - this.translate_x * 2;
}
setupConstants() {}

View File

@ -5,8 +5,8 @@ export default class LineChart extends AxisChart {
constructor(args) {
super(args);
this.x_axis_mode = args.x_axis_mode || 'span';
this.y_axis_mode = args.y_axis_mode || 'span';
this.xAxisMode = args.xAxisMode || 'span';
this.yAxisMode = args.yAxisMode || 'span';
if(args.hasOwnProperty('show_dots')) {
this.show_dots = args.show_dots;

View File

@ -1,5 +1,5 @@
import BaseChart from './BaseChart';
import { $, offset } from '../utils/dom';
import { $, get_offset } from '../utils/dom';
export default class PercentageChart extends BaseChart {
constructor(args) {
@ -92,7 +92,7 @@ export default class PercentageChart extends BaseChart {
bind_tooltip() {
this.slices.map((slice, i) => {
slice.addEventListener('mouseenter', () => {
let g_off = offset(this.chart_wrapper), p_off = offset(slice);
let g_off = get_offset(this.chart_wrapper), p_off = get_offset(slice);
let x = p_off.left - g_off.left + slice.offsetWidth/2;
let y = p_off.top - g_off.top - 6;

View File

@ -1,5 +1,5 @@
import BaseChart from './BaseChart';
import { $, offset } from '../utils/dom';
import { $, get_offset } from '../utils/dom';
import { makePath } from '../utils/draw';
import { lightenDarkenColor } from '../utils/colors';
import { runSMILAnimation, transform } from '../utils/animation';
@ -133,7 +133,7 @@ export default class PieChart extends BaseChart {
if(flag){
transform(path,this.calTranslateByAngle(this.slicesProperties[i]));
path.style.fill = lightenDarkenColor(color,50);
let g_off = offset(this.svg);
let g_off = get_offset(this.svg);
let x = e.pageX - g_off.left + 10;
let y = e.pageY - g_off.top - 10;
let title = (this.formatted_labels && this.formatted_labels.length>0

View File

@ -43,7 +43,7 @@ $.create = (tag, o) => {
return element;
};
export function offset(element) {
export function get_offset(element) {
let rect = element.getBoundingClientRect();
return {
// https://stackoverflow.com/a/7436602/6495043
@ -74,7 +74,7 @@ export function getElementContentWidth(element) {
return element.clientWidth - padding;
}
$.bind = (element, o) => {
export function bind(element, o){
if (element) {
for (var event in o) {
var callback = o[event];
@ -84,9 +84,9 @@ $.bind = (element, o) => {
});
}
}
};
}
$.unbind = (element, o) => {
export function unbind(element, o){
if (element) {
for (var event in o) {
var callback = o[event];
@ -96,9 +96,9 @@ $.unbind = (element, o) => {
});
}
}
};
}
$.fire = (target, type, properties) => {
export function fire(target, type, properties) {
var evt = document.createEvent("HTMLEvents");
evt.initEvent(type, true, true );
@ -108,4 +108,4 @@ $.fire = (target, type, properties) => {
}
return target.dispatchEvent(evt);
};
}

View File

@ -1,8 +1,13 @@
import { fillArray } from '../utils/helpers';
const AXIS_TICK_LENGTH = 6;
const LABEL_MARGIN = 4;
const MIN_BAR_PERCENT_HEIGHT = 0.01;
export function verticalLineProps(start, height, label='down') {
//
}
export function getXLineProps(totalHeight, mode) {
let startAt = totalHeight + 6, height, textStartAt, axisLineClass = '';
if(mode === 'span') { // long spanning lines
@ -19,10 +24,11 @@ export function getXLineProps(totalHeight, mode) {
return [startAt, height, textStartAt, axisLineClass];
}
export function getYLineProps(totalWidth, mode, specific=false) {
if(specific) {
return[totalWidth, totalWidth + 5, 'specific-value', 0];
}
// export function getYLineProps(totalWidth, mode, specific=false) {
export function getYLineProps(totalWidth, mode) {
// if(specific) {
// return[totalWidth, totalWidth + 5, 'specific-value', 0];
// }
let width, text_end_at = -9, axisLineClass = '', startAt = 0;
if(mode === 'span') { // long spanning lines
width = totalWidth + 6;
@ -35,6 +41,27 @@ export function getYLineProps(totalWidth, mode, specific=false) {
return [width, text_end_at, axisLineClass, startAt];
}
// let char_width = 8;
// let allowed_space = avg_unit_width * 1.5;
// let allowed_letters = allowed_space / 8;
// return values.map((value, i) => {
// let space_taken = getStringWidth(value, char_width) + 2;
// if(space_taken > allowed_space) {
// if(is_series) {
// // Skip some axis lines if X axis is a series
// let skips = 1;
// while((space_taken/skips)*2 > allowed_space) {
// skips++;
// }
// if(i % skips !== 0) {
// return;
// }
// } else {
// value = value.slice(0, allowed_letters-3) + " ...";
// }
// }
export function getBarHeightAndYAttr(yTop, zeroLine, totalHeight) {
let height, y;
if (yTop <= zeroLine) {

View File

@ -1,7 +1,10 @@
import { getBarHeightAndYAttr } from './draw-utils';
import { getBarHeightAndYAttr, getXLineProps, getYLineProps } from './draw-utils';
const X_AXIS_LINE_CLASS = 'x-value-text';
const Y_AXIS_LINE_CLASS = 'y-value-text';
const X_LABEL_CLASS = 'x-value-text';
const Y_LABEL_CLASS = 'y-value-text';
// const X_AXIS_LINE_CLASS = 'x-value-text';
// const Y_AXIS_LINE_CLASS = 'y-value-text';
function $(expr, con) {
return typeof expr === "string"? (con || document).querySelector(expr) : expr || null;
@ -136,74 +139,17 @@ export function makeText(className, x, y, content) {
});
}
export function makeXLine(xPos, startAt, height, textStartAt, point, labelClass, axisLineClass) {
let line = createSVG('line', {
x1: 0,
x2: 0,
y1: startAt,
y2: height
});
let text = createSVG('text', {
className: labelClass,
x: 0,
y: textStartAt,
dy: '.71em',
innerHTML: point
});
let xLine = createSVG('g', {
className: `tick ${X_AXIS_LINE_CLASS}`,
transform: `translate(${ xPos }, 0)`
});
xLine.appendChild(line);
xLine.appendChild(text);
return xLine;
}
export function makeYLine(startAt, width, textEndAt, point, labelClass, axisLineClass, yPos, darker=false, lineType="") {
let line = createSVG('line', {
className: lineType === "dashed" ? "dashed": "",
x1: startAt,
x2: width,
y1: 0,
y2: 0
});
let text = createSVG('text', {
className: labelClass,
x: textEndAt,
y: 0,
dy: '.32em',
innerHTML: point+""
});
let yLine = createSVG('g', {
className: `tick ${axisLineClass}`,
transform: `translate(0, ${yPos})`,
'stroke-opacity': 1
});
if(darker) {
line.style.stroke = "rgba(27, 31, 35, 0.6)";
}
yLine.appendChild(line);
yLine.appendChild(text);
return yLine;
}
export var UnitRenderer = (function() {
var UnitRenderer = function(totalHeight, zeroLine, avgUnitWidth) {
export var AxisChartRenderer = (function() {
var AxisChartRenderer = function(totalHeight, totalWidth, zeroLine, avgUnitWidth, xAxisMode, yAxisMode) {
this.totalHeight = totalHeight;
this.totalWidth = totalWidth;
this.zeroLine = zeroLine;
this.avgUnitWidth = avgUnitWidth;
this.xAxisMode = xAxisMode;
this.yAxisMode = yAxisMode;
};
UnitRenderer.prototype = {
AxisChartRenderer.prototype = {
bar: function (x, yTop, args, color, index, datasetIndex, noOfDatasets) {
let totalWidth = this.avgUnitWidth - args.spaceWidth;
let startX = x - totalWidth/2;
@ -232,8 +178,78 @@ export var UnitRenderer = (function() {
cy: y,
r: args.radius
});
}
},
xLine: function(x, label, mode=this.xAxisMode) {
// Draw X axis line in span/tick mode with optional label
let [startAt, height, textStartAt, axisLineClass] = getXLineProps(this.totalHeight, mode);
let l = createSVG('line', {
x1: 0,
x2: 0,
y1: startAt,
y2: height
});
let text = createSVG('text', {
className: X_LABEL_CLASS,
x: 0,
y: textStartAt,
dy: '.71em',
innerHTML: label
});
let line = createSVG('g', {
className: `tick ${axisLineClass}`,
transform: `translate(${ x }, 0)`
});
line.appendChild(l);
line.appendChild(text);
return line;
},
yLine: function(y, label, mode=this.yAxisMode) {
// TODO: stroke type
let lineType = '';
let [width, textEndAt, axisLineClass, startAt] = getYLineProps(this.totalWidth, mode);
let l = createSVG('line', {
className: lineType === "dashed" ? "dashed": "",
x1: startAt,
x2: width,
y1: 0,
y2: 0
});
let text = createSVG('text', {
className: Y_LABEL_CLASS,
x: textEndAt,
y: 0,
dy: '.32em',
innerHTML: label+""
});
let line = createSVG('g', {
className: `tick ${axisLineClass}`,
transform: `translate(0, ${y})`,
'stroke-opacity': 1
});
// if(darker) {
// line.style.stroke = "rgba(27, 31, 35, 0.6)";
// }
line.appendChild(l);
line.appendChild(text);
return line;
},
xRegion: function(x1, x2, label) { },
yRegion: function(y1, y2, label) { }
};
return UnitRenderer;
return AxisChartRenderer;
})();