@@ -0,0 +1,10 @@ | |||
#### Expected Behaviour | |||
#### Actual Behaviour | |||
#### Steps to Reproduce: | |||
* | |||
NOTE: Add a GIF/Screenshot if required. | |||
Frappé Charts version: |
@@ -1,7 +1,14 @@ | |||
<!-- Thank you so much for contributing! We're glad to have you onboard :) --> | |||
<!-- Please help us understand you contribution better with these details --> | |||
###### Explanation About What Code Achieves: | |||
<!-- Please explain why this code is necessary / what it does --> | |||
- Explanation | |||
###### Screenshots/GIFs: | |||
<!-- As this is mainly a visual lib, please include a screenshot/gif if your contribution modifies on-screen components --> | |||
- Screenshot | |||
###### Steps To Test: | |||
<!-- What would someone do to be able to see the effects of your code? --> | |||
- Steps | |||
@@ -10,20 +10,8 @@ | |||
</div> | |||
<p align="center"> | |||
<a href="https://www.npmjs.com/package/frappe-charts"> | |||
<img src="https://img.shields.io/npm/v/frappe-charts.svg?maxAge=2592000"> | |||
</a> | |||
<a href="https://www.npmjs.com/package/frappe-charts"> | |||
<img src="https://img.shields.io/npm/dm/frappe-charts.svg?maxAge=2592000"> | |||
</a> | |||
<a href="https://www.npmjs.com/package/frappe-charts"> | |||
<img src="https://img.shields.io/npm/dt/frappe-charts.svg?maxAge=2592000"> | |||
</a> | |||
<a href="http://github.com/frappe/charts/tree/master/dist/js/frappe-charts.min.js"> | |||
<img src="http://img.badgesize.io/frappe/charts/master/dist/frappe-charts.min.js.svg?compression=gzip"> | |||
</a> | |||
<a href="https://saythanks.io/to/frappe"> | |||
<img src="https://img.shields.io/badge/Say%20Thanks-🦉-1EAEDB.svg?style=flat-square"> | |||
<a href="http://github.com/frappe/charts/tree/master/dist/js/frappe-charts.min.iife.js"> | |||
<img src="http://img.badgesize.io/frappe/charts/master/dist/frappe-charts.min.iife.js.svg?compression=gzip"> | |||
</a> | |||
</p> | |||
@@ -47,7 +35,7 @@ | |||
* ...or include within your HTML | |||
```html | |||
<script src="https://raw.githubusercontent.com/frappe/charts/master/dist/frappe-charts.min.js"></script> | |||
<script src="https://unpkg.com/frappe-charts@0.0.3/dist/frappe-charts.min.iife.js"></script> | |||
``` | |||
#### Usage | |||
@@ -74,7 +62,7 @@ const chart = new Chart({ | |||
parent: '#chart', | |||
title: "My Awesome Chart", | |||
data: data, | |||
type: 'bar', // or 'line', 'scatter', 'percentage' | |||
type: 'bar', // or 'line', 'scatter', 'pie', 'percentage' | |||
height: 250 | |||
}) | |||
``` | |||
@@ -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'); | |||
@@ -349,25 +351,21 @@ document.querySelector('[data-aggregation="average"]').addEventListener("click", | |||
// Heatmap | |||
// ================================================================================ | |||
let heatmap_data = { | |||
1479753000.0: 1, | |||
1498588200.0: 1, | |||
1499193000.0: 1, | |||
1499625000.0: 2, | |||
1500921000.0: 1, | |||
1501612200.0: 1, | |||
1502994600.0: 1, | |||
1503858600.0: 1, | |||
1504809000.0: 3, | |||
1505241000.0: 1, | |||
1506277800.0: 2 | |||
}; | |||
let heatmap_data = {}; | |||
let current_date = new Date(); | |||
let timestamp = current_date.getTime()/1000; | |||
timestamp = Math.floor(timestamp - (timestamp % 86400)).toFixed(1); // convert to midnight | |||
for (var i = 0; i< 375; i++) { | |||
heatmap_data[parseInt(timestamp)] = Math.floor(Math.random() * 6); | |||
timestamp = Math.floor(timestamp - 86400).toFixed(1); | |||
} | |||
new Chart({ | |||
parent: "#chart-heatmap", | |||
data: heatmap_data, | |||
type: 'heatmap', | |||
height: 100, | |||
height: 115, | |||
discrete_domains: 1 // default 0 | |||
}); | |||
@@ -377,24 +375,20 @@ Array.prototype.slice.call( | |||
el.addEventListener('click', (e) => { | |||
let btn = e.target; | |||
let mode = btn.getAttribute('data-mode'); | |||
let discrete_domains = 0; | |||
if(mode === 'discrete') { | |||
new Chart({ | |||
parent: "#chart-heatmap", | |||
data: heatmap_data, | |||
type: 'heatmap', | |||
height: 100, | |||
discrete_domains: 1 // default 0 | |||
}); | |||
} else { | |||
new Chart({ | |||
parent: "#chart-heatmap", | |||
data: heatmap_data, | |||
type: 'heatmap', | |||
height: 100 | |||
}); | |||
discrete_domains = 1; | |||
} | |||
new Chart({ | |||
parent: "#chart-heatmap", | |||
data: heatmap_data, | |||
type: 'heatmap', | |||
height: 115, | |||
discrete_domains: discrete_domains | |||
}); | |||
Array.prototype.slice.call( | |||
btn.parentNode.querySelectorAll('button')).map(el => { | |||
el.classList.remove('active'); | |||
@@ -426,3 +420,5 @@ function shuffle(array) { | |||
return array; | |||
} | |||
@@ -80,7 +80,7 @@ | |||
parent: "#chart", | |||
title: "My Awesome Chart", | |||
data: data, | |||
type: 'bar', // or 'line', 'scatter', 'percentage' | |||
type: 'bar', // or 'line', 'scatter', 'pie', 'percentage' | |||
height: 250 | |||
});</code></pre> | |||
<div id="chart-types" class="border"></div> | |||
@@ -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"> | |||
@@ -125,6 +126,18 @@ | |||
<button type="button" class="btn btn-sm btn-secondary" data-update="add">Add Value</button> | |||
<button type="button" class="btn btn-sm btn-secondary" data-update="remove">Remove Value</button> | |||
</div> | |||
<pre><code class="hljs javascript margin-vertical-px"> ... | |||
// Include specific Y values in input data to be displayed as lines | |||
// (before passing data to a new chart): | |||
data.specific_values = [ | |||
{ | |||
title: "Altitude", | |||
line_type: "dashed", // or "solid" | |||
value: 38 | |||
} | |||
] | |||
...</code></pre> | |||
</div> | |||
</div> | |||
@@ -215,7 +228,7 @@ | |||
parent: "#heatmap", | |||
data: heatmap_data, // object with date/timestamp-value pairs | |||
type: 'heatmap', | |||
height: 100, | |||
height: 115, | |||
discrete_domains: 1 // default 0 | |||
});</code></pre> | |||
</div> | |||
@@ -226,7 +239,8 @@ | |||
<!-- Closing --> | |||
<div class="text-center" style="margin-top: 70px"> | |||
<a href="https://github.com/frappe/charts/archive/master.zip"><button class="large blue button">Download</button></a> | |||
<p class="mt-2"><a href="https://github.com/frappe/charts" target="_blank">View on GitHub</a></p> | |||
<p style="margin-top: 3rem;margin-bottom: 1.5rem;"><a href="https://github.com/frappe/charts" target="_blank">View on GitHub</a></p> | |||
<p style="margin-top: 1rem;"><iframe src="https://ghbtns.com/github-btn.html?user=frappe&repo=charts&type=star&count=true" frameborder="0" scrolling="0" width="94px" height="20px"></iframe></p> | |||
<p>License: MIT</p> | |||
</div> | |||
</div> | |||
@@ -1,11 +1,16 @@ | |||
{ | |||
"name": "frappe-charts", | |||
"version": "0.0.1", | |||
"version": "0.0.3", | |||
"description": "https://frappe.github.io/charts", | |||
"main": "src/scripts/charts.js", | |||
"main": "dist/frappe-charts.min.cjs.js", | |||
"module": "dist/frappe-charts.min.esm.js", | |||
"browser": "dist/frappe-charts.min.iife.js", | |||
"directories": { | |||
"doc": "docs" | |||
}, | |||
"files":[ | |||
"dist" | |||
], | |||
"scripts": { | |||
"test": "echo \"Error: no test specified\" && exit 1", | |||
"watch": "rollup -c --watch", | |||
@@ -10,15 +10,21 @@ import nested from 'postcss-nested'; | |||
import cssnext from 'postcss-cssnext'; | |||
import cssnano from 'cssnano'; | |||
import pkg from './package.json'; | |||
export default [ | |||
{ | |||
input: 'src/scripts/charts.js', | |||
output: { | |||
file: 'dist/frappe-charts.min.js', | |||
format: 'iife', | |||
}, | |||
name: 'Chart', | |||
sourcemap: 'true', | |||
output: [ | |||
{ | |||
file: pkg.main, | |||
format: 'cjs', | |||
}, | |||
{ | |||
file: pkg.module, | |||
format: 'es', | |||
} | |||
], | |||
plugins: [ | |||
postcss({ | |||
extensions: [ '.less' ], | |||
@@ -40,18 +46,23 @@ export default [ | |||
replace({ | |||
exclude: 'node_modules/**', | |||
ENV: JSON.stringify(process.env.NODE_ENV || 'development'), | |||
}), | |||
uglify() | |||
}) | |||
// uglify() | |||
], | |||
}, | |||
{ | |||
input: 'src/scripts/charts.js', | |||
output: { | |||
file: 'docs/assets/js/frappe-charts.min.js', | |||
format: 'iife', | |||
}, | |||
output: [ | |||
{ | |||
file: 'docs/assets/js/frappe-charts.min.js', | |||
format: 'iife', | |||
}, | |||
{ | |||
file: pkg.browser, | |||
format: 'iife', | |||
} | |||
], | |||
name: 'Chart', | |||
sourcemap: 'false', | |||
plugins: [ | |||
postcss({ | |||
extensions: [ '.less' ], | |||
@@ -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) { | |||
@@ -4,12 +4,10 @@ import Chart from '../charts'; | |||
export default class BaseChart { | |||
constructor({ | |||
parent = "", | |||
height = 240, | |||
title = '', subtitle = '', | |||
data = {}, | |||
format_lambdas = {}, | |||
summary = [], | |||
@@ -17,7 +15,10 @@ export default class BaseChart { | |||
is_navigable = 0, | |||
has_legend = 0, | |||
type = '' // eslint-disable-line no-unused-vars | |||
type = '', // eslint-disable-line no-unused-vars | |||
parent, | |||
data | |||
}) { | |||
this.raw_chart_args = arguments[0]; | |||
@@ -37,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); | |||
} | |||
@@ -50,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: [] | |||
}; | |||
@@ -81,6 +83,10 @@ export default class BaseChart { | |||
} | |||
setup() { | |||
if(!this.parent) { | |||
console.error("No parent element to render on was provided."); | |||
return; | |||
} | |||
this.bind_window_events(); | |||
this.refresh(true); | |||
} | |||
@@ -48,7 +48,7 @@ export default class Heatmap extends BaseChart { | |||
} | |||
set_width() { | |||
this.base_width = (this.no_of_cols) * 12; | |||
this.base_width = (this.no_of_cols + 3) * 12 ; | |||
if(this.discrete_domains) { | |||
this.base_width += (12 * 12); | |||
@@ -124,7 +124,8 @@ export default class Heatmap extends BaseChart { | |||
let data_value = 0; | |||
let color_index = 0; | |||
let timestamp = Math.floor(current_date.getTime()/1000).toFixed(1); | |||
let current_timestamp = current_date.getTime()/1000; | |||
let timestamp = Math.floor(current_timestamp - (current_timestamp % 86400)).toFixed(1); | |||
if(this.data[timestamp]) { | |||
data_value = this.data[timestamp]; | |||
@@ -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>`; | |||
} | |||
}); | |||
} | |||
} |
@@ -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' | |||
@@ -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/ | |||
@@ -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; | |||
} | |||