瀏覽代碼

in bars stage

tags/1.2.0
Prateeksha Singh 7 年之前
父節點
當前提交
4ca6166c8d
共有 12 個文件被更改,包括 263 次插入461 次删除
  1. +117
    -344
      dist/frappe-charts.esm.js
  2. +1
    -1
      dist/frappe-charts.min.cjs.js
  3. +1
    -1
      dist/frappe-charts.min.esm.js
  4. +1
    -1
      dist/frappe-charts.min.iife.js
  5. +1
    -1
      dist/frappe-charts.min.iife.js.map
  6. +1
    -1
      docs/assets/js/frappe-charts.min.js
  7. +1
    -1
      docs/assets/js/frappe-charts.min.js.map
  8. +16
    -78
      src/js/charts/AxisChart.js
  9. +4
    -18
      src/js/charts/BaseChart.js
  10. +96
    -15
      src/js/objects/ChartComponents.js
  11. +1
    -0
      src/js/utils/constants.js
  12. +23
    -0
      src/js/utils/helpers.js

+ 117
- 344
dist/frappe-charts.esm.js 查看文件

@@ -232,25 +232,6 @@ function getStringWidth(string, charWidth) {
return (string+"").length * charWidth; return (string+"").length * charWidth;
} }


function getBarHeightAndYAttr(yTop, zeroLine) {
let height, y;
if (yTop <= zeroLine) {
height = zeroLine - yTop;
y = yTop;

// In case of invisible bars
if(height === 0) {
height = totalHeight * MIN_BAR_PERCENT_HEIGHT;
y -= height;
}
} else {
height = yTop - zeroLine;
y = zeroLine;
}

return [height, y];
}

function equilizeNoOfElements(array1, array2, function equilizeNoOfElements(array1, array2,
extra_count=array2.length - array1.length) { extra_count=array2.length - array1.length) {


@@ -284,7 +265,7 @@ function equilizeNoOfElements(array1, array2,
// } // }


const UNIT_ANIM_DUR = 350; const UNIT_ANIM_DUR = 350;
const PATH_ANIM_DUR = 350;
const MARKER_LINE_ANIM_DUR = UNIT_ANIM_DUR; const MARKER_LINE_ANIM_DUR = UNIT_ANIM_DUR;
const REPLACE_ALL_NEW_DUR = 250; const REPLACE_ALL_NEW_DUR = 250;


@@ -408,13 +389,7 @@ function makeSVGGroup(parent, className, transform='') {
}); });
} }


function wrapInSVGGroup(elements, className='') {
let g = createSVG('g', {
className: className
});
elements.forEach(e => g.appendChild(e));
return g;
}



function makePath(pathStr, className='', stroke='none', fill='none') { function makePath(pathStr, className='', stroke='none', fill='none') {
return createSVG('path', { return createSVG('path', {
@@ -663,79 +638,6 @@ function yRegion(y1, y2, width, label) {
return region; return region;
} }


class AxisChartRenderer {
constructor(state) {
this.refreshState(state);
}

refreshState(state) {
this.totalHeight = state.totalHeight;
this.totalWidth = state.totalWidth;
this.zeroLine = state.zeroLine;
this.unitWidth = state.unitWidth;
this.xAxisMode = state.xAxisMode;
this.yAxisMode = state.yAxisMode;
}

setZeroline(zeroLine) {
this.zeroLine = zeroLine;
}

xMarker() {}


xRegion() {
return createSVG('rect', {
className: `bar mini`, // remove class
style: `fill: rgba(228, 234, 239, 0.49)`,
// 'data-point-index': index,
x: 0,
y: y2,
width: this.totalWidth,
height: y1 - y2
});

return region;
}

animatebar(bar, x, yTop, index, noOfDatasets) {
let start = x - this.avgUnitWidth/4;
let width = (this.avgUnitWidth/2)/noOfDatasets;
let [height, y] = getBarHeightAndYAttr(yTop, this.zeroLine, this.totalHeight);

x = start + (width * index);

return [bar, {width: width, height: height, x: x, y: y}, UNIT_ANIM_DUR, STD_EASING];
// bar.animate({height: args.newHeight, y: yTop}, UNIT_ANIM_DUR, mina.easein);
}

animatedot(dot, x, yTop) {
return [dot, {cx: x, cy: yTop}, UNIT_ANIM_DUR, STD_EASING];
// dot.animate({cy: yTop}, UNIT_ANIM_DUR, mina.easein);
}

animatepath(paths, pathStr) {
let pathComponents = [];
const animPath = [paths[0], {d:"M"+pathStr}, PATH_ANIM_DUR, STD_EASING];
pathComponents.push(animPath);

if(paths[1]) {
let regStartPt = `0,${this.zeroLine}L`;
let regEndPt = `L${this.totalWidth}, ${this.zeroLine}`;

const animRegion = [
paths[1],
{d:"M" + regStartPt + pathStr + regEndPt},
PATH_ANIM_DUR,
STD_EASING
];
pathComponents.push(animRegion);
}

return pathComponents;
}
}

const PRESET_COLOR_MAP = { const PRESET_COLOR_MAP = {
'light-blue': '#7cd6fd', 'light-blue': '#7cd6fd',
'blue': '#5e64ff', 'blue': '#5e64ff',
@@ -1047,18 +949,10 @@ class BaseChart {
let valid = this.checkData(data); let valid = this.checkData(data);
if(!valid) return false; if(!valid) return false;


if(!this.config.animate) {
this.data = data;
} else {
[this.data, this.firstUpdateData] =
this.getFirstUpdateData(data);
}
this.data = data;
return true; return true;
} }


checkData() {}
getFirstUpdateData() {}

setup() { setup() {
if(this.validate()) { if(this.validate()) {
this._setup(); this._setup();
@@ -1145,7 +1039,6 @@ class BaseChart {
update(data=this.data) { update(data=this.data) {
this.prepareData(data); this.prepareData(data);
this.calc(); // builds state this.calc(); // builds state
this.refreshRenderer();
this.render(); this.render();
} }


@@ -1155,13 +1048,10 @@ class BaseChart {


calc() {} // builds state calc() {} // builds state


refreshRenderer() {
this.renderer = {};
}

render(animate=true) { render(animate=true) {
this.refreshComponents();
this.elementsToAnimate = [].concat.apply([], this.components.map(c => c.update(animate)));
// Can decouple to this.refreshComponents() first to save animation timeout
this.elementsToAnimate = [].concat.apply([],
this.components.map(c => c.update(animate)));
if(this.elementsToAnimate) { if(this.elementsToAnimate) {
runSMILAnimation(this.chartWrapper, this.svg, this.elementsToAnimate); runSMILAnimation(this.chartWrapper, this.svg, this.elementsToAnimate);
} }
@@ -1172,8 +1062,6 @@ class BaseChart {
// } // }
} }


refreshComponents() {}

makeChartArea() { makeChartArea() {
this.svg = makeSVGContainer( this.svg = makeSVGContainer(
this.chartWrapper, this.chartWrapper,
@@ -1285,18 +1173,15 @@ class ChartComponent$1 {
data, data,


// called on update // called on update
preMake,
makeElements, makeElements,
postMake, postMake,
getData, getData,
animateElements animateElements
}) { }) {
this.parent = parent; this.parent = parent;
this.layerClass = layerClass;
this.layerTransform = layerTransform; this.layerTransform = layerTransform;
this.constants = constants; this.constants = constants;


this.preMake = preMake;
this.makeElements = makeElements; this.makeElements = makeElements;
this.postMake = postMake; this.postMake = postMake;
this.getData = getData; this.getData = getData;
@@ -1304,7 +1189,11 @@ class ChartComponent$1 {
this.animateElements = animateElements; this.animateElements = animateElements;


this.store = []; this.store = [];
this.layer = makeSVGGroup(this.parent, this.layerClass, this.layerTransform);

layerClass = typeof(layerClass) === 'function'
? layerClass() : layerClass;

this.layer = makeSVGGroup(this.parent, layerClass, this.layerTransform);


this.data = data; this.data = data;


@@ -1312,7 +1201,7 @@ class ChartComponent$1 {
} }


refresh(data) { refresh(data) {
this.data = data;
this.data = data || this.getData();
} }


make() { make() {
@@ -1332,6 +1221,7 @@ class ChartComponent$1 {
} }


update(animate = true) { update(animate = true) {
this.refresh();
let animateElements = []; let animateElements = [];
if(animate) { if(animate) {
animateElements = this.animateElements(this.data); animateElements = this.animateElements(this.data);
@@ -1347,14 +1237,14 @@ class ChartComponent$1 {
let componentConfigs = { let componentConfigs = {
yAxis: { yAxis: {
layerClass: 'y axis', layerClass: 'y axis',
makeElements: function(data) {
makeElements(data) {
return data.positions.map((position, i) => return data.positions.map((position, i) =>
yLine(position, data.labels[i], this.constants.width, yLine(position, data.labels[i], this.constants.width,
{mode: this.constants.mode, pos: this.constants.pos}) {mode: this.constants.mode, pos: this.constants.pos})
); );
}, },


animateElements: function(newData) {
animateElements(newData) {
let newPos = newData.positions; let newPos = newData.positions;
let newLabels = newData.labels; let newLabels = newData.labels;
let oldPos = this.oldData.positions; let oldPos = this.oldData.positions;
@@ -1378,14 +1268,14 @@ let componentConfigs = {


xAxis: { xAxis: {
layerClass: 'x axis', layerClass: 'x axis',
makeElements: function(data) {
makeElements(data) {
return data.positions.map((position, i) => return data.positions.map((position, i) =>
xLine(position, data.labels[i], this.constants.height, xLine(position, data.labels[i], this.constants.height,
{mode: this.constants.mode, pos: this.constants.pos}) {mode: this.constants.mode, pos: this.constants.pos})
); );
}, },


animateElements: function(newData) {
animateElements(newData) {
let newPos = newData.positions; let newPos = newData.positions;
let newLabels = newData.labels; let newLabels = newData.labels;
let oldPos = this.oldData.positions; let oldPos = this.oldData.positions;
@@ -1409,13 +1299,13 @@ let componentConfigs = {


yMarkers: { yMarkers: {
layerClass: 'y-markers', layerClass: 'y-markers',
makeElements: function(data) {
makeElements(data) {
return data.map(marker => return data.map(marker =>
yMarker(marker.position, marker.label, this.constants.width, yMarker(marker.position, marker.label, this.constants.width,
{pos:'right', mode: 'span', lineType: 'dashed'}) {pos:'right', mode: 'span', lineType: 'dashed'})
); );
}, },
animateElements: function(newData) {
animateElements(newData) {
[this.oldData, newData] = equilizeNoOfElements(this.oldData, newData); [this.oldData, newData] = equilizeNoOfElements(this.oldData, newData);


let newPos = newData.map(d => d.position); let newPos = newData.map(d => d.position);
@@ -1441,13 +1331,13 @@ let componentConfigs = {


yRegions: { yRegions: {
layerClass: 'y-regions', layerClass: 'y-regions',
makeElements: function(data) {
makeElements(data) {
return data.map(region => return data.map(region =>
yRegion(region.start, region.end, this.constants.width, yRegion(region.start, region.end, this.constants.width,
region.label) region.label)
); );
}, },
animateElements: function(newData) {
animateElements(newData) {
[this.oldData, newData] = equilizeNoOfElements(this.oldData, newData); [this.oldData, newData] = equilizeNoOfElements(this.oldData, newData);


let newPos = newData.map(d => d.end); let newPos = newData.map(d => d.end);
@@ -1478,153 +1368,99 @@ let componentConfigs = {
} }
}, },


dataUnits: {
//
}
};

function getComponent(name, parent, constants, initData, getData) {
let config = componentConfigs[name];
Object.assign(config, {
parent: parent,
constants: constants,
data: initData,
getData: getData
});
return new ChartComponent$1(config);
}

const MIN_BAR_PERCENT_HEIGHT$1 = 0.01;

class AxisChartController {
constructor(meta) {
// TODO: make configurable passing args
this.meta = meta || {};
this.setupArgs();
}

setupArgs() {
this.consts = {};
}

setup() {}

refreshMeta(meta) {
this.meta = Object.assign((this.meta || {}), meta);
}

draw() {}
animate() {}
}



class BarChartController extends AxisChartController {
constructor(meta) {
super(meta);
}

setupArgs() {
this.consts = {
spaceRatio: 0.5,
minHeight: this.meta.totalHeight * MIN_BAR_PERCENT_HEIGHT$1
};
}
barGraph: {
// opt:[
// 'barGraph',
// this.drawArea,
// {
// controller: barController,
// index: index,
// color: this.colors[index],
// valuesOverPoints: this.valuesOverPoints,
// stacked: this.barOptions && this.barOptions.stacked,
// spaceRatio: 0.5,
// minHeight: this.height * MIN_BAR_PERCENT_HEIGHT
// },
// {
// barsWidth: this.state.unitWidth * (1 - spaceRatio),
// barWidth: barsWidth/(stacked ? 1 : this.state.noOfDatasets),

// },
// function() {
// let s = this.state;
// return {
// barsWidth: this.state.unitWidth * (1 - spaceRatio),
// barWidth: barsWidth/(stacked ? 1 : this.state.noOfDatasets),
// positions: s.xAxisPositions,
// labels: s.xAxisLabels,
// }
// }.bind(this)
// ],
layerClass() { return 'y-regions' + this.constants.index; },
makeElements(data) {
let c = this.constants;
return data.yPositions.map((y, j) =>
barController.draw(
data.xPositions[j],
y,
color,
(c.valuesOverPoints ? (c.stacked ? data.cumulativeYs[j] : data.values[j]) : ''),
j,
y - (data.cumulativePositions ? data.cumulativePositions[j] : y)
)
);
},
postMake() {
if((!this.constants.stacked)) {
this.layer.setAttribute('transform',
`translate(${unitRenderer.consts.width * index}, 0)`);
}
},
animateElements(newData) {
[this.oldData, newData] = equilizeNoOfElements(this.oldData, newData);


refreshMeta(meta) {
if(meta) {
super.refreshMeta(meta);
}
let m = this.meta;
this.consts.barsWidth = m.unitWidth - m.unitWidth * this.consts.spaceRatio;
let newPos = newData.map(d => d.end);
let newLabels = newData.map(d => d.label);
let newStarts = newData.map(d => d.start);


this.consts.width = this.consts.barsWidth / (m.options && m.options.stacked
? m.options.stacked : m.noOfDatasets);
}
let oldPos = this.oldData.map(d => d.end);
let oldLabels = this.oldData.map(d => d.label);
let oldStarts = this.oldData.map(d => d.start);


draw(x, yTop, color, label='', index=0, offset=0) {
let [height, y] = getBarHeightAndYAttr(yTop, this.meta.zeroLine);
this.render(oldPos.map((pos, i) => {
return {
start: oldStarts[i],
end: oldPos[i],
label: newLabels[i]
}
}));


let rect = createSVG('rect', {
className: `bar mini`,
style: `fill: ${color}`,
'data-point-index': index,
x: x - this.consts.barsWidth/2,
y: y - offset,
width: this.consts.width,
height: height || this.consts.minHeight
});
let animateElements = [];


if(!label && !label.length) {
return rect;
} else {
let text = createSVG('text', {
className: 'data-point-value',
x: x,
y: y - offset,
dy: (FONT_SIZE / 2 * -1) + 'px',
'font-size': FONT_SIZE + 'px',
'text-anchor': 'middle',
innerHTML: label
this.store.map((rectGroup, i) => {
animateElements = animateElements.concat(animateRegion(
rectGroup, newStarts[i], newPos[i], oldPos[i]
));
}); });


return wrapInSVGGroup([rect, text]);
return animateElements;
} }
}

animate(bar, x, yTop, index, noOfDatasets) {
let start = x - this.meta.unitWidth/4;
let width = (this.meta.unitWidth/2)/noOfDatasets;
let [height, y] = getBarHeightAndYAttr(yTop, this.meta.zeroLine, this.meta.totalHeight);

x = start + (width * index);

return [bar, {width: width, height: height, x: x, y: y}, UNIT_ANIM_DUR, STD_EASING];
// bar.animate({height: args.newHeight, y: yTop}, UNIT_ANIM_DUR, mina.easein);
}
}

class LineChartController extends AxisChartController {
constructor(meta) {
super(meta);
}

setupArgs() {
this.consts = {
radius: this.meta.dotSize || 4
};
}

draw(x, y, color, label='', index=0) {
let dot = createSVG('circle', {
style: `fill: ${color}`,
'data-point-index': index,
cx: x,
cy: y,
r: this.consts.radius
});
},


if(!label && !label.length) {
return dot;
} else {
let text = createSVG('text', {
className: 'data-point-value',
x: x,
y: y,
dy: (FONT_SIZE / 2 * -1 - this.consts.radius) + 'px',
'font-size': FONT_SIZE + 'px',
'text-anchor': 'middle',
innerHTML: label
});
lineGraph: {


return wrapInSVGGroup([dot, text]);
}
} }
};


animate(dot, x, yTop) {
return [dot, {cx: x, cy: yTop}, UNIT_ANIM_DUR, STD_EASING];
// dot.animate({cy: yTop}, UNIT_ANIM_DUR, mina.easein);
}
function getComponent(name, parent, constants, initData, getData) {
let config = componentConfigs[name];
Object.assign(config, {
parent: parent,
constants: constants,
data: initData,
getData: getData
});
return new ChartComponent$1(config);
} }


function getPaths(yList, xList, color, heatline=false, regionFill=false) { function getPaths(yList, xList, color, heatline=false, regionFill=false) {
@@ -1953,10 +1789,8 @@ class AxisChart extends BaseChart {
this.xAxisMode = args.xAxisMode || 'span'; this.xAxisMode = args.xAxisMode || 'span';
this.yAxisMode = args.yAxisMode || 'span'; this.yAxisMode = args.yAxisMode || 'span';


this.setupUnitRenderer();

this.zeroLine = this.height; this.zeroLine = this.height;
this.preSetup();
this.setPrimitiveData();
this.setup(); this.setup();
} }


@@ -1967,15 +1801,14 @@ class AxisChart extends BaseChart {
this.config.yAxisMode = args.yAxisMode; this.config.yAxisMode = args.yAxisMode;
} }


preSetup() {}
setPrimitiveData() {
// Define data and stuff
this.setObservers();
}


setupUnitRenderer() {
// TODO: this is empty
let options = this.rawChartArgs.options;
this.unitRenderers = {
bar: new BarChartController(options),
line: new LineChartController(options)
};
setObservers() {
// go through each component and check the keys in this.state it depends on
// set an observe() on each of those keys for that component
} }


setHorizontalMargin() { setHorizontalMargin() {
@@ -1987,10 +1820,6 @@ class AxisChart extends BaseChart {
return true; return true;
} }


getFirstUpdateData(data) {
//
}

setupConstants() { setupConstants() {
this.state = { this.state = {
xAxisLabels: [], xAxisLabels: [],
@@ -2005,18 +1834,19 @@ class AxisChart extends BaseChart {
} }
}); });


this.prepareYAxis();
// Prepare Y Axis
this.state.yAxis = {
labels: [],
positions: []
};
} }


prepareData(data) { prepareData(data) {
let s = this.state; let s = this.state;

s.xAxisLabels = data.labels || []; s.xAxisLabels = data.labels || [];

s.datasetLength = s.xAxisLabels.length; s.datasetLength = s.xAxisLabels.length;


let zeroArray = new Array(s.datasetLength).fill(0); let zeroArray = new Array(s.datasetLength).fill(0);

s.datasets = data.datasets; // whole dataset info too s.datasets = data.datasets; // whole dataset info too
if(!data.datasets) { if(!data.datasets) {
// default // default
@@ -2049,13 +1879,6 @@ class AxisChart extends BaseChart {
s.yRegions = data.yRegions; s.yRegions = data.yRegions;
} }


prepareYAxis() {
this.state.yAxis = {
labels: [],
positions: []
};
}

calc() { calc() {
let s = this.state; let s = this.state;


@@ -2068,8 +1891,6 @@ class AxisChart extends BaseChart {
this.calcYMaximums(); this.calcYMaximums();
this.calcYRegions(); this.calcYRegions();


// should be state
this.configUnits();
} }


setYAxis() { setYAxis() {
@@ -2154,8 +1975,6 @@ class AxisChart extends BaseChart {
} }
} }


configUnits() {}

// Default, as per bar, and mixed. Only line will be a special case // Default, as per bar, and mixed. Only line will be a special case
setUnitWidthAndXOffset() { setUnitWidthAndXOffset() {
this.state.unitWidth = this.width/(this.state.datasetLength); this.state.unitWidth = this.width/(this.state.datasetLength);
@@ -2179,12 +1998,6 @@ class AxisChart extends BaseChart {
return [].concat(...this.state.datasets.map(d => d[key])); return [].concat(...this.state.datasets.map(d => d[key]));
} }


calcIntermedState() {
//
}

setupValues() {}

initComponents() { initComponents() {
this.componentConfigs = [ this.componentConfigs = [
[ [
@@ -2266,7 +2079,7 @@ class AxisChart extends BaseChart {
function() { function() {
return this.state.yMarkers || []; return this.state.yMarkers || [];
}.bind(this) }.bind(this)
],
]
]; ];
} }
setupComponents() { setupComponents() {
@@ -2276,10 +2089,6 @@ class AxisChart extends BaseChart {
.map(args => getComponent(...args)); .map(args => getComponent(...args));
} }


refreshComponents() {
this.components.forEach(comp => comp.refresh(comp.getData()));
}

getChartComponents() { getChartComponents() {
let dataUnitsComponents = []; let dataUnitsComponents = [];
// this.state is not defined at this stage // this.state is not defined at this stage
@@ -2299,9 +2108,8 @@ class AxisChart extends BaseChart {
getDataUnitComponent(index, unitRenderer) { getDataUnitComponent(index, unitRenderer) {
return new ChartComponent({ return new ChartComponent({
layerClass: 'dataset-units dataset-' + index, layerClass: 'dataset-units dataset-' + index,
setData: () => {},
preMake: () => { },
makeElements: () => { makeElements: () => {
// yPositions, xPostions, color, valuesOverPoints,
let d = this.state.datasets[index]; let d = this.state.datasets[index];


return d.positions.map((y, j) => { return d.positions.map((y, j) => {
@@ -2405,41 +2213,6 @@ class AxisChart extends BaseChart {
}); });
} }


refreshRenderer() {
// These args are basically the current state of the chart,
// with constant and alive params mixed
let state = {
totalHeight: this.height,
totalWidth: this.width,

xAxisMode: this.config.xAxisMode,
yAxisMode: this.config.yAxisMode,

zeroLine: this.state.zeroLine,
unitWidth: this.state.unitWidth,
};
if(!this.renderer) {
this.renderer = new AxisChartRenderer(state);
} else {
this.renderer.refreshState(state);
}

let meta = {
totalHeight: this.height,
totalWidth: this.width,
zeroLine: this.state.zeroLine,
unitWidth: this.state.unitWidth,
noOfDatasets: this.state.noOfDatasets,
};

meta = Object.assign(meta, this.rawChartArgs.options);

Object.keys(this.unitRenderers).map(key => {
meta.options = this[key + 'Options'];
this.unitRenderers[key].refreshMeta(meta);
});
}

bindTooltip() { bindTooltip() {
// TODO: could be in tooltip itself, as it is a given functionality for its parent // TODO: could be in tooltip itself, as it is a given functionality for its parent
this.chartWrapper.addEventListener('mousemove', (e) => { this.chartWrapper.addEventListener('mousemove', (e) => {


+ 1
- 1
dist/frappe-charts.min.cjs.js
文件差異過大導致無法顯示
查看文件


+ 1
- 1
dist/frappe-charts.min.esm.js
文件差異過大導致無法顯示
查看文件


+ 1
- 1
dist/frappe-charts.min.iife.js
文件差異過大導致無法顯示
查看文件


+ 1
- 1
dist/frappe-charts.min.iife.js.map
文件差異過大導致無法顯示
查看文件


+ 1
- 1
docs/assets/js/frappe-charts.min.js
文件差異過大導致無法顯示
查看文件


+ 1
- 1
docs/assets/js/frappe-charts.min.js.map
文件差異過大導致無法顯示
查看文件


+ 16
- 78
src/js/charts/AxisChart.js 查看文件

@@ -9,6 +9,7 @@ import { Animator, translateHoriLine } from '../utils/animate';
import { runSMILAnimation } from '../utils/animation'; import { runSMILAnimation } from '../utils/animation';
import { getRealIntervals, calcChartIntervals, getIntervalSize, getValueRange, getZeroIndex } from '../utils/intervals'; import { getRealIntervals, calcChartIntervals, getIntervalSize, getValueRange, getZeroIndex } from '../utils/intervals';
import { floatTwo, fillArray } from '../utils/helpers'; import { floatTwo, fillArray } from '../utils/helpers';
import { MIN_BAR_PERCENT_HEIGHT } from '../utils/constants';


export default class AxisChart extends BaseChart { export default class AxisChart extends BaseChart {
constructor(args) { constructor(args) {
@@ -24,10 +25,8 @@ export default class AxisChart extends BaseChart {
this.xAxisMode = args.xAxisMode || 'span'; this.xAxisMode = args.xAxisMode || 'span';
this.yAxisMode = args.yAxisMode || 'span'; this.yAxisMode = args.yAxisMode || 'span';


this.setupUnitRenderer();

this.zeroLine = this.height; this.zeroLine = this.height;
this.preSetup();
this.setPrimitiveData();
this.setup(); this.setup();
} }


@@ -38,15 +37,14 @@ export default class AxisChart extends BaseChart {
this.config.yAxisMode = args.yAxisMode; this.config.yAxisMode = args.yAxisMode;
} }


preSetup() {}
setPrimitiveData() {
// Define data and stuff
this.setObservers();
}


setupUnitRenderer() {
// TODO: this is empty
let options = this.rawChartArgs.options;
this.unitRenderers = {
bar: new BarChartController(options),
line: new LineChartController(options)
};
setObservers() {
// go through each component and check the keys in this.state it depends on
// set an observe() on each of those keys for that component
} }


setHorizontalMargin() { setHorizontalMargin() {
@@ -58,10 +56,6 @@ export default class AxisChart extends BaseChart {
return true; return true;
} }


getFirstUpdateData(data) {
//
}

setupConstants() { setupConstants() {
this.state = { this.state = {
xAxisLabels: [], xAxisLabels: [],
@@ -76,18 +70,19 @@ export default class AxisChart extends BaseChart {
} }
}); });


this.prepareYAxis();
// Prepare Y Axis
this.state.yAxis = {
labels: [],
positions: []
};
} }


prepareData(data) { prepareData(data) {
let s = this.state; let s = this.state;

s.xAxisLabels = data.labels || []; s.xAxisLabels = data.labels || [];

s.datasetLength = s.xAxisLabels.length; s.datasetLength = s.xAxisLabels.length;


let zeroArray = new Array(s.datasetLength).fill(0); let zeroArray = new Array(s.datasetLength).fill(0);

s.datasets = data.datasets; // whole dataset info too s.datasets = data.datasets; // whole dataset info too
if(!data.datasets) { if(!data.datasets) {
// default // default
@@ -120,13 +115,6 @@ export default class AxisChart extends BaseChart {
s.yRegions = data.yRegions; s.yRegions = data.yRegions;
} }


prepareYAxis() {
this.state.yAxis = {
labels: [],
positions: []
};
}

calc() { calc() {
let s = this.state; let s = this.state;


@@ -139,8 +127,6 @@ export default class AxisChart extends BaseChart {
this.calcYMaximums(); this.calcYMaximums();
this.calcYRegions(); this.calcYRegions();


// should be state
this.configUnits();
} }


setYAxis() { setYAxis() {
@@ -225,8 +211,6 @@ export default class AxisChart extends BaseChart {
} }
} }


configUnits() {}

// Default, as per bar, and mixed. Only line will be a special case // Default, as per bar, and mixed. Only line will be a special case
setUnitWidthAndXOffset() { setUnitWidthAndXOffset() {
this.state.unitWidth = this.width/(this.state.datasetLength); this.state.unitWidth = this.width/(this.state.datasetLength);
@@ -250,12 +234,6 @@ export default class AxisChart extends BaseChart {
return [].concat(...this.state.datasets.map(d => d[key])); return [].concat(...this.state.datasets.map(d => d[key]));
} }


calcIntermedState() {
//
}

setupValues() {}

initComponents() { initComponents() {
this.componentConfigs = [ this.componentConfigs = [
[ [
@@ -337,7 +315,7 @@ export default class AxisChart extends BaseChart {
function() { function() {
return this.state.yMarkers || []; return this.state.yMarkers || [];
}.bind(this) }.bind(this)
],
]
]; ];
} }
setupComponents() { setupComponents() {
@@ -347,10 +325,6 @@ export default class AxisChart extends BaseChart {
.map(args => getComponent(...args)); .map(args => getComponent(...args));
} }


refreshComponents() {
this.components.forEach(comp => comp.refresh(comp.getData()));
}

getChartComponents() { getChartComponents() {
let dataUnitsComponents = [] let dataUnitsComponents = []
// this.state is not defined at this stage // this.state is not defined at this stage
@@ -370,9 +344,8 @@ export default class AxisChart extends BaseChart {
getDataUnitComponent(index, unitRenderer) { getDataUnitComponent(index, unitRenderer) {
return new ChartComponent({ return new ChartComponent({
layerClass: 'dataset-units dataset-' + index, layerClass: 'dataset-units dataset-' + index,
setData: () => {},
preMake: () => { },
makeElements: () => { makeElements: () => {
// yPositions, xPostions, color, valuesOverPoints,
let d = this.state.datasets[index]; let d = this.state.datasets[index];


return d.positions.map((y, j) => { return d.positions.map((y, j) => {
@@ -476,41 +449,6 @@ export default class AxisChart extends BaseChart {
}); });
} }


refreshRenderer() {
// These args are basically the current state of the chart,
// with constant and alive params mixed
let state = {
totalHeight: this.height,
totalWidth: this.width,

xAxisMode: this.config.xAxisMode,
yAxisMode: this.config.yAxisMode,

zeroLine: this.state.zeroLine,
unitWidth: this.state.unitWidth,
};
if(!this.renderer) {
this.renderer = new AxisChartRenderer(state);
} else {
this.renderer.refreshState(state);
}

let meta = {
totalHeight: this.height,
totalWidth: this.width,
zeroLine: this.state.zeroLine,
unitWidth: this.state.unitWidth,
noOfDatasets: this.state.noOfDatasets,
};

meta = Object.assign(meta, this.rawChartArgs.options);

Object.keys(this.unitRenderers).map(key => {
meta.options = this[key + 'Options'];
this.unitRenderers[key].refreshMeta(meta);
});
}

bindTooltip() { bindTooltip() {
// TODO: could be in tooltip itself, as it is a given functionality for its parent // TODO: could be in tooltip itself, as it is a given functionality for its parent
this.chartWrapper.addEventListener('mousemove', (e) => { this.chartWrapper.addEventListener('mousemove', (e) => {


+ 4
- 18
src/js/charts/BaseChart.js 查看文件

@@ -107,18 +107,10 @@ export default class BaseChart {
let valid = this.checkData(data); let valid = this.checkData(data);
if(!valid) return false; if(!valid) return false;


if(!this.config.animate) {
this.data = data;
} else {
[this.data, this.firstUpdateData] =
this.getFirstUpdateData(data);
}
this.data = data;
return true; return true;
} }


checkData() {}
getFirstUpdateData() {}

setup() { setup() {
if(this.validate()) { if(this.validate()) {
this._setup(); this._setup();
@@ -205,7 +197,6 @@ export default class BaseChart {
update(data=this.data) { update(data=this.data) {
this.prepareData(data); this.prepareData(data);
this.calc(); // builds state this.calc(); // builds state
this.refreshRenderer();
this.render(); this.render();
} }


@@ -215,13 +206,10 @@ export default class BaseChart {


calc() {} // builds state calc() {} // builds state


refreshRenderer() {
this.renderer = {};
}

render(animate=true) { render(animate=true) {
this.refreshComponents();
this.elementsToAnimate = [].concat.apply([], this.components.map(c => c.update(animate)));
// Can decouple to this.refreshComponents() first to save animation timeout
this.elementsToAnimate = [].concat.apply([],
this.components.map(c => c.update(animate)));
if(this.elementsToAnimate) { if(this.elementsToAnimate) {
runSMILAnimation(this.chartWrapper, this.svg, this.elementsToAnimate); runSMILAnimation(this.chartWrapper, this.svg, this.elementsToAnimate);
} }
@@ -232,8 +220,6 @@ export default class BaseChart {
// } // }
} }


refreshComponents() {}

makeChartArea() { makeChartArea() {
this.svg = makeSVGContainer( this.svg = makeSVGContainer(
this.chartWrapper, this.chartWrapper,


+ 96
- 15
src/js/objects/ChartComponents.js 查看文件

@@ -12,18 +12,15 @@ class ChartComponent {
data, data,


// called on update // called on update
preMake,
makeElements, makeElements,
postMake, postMake,
getData, getData,
animateElements animateElements
}) { }) {
this.parent = parent; this.parent = parent;
this.layerClass = layerClass;
this.layerTransform = layerTransform; this.layerTransform = layerTransform;
this.constants = constants; this.constants = constants;


this.preMake = preMake;
this.makeElements = makeElements; this.makeElements = makeElements;
this.postMake = postMake; this.postMake = postMake;
this.getData = getData; this.getData = getData;
@@ -31,7 +28,11 @@ class ChartComponent {
this.animateElements = animateElements; this.animateElements = animateElements;


this.store = []; this.store = [];
this.layer = makeSVGGroup(this.parent, this.layerClass, this.layerTransform);

layerClass = typeof(layerClass) === 'function'
? layerClass() : layerClass;

this.layer = makeSVGGroup(this.parent, layerClass, this.layerTransform);


this.data = data; this.data = data;


@@ -39,7 +40,7 @@ class ChartComponent {
} }


refresh(data) { refresh(data) {
this.data = data;
this.data = data || this.getData();
} }


make() { make() {
@@ -59,6 +60,7 @@ class ChartComponent {
} }


update(animate = true) { update(animate = true) {
this.refresh();
let animateElements = [] let animateElements = []
if(animate) { if(animate) {
animateElements = this.animateElements(this.data); animateElements = this.animateElements(this.data);
@@ -74,14 +76,14 @@ class ChartComponent {
let componentConfigs = { let componentConfigs = {
yAxis: { yAxis: {
layerClass: 'y axis', layerClass: 'y axis',
makeElements: function(data) {
makeElements(data) {
return data.positions.map((position, i) => return data.positions.map((position, i) =>
yLine(position, data.labels[i], this.constants.width, yLine(position, data.labels[i], this.constants.width,
{mode: this.constants.mode, pos: this.constants.pos}) {mode: this.constants.mode, pos: this.constants.pos})
); );
}, },


animateElements: function(newData) {
animateElements(newData) {
let newPos = newData.positions; let newPos = newData.positions;
let newLabels = newData.labels; let newLabels = newData.labels;
let oldPos = this.oldData.positions; let oldPos = this.oldData.positions;
@@ -105,14 +107,14 @@ let componentConfigs = {


xAxis: { xAxis: {
layerClass: 'x axis', layerClass: 'x axis',
makeElements: function(data) {
makeElements(data) {
return data.positions.map((position, i) => return data.positions.map((position, i) =>
xLine(position, data.labels[i], this.constants.height, xLine(position, data.labels[i], this.constants.height,
{mode: this.constants.mode, pos: this.constants.pos}) {mode: this.constants.mode, pos: this.constants.pos})
); );
}, },


animateElements: function(newData) {
animateElements(newData) {
let newPos = newData.positions; let newPos = newData.positions;
let newLabels = newData.labels; let newLabels = newData.labels;
let oldPos = this.oldData.positions; let oldPos = this.oldData.positions;
@@ -136,13 +138,13 @@ let componentConfigs = {


yMarkers: { yMarkers: {
layerClass: 'y-markers', layerClass: 'y-markers',
makeElements: function(data) {
makeElements(data) {
return data.map(marker => return data.map(marker =>
yMarker(marker.position, marker.label, this.constants.width, yMarker(marker.position, marker.label, this.constants.width,
{pos:'right', mode: 'span', lineType: 'dashed'}) {pos:'right', mode: 'span', lineType: 'dashed'})
); );
}, },
animateElements: function(newData) {
animateElements(newData) {
[this.oldData, newData] = equilizeNoOfElements(this.oldData, newData); [this.oldData, newData] = equilizeNoOfElements(this.oldData, newData);


let newPos = newData.map(d => d.position); let newPos = newData.map(d => d.position);
@@ -168,13 +170,13 @@ let componentConfigs = {


yRegions: { yRegions: {
layerClass: 'y-regions', layerClass: 'y-regions',
makeElements: function(data) {
makeElements(data) {
return data.map(region => return data.map(region =>
yRegion(region.start, region.end, this.constants.width, yRegion(region.start, region.end, this.constants.width,
region.label) region.label)
); );
}, },
animateElements: function(newData) {
animateElements(newData) {
[this.oldData, newData] = equilizeNoOfElements(this.oldData, newData); [this.oldData, newData] = equilizeNoOfElements(this.oldData, newData);


let newPos = newData.map(d => d.end); let newPos = newData.map(d => d.end);
@@ -205,8 +207,87 @@ let componentConfigs = {
} }
}, },


dataUnits: {
//
barGraph: {
// opt:[
// 'barGraph',
// this.drawArea,
// {
// controller: barController,
// index: index,
// color: this.colors[index],
// valuesOverPoints: this.valuesOverPoints,
// stacked: this.barOptions && this.barOptions.stacked,
// spaceRatio: 0.5,
// minHeight: this.height * MIN_BAR_PERCENT_HEIGHT
// },
// {
// barsWidth: this.state.unitWidth * (1 - spaceRatio),
// barWidth: barsWidth/(stacked ? 1 : this.state.noOfDatasets),

// },
// function() {
// let s = this.state;
// return {
// barsWidth: this.state.unitWidth * (1 - spaceRatio),
// barWidth: barsWidth/(stacked ? 1 : this.state.noOfDatasets),
// positions: s.xAxisPositions,
// labels: s.xAxisLabels,
// }
// }.bind(this)
// ],
layerClass() { return 'y-regions' + this.constants.index; },
makeElements(data) {
let c = this.constants;
return data.yPositions.map((y, j) =>
barController.draw(
data.xPositions[j],
y,
color,
(c.valuesOverPoints ? (c.stacked ? data.cumulativeYs[j] : data.values[j]) : ''),
j,
y - (data.cumulativePositions ? data.cumulativePositions[j] : y)
)
);
},
postMake() {
if((!this.constants.stacked)) {
this.layer.setAttribute('transform',
`translate(${unitRenderer.consts.width * index}, 0)`);
}
},
animateElements(newData) {
[this.oldData, newData] = equilizeNoOfElements(this.oldData, newData);

let newPos = newData.map(d => d.end);
let newLabels = newData.map(d => d.label);
let newStarts = newData.map(d => d.start);

let oldPos = this.oldData.map(d => d.end);
let oldLabels = this.oldData.map(d => d.label);
let oldStarts = this.oldData.map(d => d.start);

this.render(oldPos.map((pos, i) => {
return {
start: oldStarts[i],
end: oldPos[i],
label: newLabels[i]
}
}));

let animateElements = [];

this.store.map((rectGroup, i) => {
animateElements = animateElements.concat(animateRegion(
rectGroup, newStarts[i], newPos[i], oldPos[i]
));
});

return animateElements;
}
},

lineGraph: {

} }
} }




+ 1
- 0
src/js/utils/constants.js 查看文件

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

+ 23
- 0
src/js/utils/helpers.js 查看文件

@@ -61,3 +61,26 @@ export function fillArray(array, count, element, start=false) {
export function getStringWidth(string, charWidth) { export function getStringWidth(string, charWidth) {
return (string+"").length * charWidth; return (string+"").length * charWidth;
} }

function observe(obj, componentNames) {
let components = this.components.get(name);

fn = function() {
components.map();
}
bindChange(obj, fn)
}

// observe(s.yAxis, ['yAxis', 'barGraph'])

export function bindChange(obj, fn) {
var proxied = new Proxy(obj, {
set: function(target, prop, value) {
fn();
return Reflect.set(target, prop, value);
}
});

// proxied.bar = 2;
// ==> {type: 'set', target: <obj>, prop: 'bar', value: 2}
}

Loading…
取消
儲存