Преглед изворни кода

Merge pull request #28 from sheweichun/master

Add Pie Charts!
tags/1.2.0
Prateeksha Singh пре 7 година
committed by GitHub
родитељ
комит
1f50544bcc
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
12 измењених фајлова са 294 додато и 23 уклоњено
  1. +1
    -1
      dist/frappe-charts.min.js
  2. +1
    -1
      dist/frappe-charts.min.js.map
  3. +1
    -1
      docs/assets/js/frappe-charts.min.js
  4. +1
    -1
      docs/assets/js/frappe-charts.min.js.map
  5. +6
    -2
      docs/assets/js/index.js
  6. +1
    -0
      docs/index.html
  7. +3
    -1
      src/scripts/charts.js
  8. +6
    -5
      src/scripts/charts/BaseChart.js
  9. +220
    -0
      src/scripts/charts/PieChart.js
  10. +19
    -10
      src/scripts/helpers/dom.js
  11. +19
    -0
      src/scripts/helpers/utils.js
  12. +16
    -1
      src/styles/charts.less

+ 1
- 1
dist/frappe-charts.min.js
Разлика између датотеке није приказан због своје велике величине
Прегледај датотеку


+ 1
- 1
dist/frappe-charts.min.js.map
Разлика између датотеке није приказан због своје велике величине
Прегледај датотеку


+ 1
- 1
docs/assets/js/frappe-charts.min.js
Разлика између датотеке није приказан због своје велике величине
Прегледај датотеку


+ 1
- 1
docs/assets/js/frappe-charts.min.js.map
Разлика између датотеке није приказан због своје велике величине
Прегледај датотеку


+ 6
- 2
docs/assets/js/index.js Прегледај датотеку

@@ -100,8 +100,10 @@ Array.prototype.slice.call(
let btn = e.target;
let type = btn.getAttribute('data-type');

type_chart = type_chart.get_different_chart(type);

let newChart = type_chart.get_different_chart(type);
if(newChart){
type_chart = newChart;
}
Array.prototype.slice.call(
btn.parentNode.querySelectorAll('button')).map(el => {
el.classList.remove('active');
@@ -418,3 +420,5 @@ function shuffle(array) {

return array;
}



+ 1
- 0
docs/index.html Прегледај датотеку

@@ -88,6 +88,7 @@
<button type="button" class="btn btn-sm btn-secondary active" data-type='bar'>Bar Chart</button>
<button type="button" class="btn btn-sm btn-secondary" data-type='line'>Line Chart</button>
<button type="button" class="btn btn-sm btn-secondary" data-type='scatter'>Scatter 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='percentage'>Percentage Chart</button>
</div>
<p class="text-muted">


+ 3
- 1
src/scripts/charts.js Прегледај датотеку

@@ -4,6 +4,7 @@ import BarChart from './charts/BarChart';
import LineChart from './charts/LineChart';
import ScatterChart from './charts/ScatterChart';
import PercentageChart from './charts/PercentageChart';
import PieChart from './charts/PieChart';
import Heatmap from './charts/Heatmap';

// if (ENV !== 'production') {
@@ -19,7 +20,8 @@ const chartTypes = {
bar: BarChart,
scatter: ScatterChart,
percentage: PercentageChart,
heatmap: Heatmap
heatmap: Heatmap,
pie: PieChart
};

function getChartByType(chartType = 'line', options) {


+ 6
- 5
src/scripts/charts/BaseChart.js Прегледај датотеку

@@ -38,7 +38,7 @@ export default class BaseChart {
}
this.has_legend = has_legend;

this.chart_types = ['line', 'scatter', 'bar', 'percentage', 'heatmap'];
this.chart_types = ['line', 'scatter', 'bar', 'percentage', 'heatmap', 'pie'];

this.set_margins(height);
}
@@ -51,10 +51,11 @@ export default class BaseChart {

// Only across compatible types
let compatible_types = {
bar: ['line', 'scatter', 'percentage'],
line: ['scatter', 'bar', 'percentage'],
scatter: ['line', 'bar', 'percentage'],
percentage: ['bar', 'line', 'scatter'],
bar: ['line', 'scatter', 'percentage', 'pie'],
line: ['scatter', 'bar', 'percentage', 'pie'],
pie: ['line', 'scatter', 'percentage', 'bar'],
scatter: ['line', 'bar', 'percentage', 'pie'],
percentage: ['bar', 'line', 'scatter', 'pie'],
heatmap: []
};



+ 220
- 0
src/scripts/charts/PieChart.js Прегледај датотеку

@@ -0,0 +1,220 @@
import BaseChart from './BaseChart';
import $ from '../helpers/dom';
import { lightenDarkenColor } from '../helpers/utils';
const ANGLE_RATIO = Math.PI / 180;
const FULL_ANGLE = 360;

export default class PieChart extends BaseChart {
constructor(args) {
super(args);
this.type = 'pie';
this.get_y_label = this.format_lambdas.y_label;
this.get_x_tooltip = this.format_lambdas.x_tooltip;
this.get_y_tooltip = this.format_lambdas.y_tooltip;
this.elements_to_animate = null;
this.hoverRadio = args.hoverRadio || 0.1;
this.max_slices = 10;
this.max_legend_points = 6;
this.isAnimate = false;
this.colors = args.colors;
this.startAngle = args.startAngle || 0;
this.clockWise = args.clockWise || false;
if(!this.colors || this.colors.length < this.data.labels.length) {
this.colors = ['#7cd6fd', '#5e64ff', '#743ee2', '#ff5858', '#ffa00a',
'#FEEF72', '#28a745', '#98d85b', '#b554ff', '#ffa3ef'];
}
this.mouseMove = this.mouseMove.bind(this);
this.mouseLeave = this.mouseLeave.bind(this);
this.setup();
}
setup_values() {
this.centerX = this.width / 2;
this.centerY = this.height / 2;
this.radius = (this.height > this.width ? this.centerX : this.centerY);
this.slice_totals = [];
let all_totals = this.data.labels.map((d, i) => {
let total = 0;
this.data.datasets.map(e => {
total += e.values[i];
});
return [total, d];
}).filter(d => { return d[0] > 0; }); // keep only positive results

let totals = all_totals;

if(all_totals.length > this.max_slices) {
all_totals.sort((a, b) => { return b[0] - a[0]; });

totals = all_totals.slice(0, this.max_slices-1);
let others = all_totals.slice(this.max_slices-1);

let sum_of_others = 0;
others.map(d => {sum_of_others += d[0];});

totals.push([sum_of_others, 'Rest']);

this.colors[this.max_slices-1] = 'grey';
}

this.labels = [];
totals.map(d => {
this.slice_totals.push(d[0]);
this.labels.push(d[1]);
});

this.legend_totals = this.slice_totals.slice(0, this.max_legend_points);
}

setup_utils() { }
static getPositionByAngle(angle,radius){
return {
x:Math.sin(angle * ANGLE_RATIO) * radius,
y:Math.cos(angle * ANGLE_RATIO) * radius,
};
}
makeArcPath(startPosition,endPosition){
const{centerX,centerY,radius,clockWise} = this;
return `M${centerX} ${centerY} L${centerX+startPosition.x} ${centerY+startPosition.y} A ${radius} ${radius} 0 0 ${clockWise ? 1 : 0} ${centerX+endPosition.x} ${centerY+endPosition.y} z`;
}
make_graph_components(init){
const{radius,clockWise} = this;
this.grand_total = this.slice_totals.reduce((a, b) => a + b, 0);
const prevSlicesProperties = this.slicesProperties || [];
this.slices = [];
this.elements_to_animate = [];
this.slicesProperties = [];
let curAngle = 180 - this.startAngle;
this.slice_totals.map((total, i) => {
const startAngle = curAngle;
const originDiffAngle = (total / this.grand_total) * FULL_ANGLE;
const diffAngle = clockWise ? -originDiffAngle : originDiffAngle;
const endAngle = curAngle = curAngle + diffAngle;
const startPosition = PieChart.getPositionByAngle(startAngle,radius);
const endPosition = PieChart.getPositionByAngle(endAngle,radius);
const prevProperty = init && prevSlicesProperties[i];
let curStart,curEnd;
if(init){
curStart = prevProperty?prevProperty.startPosition : startPosition;
curEnd = prevProperty? prevProperty.endPosition : startPosition;
}else{
curStart = startPosition;
curEnd = endPosition;
}
const curPath = this.makeArcPath(curStart,curEnd);
let slice = $.createSVG('path',{
inside:this.draw_area,
className:'pie-path',
style:'transition:transform .3s;',
d:curPath,
fill:this.colors[i]
});
this.slices.push(slice);
this.slicesProperties.push({
startPosition,
endPosition,
value:total,
total:this.grand_total,
startAngle,
endAngle,
angle:diffAngle
});
if(init){
this.elements_to_animate.push([{unit: slice, array: this.slices, index: this.slices.length - 1},
{d:this.makeArcPath(startPosition,endPosition)},
650, "easein",null,{
d:curPath
}]);
}

});
if(init){
this.run_animation();
}
}
run_animation() {
// if(this.isAnimate) return ;
// this.isAnimate = true;
if(!this.elements_to_animate || this.elements_to_animate.length === 0) return;
let anim_svg = $.runSVGAnimation(this.svg, this.elements_to_animate);

if(this.svg.parentNode == this.chart_wrapper) {
this.chart_wrapper.removeChild(this.svg);
this.chart_wrapper.appendChild(anim_svg);

}

// Replace the new svg (data has long been replaced)
setTimeout(() => {
// this.isAnimate = false;
if(anim_svg.parentNode == this.chart_wrapper) {
this.chart_wrapper.removeChild(anim_svg);
this.chart_wrapper.appendChild(this.svg);
}
}, 650);
}

calTranslateByAngle(property){
const{radius,hoverRadio} = this;
const position = PieChart.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;
if(flag){
$.transform(path,this.calTranslateByAngle(this.slicesProperties[i]));
path.setAttribute('fill',lightenDarkenColor(this.colors[i],50));
let g_off = $.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
? this.formatted_labels[i] : this.labels[i]) + ': ';
let percent = (this.slice_totals[i]*100/this.grand_total).toFixed(1);
this.tip.set_values(x, y, title, percent + "%");
this.tip.show_tip();
}else{
$.transform(path,'translate3d(0,0,0)');
this.tip.hide_tip();
path.setAttribute('fill',this.colors[i]);
}
}

mouseMove(e){
const target = e.target;
let prevIndex = this.curActiveSliceIndex;
let prevAcitve = this.curActiveSlice;
for(let i = 0; i < this.slices.length; i++){
if(target === this.slices[i]){
this.hoverSlice(prevAcitve,prevIndex,false);
this.curActiveSlice = target;
this.curActiveSliceIndex = i;
this.hoverSlice(target,i,true,e);
break;
}
}
}
mouseLeave(){
this.hoverSlice(this.curActiveSlice,this.curActiveSliceIndex,false);
}
bind_tooltip() {
this.draw_area.addEventListener('mousemove',this.mouseMove);
this.draw_area.addEventListener('mouseleave',this.mouseLeave);
}

show_summary() {
let x_values = this.formatted_labels && this.formatted_labels.length > 0
? this.formatted_labels : this.labels;
this.legend_totals.map((d, i) => {
if(d) {
let stats = $.create('div', {
className: 'stats',
inside: this.stats_wrapper
});
stats.innerHTML = `<span class="indicator">
<i style="background-color:${this.colors[i]};"></i>
<span class="text-muted">${x_values[i]}:</span>
${d}
</span>`;
}
});
}
}

+ 19
- 10
src/scripts/helpers/dom.js Прегледај датотеку

@@ -2,6 +2,16 @@ export default function $(expr, con) {
return typeof expr === "string"? (con || document).querySelector(expr) : expr || null;
}

const EASING = {
ease: "0.25 0.1 0.25 1",
linear: "0 0 1 1",
// easein: "0.42 0 1 1",
easein: "0.1 0.8 0.2 1",
easeout: "0 0 0.58 1",
easeinout: "0.42 0 0.58 1"
};


$.findNodeIndex = (node) =>
{
var i = 0;
@@ -83,7 +93,6 @@ $.runSVGAnimation = (svg_container, elements) => {
let anim_element, new_element;

element[0] = obj.unit;

[anim_element, new_element] = $.animateSVG(...element);

new_elements.push(new_element);
@@ -108,15 +117,15 @@ $.runSVGAnimation = (svg_container, elements) => {
return anim_svg;
};

$.transform = (element, style)=>{
element.style.transform = style;
element.style.webkitTransform = style;
element.style.msTransform = style;
element.style.mozTransform = style;
element.style.oTransform = style;
};

$.animateSVG = (element, props, dur, easing_type="linear", type=undefined, old_values={}) => {
let easing = {
ease: "0.25 0.1 0.25 1",
linear: "0 0 1 1",
// easein: "0.42 0 1 1",
easein: "0.1 0.8 0.2 1",
easeout: "0 0 0.58 1",
easeinout: "0.42 0 0.58 1"
};

let anim_element = element.cloneNode(true);
let new_element = element.cloneNode(true);
@@ -138,7 +147,7 @@ $.animateSVG = (element, props, dur, easing_type="linear", type=undefined, old_v
begin: "0s",
dur: dur/1000 + "s",
values: current_value + ";" + value,
keySplines: easing[easing_type],
keySplines: EASING[easing_type],
keyTimes: "0;1",
calcMode: "spline",
fill: 'freeze'


+ 19
- 0
src/scripts/helpers/utils.js Прегледај датотеку

@@ -11,6 +11,25 @@ export function arrays_equal(arr1, arr2) {
return are_equal;
}

function limitColor(r){
if (r > 255) return 255;
else if (r < 0) return 0;
return r;
}

export function lightenDarkenColor(col,amt) {
let usePound = false;
if (col[0] == "#") {
col = col.slice(1);
usePound = true;
}
let num = parseInt(col,16);
let r = limitColor((num >> 16) + amt);
let b = limitColor(((num >> 8) & 0x00FF) + amt);
let g = limitColor((num & 0x0000FF) + amt);
return (usePound?"#":"") + (g | (b << 8) | (r << 16)).toString(16);
}

export function shuffle(array) {
// https://stackoverflow.com/a/2450976/6495043
// Awesomeness: https://bost.ocks.org/mike/shuffle/


+ 16
- 1
src/styles/charts.less Прегледај датотеку

@@ -187,6 +187,7 @@
color: #6c7680;
}
.indicator::before,
.indicator i ,
.indicator-right::after {
content: '';
display: inline-block;
@@ -194,7 +195,7 @@
width: 8px;
border-radius: 8px;
}
.indicator::before {
.indicator::before,.indicator i {
margin: 0 4px 0 0px;
}
.indicator-right::after {
@@ -203,71 +204,85 @@

.background.grey,
.indicator.grey::before,
.indicator.grey i,
.indicator-right.grey::after {
background: #bdd3e6;
}
.background.light-grey,
.indicator.light-grey::before,
.indicator.light-grey i,
.indicator-right.light-grey::after {
background: #F0F4F7;
}
.background.blue,
.indicator.blue::before,
.indicator.blue i,
.indicator-right.blue::after {
background: #5e64ff;
}
.background.red,
.indicator.red::before,
.indicator.red i,
.indicator-right.red::after {
background: #ff5858;
}
.background.green,
.indicator.green::before,
.indicator.green i,
.indicator-right.green::after {
background: #28a745;
}
.background.light-green,
.indicator.light-green::before,
.indicator.light-green i,
.indicator-right.light-green::after {
background: #98d85b;
}
.background.orange,
.indicator.orange::before,
.indicator.orange i,
.indicator-right.orange::after {
background: #ffa00a;
}
.background.violet,
.indicator.violet::before,
.indicator.violet i,
.indicator-right.violet::after {
background: #743ee2;
}
.background.dark-grey,
.indicator.dark-grey::before,
.indicator.dark-grey i,
.indicator-right.dark-grey::after {
background: #b8c2cc;
}
.background.black,
.indicator.black::before,
.indicator.black i,
.indicator-right.black::after {
background: #36414C;
}
.background.yellow,
.indicator.yellow::before,
.indicator.yellow i,
.indicator-right.yellow::after {
background: #FEEF72;
}
.background.light-blue,
.indicator.light-blue::before,
.indicator.light-blue i,
.indicator-right.light-blue::after {
background: #7CD6FD;
}
.background.purple,
.indicator.purple::before,
.indicator.purple i,
.indicator-right.purple::after {
background: #b554ff;
}
.background.magenta,
.indicator.magenta::before,
.indicator.magenta i,
.indicator-right.magenta::after {
background: #ffa3ef;
}


Loading…
Откажи
Сачувај