ability to create donut chart
This commit is contained in:
parent
81cddd45a3
commit
8d00770207
180
dist/frappe-charts.esm.js
vendored
180
dist/frappe-charts.esm.js
vendored
@ -166,7 +166,8 @@ const DEFAULT_COLORS = {
|
|||||||
line: DEFAULT_CHART_COLORS,
|
line: DEFAULT_CHART_COLORS,
|
||||||
pie: DEFAULT_CHART_COLORS,
|
pie: DEFAULT_CHART_COLORS,
|
||||||
percentage: DEFAULT_CHART_COLORS,
|
percentage: DEFAULT_CHART_COLORS,
|
||||||
heatmap: HEATMAP_COLORS_GREEN
|
heatmap: HEATMAP_COLORS_GREEN,
|
||||||
|
donut: DEFAULT_CHART_COLORS
|
||||||
};
|
};
|
||||||
|
|
||||||
// Universal constants
|
// Universal constants
|
||||||
@ -516,13 +517,14 @@ function makeSVGGroup(className, transform='', parent=undefined) {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
function makePath(pathStr, className='', stroke='none', fill='none') {
|
function makePath(pathStr, className='', stroke='none', fill='none', strokeWidth=0) {
|
||||||
return createSVG('path', {
|
return createSVG('path', {
|
||||||
className: className,
|
className: className,
|
||||||
d: pathStr,
|
d: pathStr,
|
||||||
styles: {
|
styles: {
|
||||||
stroke: stroke,
|
stroke: stroke,
|
||||||
fill: fill
|
fill: fill,
|
||||||
|
'stroke-width': strokeWidth
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -537,6 +539,15 @@ function makeArcPathStr(startPosition, endPosition, center, radius, clockWise=1)
|
|||||||
${arcEndX} ${arcEndY} z`;
|
${arcEndX} ${arcEndY} z`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function makeArcStrokePathStr(startPosition, endPosition, center, radius, clockWise=1){
|
||||||
|
let [arcStartX, arcStartY] = [center.x + startPosition.x, center.y + startPosition.y];
|
||||||
|
let [arcEndX, arcEndY] = [center.x + endPosition.x, center.y + endPosition.y];
|
||||||
|
|
||||||
|
return `M${arcStartX} ${arcStartY}
|
||||||
|
A ${radius} ${radius} 0 0 ${clockWise ? 1 : 0}
|
||||||
|
${arcEndX} ${arcEndY}`;
|
||||||
|
}
|
||||||
|
|
||||||
function makeGradient(svgDefElem, color, lighter = false) {
|
function makeGradient(svgDefElem, color, lighter = false) {
|
||||||
let gradientId ='path-fill-gradient' + '-' + color + '-' +(lighter ? 'lighter' : 'default');
|
let gradientId ='path-fill-gradient' + '-' + color + '-' +(lighter ? 'lighter' : 'default');
|
||||||
let gradientDef = renderVerticalGradient(svgDefElem, gradientId);
|
let gradientDef = renderVerticalGradient(svgDefElem, gradientId);
|
||||||
@ -1878,6 +1889,20 @@ class ChartComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let componentConfigs = {
|
let componentConfigs = {
|
||||||
|
donutSlices: {
|
||||||
|
layerClass: 'donut-slices',
|
||||||
|
makeElements(data) {
|
||||||
|
return data.sliceStrings.map((s, i) => {
|
||||||
|
let slice = makePath(s, 'donut-path', data.colors[i], 'none', data.strokeWidth);
|
||||||
|
slice.style.transition = 'transform .3s;';
|
||||||
|
return slice;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
animateElements(newData) {
|
||||||
|
return this.store.map((slice, i) => animatePathStr(slice, newData.sliceStrings[i]));
|
||||||
|
},
|
||||||
|
},
|
||||||
pieSlices: {
|
pieSlices: {
|
||||||
layerClass: 'pie-slices',
|
layerClass: 'pie-slices',
|
||||||
makeElements(data) {
|
makeElements(data) {
|
||||||
@ -3677,6 +3702,152 @@ class AxisChart extends BaseChart {
|
|||||||
// removeDataPoint(index = 0) {}
|
// removeDataPoint(index = 0) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class DonutChart extends AggregationChart {
|
||||||
|
constructor(parent, args) {
|
||||||
|
super(parent, args);
|
||||||
|
this.type = 'donut';
|
||||||
|
this.initTimeout = 0;
|
||||||
|
this.init = 1;
|
||||||
|
|
||||||
|
this.setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
configure(args) {
|
||||||
|
super.configure(args);
|
||||||
|
this.mouseMove = this.mouseMove.bind(this);
|
||||||
|
this.mouseLeave = this.mouseLeave.bind(this);
|
||||||
|
|
||||||
|
this.hoverRadio = args.hoverRadio || 0.1;
|
||||||
|
this.config.startAngle = args.startAngle || 0;
|
||||||
|
|
||||||
|
this.clockWise = args.clockWise || false;
|
||||||
|
this.strokeWidth = args.strokeWidth || 30;
|
||||||
|
}
|
||||||
|
|
||||||
|
calc() {
|
||||||
|
super.calc();
|
||||||
|
let s = this.state;
|
||||||
|
this.radius = (this.height > this.width ? this.center.x - (this.strokeWidth / 2) : this.center.y - (this.strokeWidth / 2));
|
||||||
|
|
||||||
|
const { radius, clockWise } = this;
|
||||||
|
|
||||||
|
const prevSlicesProperties = s.slicesProperties || [];
|
||||||
|
s.sliceStrings = [];
|
||||||
|
s.slicesProperties = [];
|
||||||
|
let curAngle = 180 - this.config.startAngle;
|
||||||
|
|
||||||
|
s.sliceTotals.map((total, i) => {
|
||||||
|
const startAngle = curAngle;
|
||||||
|
const originDiffAngle = (total / s.grandTotal) * FULL_ANGLE;
|
||||||
|
const diffAngle = clockWise ? -originDiffAngle : originDiffAngle;
|
||||||
|
const endAngle = curAngle = curAngle + diffAngle;
|
||||||
|
const startPosition = getPositionByAngle(startAngle, radius);
|
||||||
|
const endPosition = getPositionByAngle(endAngle, radius);
|
||||||
|
|
||||||
|
const prevProperty = this.init && prevSlicesProperties[i];
|
||||||
|
|
||||||
|
let curStart,curEnd;
|
||||||
|
if(this.init) {
|
||||||
|
curStart = prevProperty ? prevProperty.startPosition : startPosition;
|
||||||
|
curEnd = prevProperty ? prevProperty.endPosition : startPosition;
|
||||||
|
} else {
|
||||||
|
curStart = startPosition;
|
||||||
|
curEnd = endPosition;
|
||||||
|
}
|
||||||
|
const curPath = makeArcStrokePathStr(curStart, curEnd, this.center, this.radius, this.clockWise);
|
||||||
|
|
||||||
|
s.sliceStrings.push(curPath);
|
||||||
|
s.slicesProperties.push({
|
||||||
|
startPosition,
|
||||||
|
endPosition,
|
||||||
|
value: total,
|
||||||
|
total: s.grandTotal,
|
||||||
|
startAngle,
|
||||||
|
endAngle,
|
||||||
|
angle: diffAngle
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
this.init = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
setupComponents() {
|
||||||
|
let s = this.state;
|
||||||
|
|
||||||
|
let componentConfigs = [
|
||||||
|
[
|
||||||
|
'donutSlices',
|
||||||
|
{ },
|
||||||
|
function() {
|
||||||
|
return {
|
||||||
|
sliceStrings: s.sliceStrings,
|
||||||
|
colors: this.colors,
|
||||||
|
strokeWidth: this.strokeWidth,
|
||||||
|
};
|
||||||
|
}.bind(this)
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
this.components = new Map(componentConfigs
|
||||||
|
.map(args => {
|
||||||
|
let component = getComponent(...args);
|
||||||
|
return [args[0], component];
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
calTranslateByAngle(property){
|
||||||
|
const{radius,hoverRadio} = this;
|
||||||
|
const position = getPositionByAngle(property.startAngle+(property.angle / 2),radius);
|
||||||
|
return `translate3d(${(position.x) * hoverRadio}px,${(position.y) * hoverRadio}px,0)`;
|
||||||
|
}
|
||||||
|
|
||||||
|
hoverSlice(path,i,flag,e){
|
||||||
|
if(!path) return;
|
||||||
|
const color = this.colors[i];
|
||||||
|
if(flag) {
|
||||||
|
transform(path, this.calTranslateByAngle(this.state.slicesProperties[i]));
|
||||||
|
path.style.stroke = lightenDarkenColor(color, 50);
|
||||||
|
let g_off = getOffset(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
|
||||||
|
? this.formatted_labels[i] : this.state.labels[i]) + ': ';
|
||||||
|
let percent = (this.state.sliceTotals[i] * 100 / this.state.grandTotal).toFixed(1);
|
||||||
|
this.tip.setValues(x, y, {name: title, value: percent + "%"});
|
||||||
|
this.tip.showTip();
|
||||||
|
} else {
|
||||||
|
transform(path,'translate3d(0,0,0)');
|
||||||
|
this.tip.hideTip();
|
||||||
|
path.style.stroke = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bindTooltip() {
|
||||||
|
this.container.addEventListener('mousemove', this.mouseMove);
|
||||||
|
this.container.addEventListener('mouseleave', this.mouseLeave);
|
||||||
|
}
|
||||||
|
|
||||||
|
mouseMove(e){
|
||||||
|
const target = e.target;
|
||||||
|
let slices = this.components.get('donutSlices').store;
|
||||||
|
let prevIndex = this.curActiveSliceIndex;
|
||||||
|
let prevAcitve = this.curActiveSlice;
|
||||||
|
if(slices.includes(target)) {
|
||||||
|
let i = slices.indexOf(target);
|
||||||
|
this.hoverSlice(prevAcitve, prevIndex,false);
|
||||||
|
this.curActiveSlice = target;
|
||||||
|
this.curActiveSliceIndex = i;
|
||||||
|
this.hoverSlice(target, i, true, e);
|
||||||
|
} else {
|
||||||
|
this.mouseLeave();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mouseLeave(){
|
||||||
|
this.hoverSlice(this.curActiveSlice,this.curActiveSliceIndex,false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// import MultiAxisChart from './charts/MultiAxisChart';
|
// import MultiAxisChart from './charts/MultiAxisChart';
|
||||||
const chartTypes = {
|
const chartTypes = {
|
||||||
bar: AxisChart,
|
bar: AxisChart,
|
||||||
@ -3684,7 +3855,8 @@ const chartTypes = {
|
|||||||
// multiaxis: MultiAxisChart,
|
// multiaxis: MultiAxisChart,
|
||||||
percentage: PercentageChart,
|
percentage: PercentageChart,
|
||||||
heatmap: Heatmap,
|
heatmap: Heatmap,
|
||||||
pie: PieChart
|
pie: PieChart,
|
||||||
|
donut: DonutChart,
|
||||||
};
|
};
|
||||||
|
|
||||||
function getChartByType(chartType = 'line', parent, options) {
|
function getChartByType(chartType = 'line', parent, options) {
|
||||||
|
|||||||
2
dist/frappe-charts.min.cjs.js
vendored
2
dist/frappe-charts.min.cjs.js
vendored
File diff suppressed because one or more lines are too long
2
dist/frappe-charts.min.cjs.js.map
vendored
2
dist/frappe-charts.min.cjs.js.map
vendored
File diff suppressed because one or more lines are too long
2
dist/frappe-charts.min.esm.js
vendored
2
dist/frappe-charts.min.esm.js
vendored
File diff suppressed because one or more lines are too long
2
dist/frappe-charts.min.esm.js.map
vendored
2
dist/frappe-charts.min.esm.js.map
vendored
File diff suppressed because one or more lines are too long
2
dist/frappe-charts.min.iife.js
vendored
2
dist/frappe-charts.min.iife.js
vendored
File diff suppressed because one or more lines are too long
2
dist/frappe-charts.min.iife.js.map
vendored
2
dist/frappe-charts.min.iife.js.map
vendored
File diff suppressed because one or more lines are too long
2
docs/assets/js/frappe-charts.min.js
vendored
2
docs/assets/js/frappe-charts.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
13
docs/assets/js/index.min.js
vendored
13
docs/assets/js/index.min.js
vendored
@ -46,6 +46,12 @@ var HEATMAP_COLORS_YELLOW = ['#ebedf0', '#fdf436', '#ffc700', '#ff9100', '#06001
|
|||||||
|
|
||||||
// Universal constants
|
// Universal constants
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the value of a number upto 2 decimal places.
|
||||||
|
* @param {Number} d Any number
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether or not two given arrays are equal.
|
* Returns whether or not two given arrays are equal.
|
||||||
* @param {Array} arr1 First array
|
* @param {Array} arr1 First array
|
||||||
@ -114,7 +120,6 @@ var MONTH_NAMES_SHORT = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug",
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
// https://stackoverflow.com/a/11252167/6495043
|
|
||||||
|
|
||||||
|
|
||||||
function clone(date) {
|
function clone(date) {
|
||||||
@ -155,6 +160,8 @@ function addDays(date, numberOfDays) {
|
|||||||
date.setDate(date.getDate() + numberOfDays);
|
date.setDate(date.getDate() + numberOfDays);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Composite Chart
|
||||||
|
// ================================================================================
|
||||||
var reportCountList = [152, 222, 199, 287, 534, 709, 1179, 1256, 1632, 1856, 1850];
|
var reportCountList = [152, 222, 199, 287, 534, 709, 1179, 1256, 1632, 1856, 1850];
|
||||||
|
|
||||||
var lineCompositeData = {
|
var lineCompositeData = {
|
||||||
@ -324,6 +331,9 @@ var demoConfig = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// import { lineComposite, barComposite } from './demoConfig';
|
||||||
|
// ================================================================================
|
||||||
|
|
||||||
var Chart = frappe.Chart; // eslint-disable-line no-undef
|
var Chart = frappe.Chart; // eslint-disable-line no-undef
|
||||||
|
|
||||||
var lc = demoConfig.lineComposite;
|
var lc = demoConfig.lineComposite;
|
||||||
@ -670,3 +680,4 @@ document.querySelector('.export-heatmap').addEventListener('click', function ()
|
|||||||
});
|
});
|
||||||
|
|
||||||
}());
|
}());
|
||||||
|
//# sourceMappingURL=index.min.js.map
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@ -65,7 +65,7 @@ redirect_to: "https://frappe.io/charts"
|
|||||||
},
|
},
|
||||||
|
|
||||||
title: "My Awesome Chart",
|
title: "My Awesome Chart",
|
||||||
type: 'axis-mixed', // or 'bar', 'line', 'pie', 'percentage'
|
type: 'axis-mixed', // or 'bar', 'line', 'pie', 'percentage', 'donut'
|
||||||
height: 300,
|
height: 300,
|
||||||
colors: ['purple', '#ffa3ef', 'light-blue'],
|
colors: ['purple', '#ffa3ef', 'light-blue'],
|
||||||
|
|
||||||
@ -82,6 +82,7 @@ redirect_to: "https://frappe.io/charts"
|
|||||||
<div class="btn-group aggr-type-buttons margin-top mx-auto" role="group">
|
<div class="btn-group aggr-type-buttons margin-top mx-auto" role="group">
|
||||||
<button type="button" class="btn btn-sm btn-secondary active" data-type='axis-mixed'>Mixed</button>
|
<button type="button" class="btn btn-sm btn-secondary active" data-type='axis-mixed'>Mixed</button>
|
||||||
<button type="button" class="btn btn-sm btn-secondary" data-type='pie'>Pie Chart</button>
|
<button type="button" class="btn btn-sm btn-secondary" data-type='pie'>Pie Chart</button>
|
||||||
|
<button type="button" class="btn btn-sm btn-secondary" data-type='donut'>Donut Chart</button>
|
||||||
<button type="button" class="btn btn-sm btn-secondary" data-type='percentage'>Percentage Chart</button>
|
<button type="button" class="btn btn-sm btn-secondary" data-type='percentage'>Percentage Chart</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="btn-group export-buttons margin-top mx-auto" role="group">
|
<div class="btn-group export-buttons margin-top mx-auto" role="group">
|
||||||
@ -233,7 +234,7 @@ redirect_to: "https://frappe.io/charts"
|
|||||||
// default: 0
|
// default: 0
|
||||||
},
|
},
|
||||||
|
|
||||||
// Pie/Percentage charts
|
// Pie/Percentage/Donut charts
|
||||||
maxLegendPoints: 6, // default: 20
|
maxLegendPoints: 6, // default: 20
|
||||||
maxSlices: 10, // default: 20
|
maxSlices: 10, // default: 20
|
||||||
|
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import PercentageChart from './charts/PercentageChart';
|
|||||||
import PieChart from './charts/PieChart';
|
import PieChart from './charts/PieChart';
|
||||||
import Heatmap from './charts/Heatmap';
|
import Heatmap from './charts/Heatmap';
|
||||||
import AxisChart from './charts/AxisChart';
|
import AxisChart from './charts/AxisChart';
|
||||||
|
import DonutChart from './charts/DonutChart';
|
||||||
|
|
||||||
const chartTypes = {
|
const chartTypes = {
|
||||||
bar: AxisChart,
|
bar: AxisChart,
|
||||||
@ -12,7 +13,8 @@ const chartTypes = {
|
|||||||
// multiaxis: MultiAxisChart,
|
// multiaxis: MultiAxisChart,
|
||||||
percentage: PercentageChart,
|
percentage: PercentageChart,
|
||||||
heatmap: Heatmap,
|
heatmap: Heatmap,
|
||||||
pie: PieChart
|
pie: PieChart,
|
||||||
|
donut: DonutChart,
|
||||||
};
|
};
|
||||||
|
|
||||||
function getChartByType(chartType = 'line', parent, options) {
|
function getChartByType(chartType = 'line', parent, options) {
|
||||||
|
|||||||
154
src/js/charts/DonutChart.js
Normal file
154
src/js/charts/DonutChart.js
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
import AggregationChart from './AggregationChart';
|
||||||
|
import { getComponent } from '../objects/ChartComponents';
|
||||||
|
import { getOffset } from '../utils/dom';
|
||||||
|
import { getPositionByAngle } from '../utils/helpers';
|
||||||
|
import { makeArcStrokePathStr } from '../utils/draw';
|
||||||
|
import { lightenDarkenColor } from '../utils/colors';
|
||||||
|
import { transform } from '../utils/animation';
|
||||||
|
import { FULL_ANGLE } from '../utils/constants';
|
||||||
|
|
||||||
|
export default class DonutChart extends AggregationChart {
|
||||||
|
constructor(parent, args) {
|
||||||
|
super(parent, args);
|
||||||
|
this.type = 'donut';
|
||||||
|
this.initTimeout = 0;
|
||||||
|
this.init = 1;
|
||||||
|
|
||||||
|
this.setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
configure(args) {
|
||||||
|
super.configure(args);
|
||||||
|
this.mouseMove = this.mouseMove.bind(this);
|
||||||
|
this.mouseLeave = this.mouseLeave.bind(this);
|
||||||
|
|
||||||
|
this.hoverRadio = args.hoverRadio || 0.1;
|
||||||
|
this.config.startAngle = args.startAngle || 0;
|
||||||
|
|
||||||
|
this.clockWise = args.clockWise || false;
|
||||||
|
this.strokeWidth = args.strokeWidth || 30;
|
||||||
|
}
|
||||||
|
|
||||||
|
calc() {
|
||||||
|
super.calc();
|
||||||
|
let s = this.state;
|
||||||
|
this.radius = (this.height > this.width ? this.center.x - (this.strokeWidth / 2) : this.center.y - (this.strokeWidth / 2));
|
||||||
|
|
||||||
|
const { radius, clockWise } = this;
|
||||||
|
|
||||||
|
const prevSlicesProperties = s.slicesProperties || [];
|
||||||
|
s.sliceStrings = [];
|
||||||
|
s.slicesProperties = [];
|
||||||
|
let curAngle = 180 - this.config.startAngle;
|
||||||
|
|
||||||
|
s.sliceTotals.map((total, i) => {
|
||||||
|
const startAngle = curAngle;
|
||||||
|
const originDiffAngle = (total / s.grandTotal) * FULL_ANGLE;
|
||||||
|
const diffAngle = clockWise ? -originDiffAngle : originDiffAngle;
|
||||||
|
const endAngle = curAngle = curAngle + diffAngle;
|
||||||
|
const startPosition = getPositionByAngle(startAngle, radius);
|
||||||
|
const endPosition = getPositionByAngle(endAngle, radius);
|
||||||
|
|
||||||
|
const prevProperty = this.init && prevSlicesProperties[i];
|
||||||
|
|
||||||
|
let curStart,curEnd;
|
||||||
|
if(this.init) {
|
||||||
|
curStart = prevProperty ? prevProperty.startPosition : startPosition;
|
||||||
|
curEnd = prevProperty ? prevProperty.endPosition : startPosition;
|
||||||
|
} else {
|
||||||
|
curStart = startPosition;
|
||||||
|
curEnd = endPosition;
|
||||||
|
}
|
||||||
|
const curPath = makeArcStrokePathStr(curStart, curEnd, this.center, this.radius, this.clockWise);
|
||||||
|
|
||||||
|
s.sliceStrings.push(curPath);
|
||||||
|
s.slicesProperties.push({
|
||||||
|
startPosition,
|
||||||
|
endPosition,
|
||||||
|
value: total,
|
||||||
|
total: s.grandTotal,
|
||||||
|
startAngle,
|
||||||
|
endAngle,
|
||||||
|
angle: diffAngle
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
this.init = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
setupComponents() {
|
||||||
|
let s = this.state;
|
||||||
|
|
||||||
|
let componentConfigs = [
|
||||||
|
[
|
||||||
|
'donutSlices',
|
||||||
|
{ },
|
||||||
|
function() {
|
||||||
|
return {
|
||||||
|
sliceStrings: s.sliceStrings,
|
||||||
|
colors: this.colors,
|
||||||
|
strokeWidth: this.strokeWidth,
|
||||||
|
};
|
||||||
|
}.bind(this)
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
this.components = new Map(componentConfigs
|
||||||
|
.map(args => {
|
||||||
|
let component = getComponent(...args);
|
||||||
|
return [args[0], component];
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
calTranslateByAngle(property){
|
||||||
|
const{radius,hoverRadio} = this;
|
||||||
|
const position = getPositionByAngle(property.startAngle+(property.angle / 2),radius);
|
||||||
|
return `translate3d(${(position.x) * hoverRadio}px,${(position.y) * hoverRadio}px,0)`;
|
||||||
|
}
|
||||||
|
|
||||||
|
hoverSlice(path,i,flag,e){
|
||||||
|
if(!path) return;
|
||||||
|
const color = this.colors[i];
|
||||||
|
if(flag) {
|
||||||
|
transform(path, this.calTranslateByAngle(this.state.slicesProperties[i]));
|
||||||
|
path.style.stroke = lightenDarkenColor(color, 50);
|
||||||
|
let g_off = getOffset(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
|
||||||
|
? this.formatted_labels[i] : this.state.labels[i]) + ': ';
|
||||||
|
let percent = (this.state.sliceTotals[i] * 100 / this.state.grandTotal).toFixed(1);
|
||||||
|
this.tip.setValues(x, y, {name: title, value: percent + "%"});
|
||||||
|
this.tip.showTip();
|
||||||
|
} else {
|
||||||
|
transform(path,'translate3d(0,0,0)');
|
||||||
|
this.tip.hideTip();
|
||||||
|
path.style.stroke = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bindTooltip() {
|
||||||
|
this.container.addEventListener('mousemove', this.mouseMove);
|
||||||
|
this.container.addEventListener('mouseleave', this.mouseLeave);
|
||||||
|
}
|
||||||
|
|
||||||
|
mouseMove(e){
|
||||||
|
const target = e.target;
|
||||||
|
let slices = this.components.get('donutSlices').store;
|
||||||
|
let prevIndex = this.curActiveSliceIndex;
|
||||||
|
let prevAcitve = this.curActiveSlice;
|
||||||
|
if(slices.includes(target)) {
|
||||||
|
let i = slices.indexOf(target);
|
||||||
|
this.hoverSlice(prevAcitve, prevIndex,false);
|
||||||
|
this.curActiveSlice = target;
|
||||||
|
this.curActiveSliceIndex = i;
|
||||||
|
this.hoverSlice(target, i, true, e);
|
||||||
|
} else {
|
||||||
|
this.mouseLeave();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mouseLeave(){
|
||||||
|
this.hoverSlice(this.curActiveSlice,this.curActiveSliceIndex,false);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -69,6 +69,20 @@ class ChartComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let componentConfigs = {
|
let componentConfigs = {
|
||||||
|
donutSlices: {
|
||||||
|
layerClass: 'donut-slices',
|
||||||
|
makeElements(data) {
|
||||||
|
return data.sliceStrings.map((s, i) => {
|
||||||
|
let slice = makePath(s, 'donut-path', data.colors[i], 'none', data.strokeWidth);
|
||||||
|
slice.style.transition = 'transform .3s;';
|
||||||
|
return slice;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
animateElements(newData) {
|
||||||
|
return this.store.map((slice, i) => animatePathStr(slice, newData.sliceStrings[i]));
|
||||||
|
},
|
||||||
|
},
|
||||||
pieSlices: {
|
pieSlices: {
|
||||||
layerClass: 'pie-slices',
|
layerClass: 'pie-slices',
|
||||||
makeElements(data) {
|
makeElements(data) {
|
||||||
|
|||||||
@ -98,7 +98,8 @@ export const DEFAULT_COLORS = {
|
|||||||
line: DEFAULT_CHART_COLORS,
|
line: DEFAULT_CHART_COLORS,
|
||||||
pie: DEFAULT_CHART_COLORS,
|
pie: DEFAULT_CHART_COLORS,
|
||||||
percentage: DEFAULT_CHART_COLORS,
|
percentage: DEFAULT_CHART_COLORS,
|
||||||
heatmap: HEATMAP_COLORS_GREEN
|
heatmap: HEATMAP_COLORS_GREEN,
|
||||||
|
donut: DEFAULT_CHART_COLORS
|
||||||
};
|
};
|
||||||
|
|
||||||
// Universal constants
|
// Universal constants
|
||||||
|
|||||||
@ -98,13 +98,14 @@ export function wrapInSVGGroup(elements, className='') {
|
|||||||
return g;
|
return g;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function makePath(pathStr, className='', stroke='none', fill='none') {
|
export function makePath(pathStr, className='', stroke='none', fill='none', strokeWidth=0) {
|
||||||
return createSVG('path', {
|
return createSVG('path', {
|
||||||
className: className,
|
className: className,
|
||||||
d: pathStr,
|
d: pathStr,
|
||||||
styles: {
|
styles: {
|
||||||
stroke: stroke,
|
stroke: stroke,
|
||||||
fill: fill
|
fill: fill,
|
||||||
|
'stroke-width': strokeWidth
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -119,6 +120,15 @@ export function makeArcPathStr(startPosition, endPosition, center, radius, clock
|
|||||||
${arcEndX} ${arcEndY} z`;
|
${arcEndX} ${arcEndY} z`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function makeArcStrokePathStr(startPosition, endPosition, center, radius, clockWise=1){
|
||||||
|
let [arcStartX, arcStartY] = [center.x + startPosition.x, center.y + startPosition.y];
|
||||||
|
let [arcEndX, arcEndY] = [center.x + endPosition.x, center.y + endPosition.y];
|
||||||
|
|
||||||
|
return `M${arcStartX} ${arcStartY}
|
||||||
|
A ${radius} ${radius} 0 0 ${clockWise ? 1 : 0}
|
||||||
|
${arcEndX} ${arcEndY}`;
|
||||||
|
}
|
||||||
|
|
||||||
export function makeGradient(svgDefElem, color, lighter = false) {
|
export function makeGradient(svgDefElem, color, lighter = false) {
|
||||||
let gradientId ='path-fill-gradient' + '-' + color + '-' +(lighter ? 'lighter' : 'default');
|
let gradientId ='path-fill-gradient' + '-' + color + '-' +(lighter ? 'lighter' : 'default');
|
||||||
let gradientDef = renderVerticalGradient(svgDefElem, gradientId);
|
let gradientDef = renderVerticalGradient(svgDefElem, gradientId);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user