mirror of
https://github.com/chartjs/Chart.js.git
synced 2025-12-08 20:36:08 +00:00
Merge pull request #1159 from etimberg/feature/v2.0dev-nestedConfig
Nested configs + major code refactors
This commit is contained in:
commit
0c91bb96e0
24
bower.json
24
bower.json
@ -1,15 +1,13 @@
|
||||
{
|
||||
"name": "Chart.js",
|
||||
"version": "1.0.2",
|
||||
"description": "Simple HTML5 Charts using the canvas element",
|
||||
"homepage": "https://github.com/nnnick/Chart.js",
|
||||
"author": "nnnick",
|
||||
"main": [
|
||||
"Chart.js"
|
||||
],
|
||||
"devDependencies": {
|
||||
"angular": "~1.3.15",
|
||||
"jquery": "~2.1.4",
|
||||
"flatstrap": "~3.3.1"
|
||||
}
|
||||
"name": "Chart.js",
|
||||
"version": "2.0.0-alpha",
|
||||
"description": "Simple HTML5 Charts using the canvas element",
|
||||
"homepage": "https://github.com/nnnick/Chart.js",
|
||||
"author": "nnnick",
|
||||
"main": [
|
||||
"Chart.js"
|
||||
],
|
||||
"devDependencies": {
|
||||
"jquery": "~2.1.4"
|
||||
}
|
||||
}
|
||||
|
||||
@ -74,135 +74,207 @@ This concept was introduced in Chart.js 1.0 to keep configuration DRY, and allow
|
||||
|
||||
```javascript
|
||||
Chart.defaults.global = {
|
||||
// Boolean - Whether to animate the chart
|
||||
animation: true,
|
||||
// Animation settings
|
||||
animation: {
|
||||
// Length that animation should take in ms assuming 60fps.
|
||||
// Set to 0 to disable animation
|
||||
duration: 1000,
|
||||
|
||||
// Number - Number of animation steps
|
||||
animationSteps: 60,
|
||||
// Easing type. Possible values are:
|
||||
// [easeInOutQuart, linear, easeOutBounce, easeInBack, easeInOutQuad,
|
||||
// easeOutQuart, easeOutQuad, easeInOutBounce, easeOutSine, easeInOutCubic,
|
||||
// easeInExpo, easeInOutBack, easeInCirc, easeInOutElastic, easeOutBack,
|
||||
// easeInQuad, easeInOutExpo, easeInQuart, easeOutQuint, easeInOutCirc,
|
||||
// easeInSine, easeOutExpo, easeOutCirc, easeOutCubic, easeInQuint,
|
||||
// easeInElastic, easeInOutSine, easeInOutQuint, easeInBounce,
|
||||
// easeOutElastic, easeInCubic]
|
||||
easing: "easeOutQuart",
|
||||
|
||||
// String - Animation easing effect
|
||||
// Possible effects are:
|
||||
// [easeInOutQuart, linear, easeOutBounce, easeInBack, easeInOutQuad,
|
||||
// easeOutQuart, easeOutQuad, easeInOutBounce, easeOutSine, easeInOutCubic,
|
||||
// easeInExpo, easeInOutBack, easeInCirc, easeInOutElastic, easeOutBack,
|
||||
// easeInQuad, easeInOutExpo, easeInQuart, easeOutQuint, easeInOutCirc,
|
||||
// easeInSine, easeOutExpo, easeOutCirc, easeOutCubic, easeInQuint,
|
||||
// easeInElastic, easeInOutSine, easeInOutQuint, easeInBounce,
|
||||
// easeOutElastic, easeInCubic]
|
||||
animationEasing: "easeOutQuart",
|
||||
// Function - function to call each time an animation step occurs
|
||||
onProgress: function() {},
|
||||
|
||||
// Boolean - If we should show the scale at all
|
||||
showScale: true,
|
||||
// Function - function to call when animations finish
|
||||
onComplete: function() {},
|
||||
},
|
||||
|
||||
// Boolean - If we want to override with a hard coded scale
|
||||
scaleOverride: false,
|
||||
// Boolean - if true, resize the charts when the page resizes
|
||||
responsive: false,
|
||||
|
||||
// ** Required if scaleOverride is true **
|
||||
// Number - The number of steps in a hard coded scale
|
||||
scaleSteps: null,
|
||||
// Number - The value jump in the hard coded scale
|
||||
scaleStepWidth: null,
|
||||
// Number - The scale starting value
|
||||
scaleStartValue: null,
|
||||
// Boolean - if true, try to maintain the screen aspect ratio
|
||||
maintainAspectRatio: true,
|
||||
|
||||
// String - Colour of the scale line
|
||||
scaleLineColor: "rgba(0,0,0,.1)",
|
||||
// Array - events to bind tooltips to
|
||||
events: ["mousemove", "mouseout", "click", "touchstart", "touchmove", "touchend"],
|
||||
|
||||
// Number - Pixel width of the scale line
|
||||
scaleLineWidth: 1,
|
||||
// Hover settings
|
||||
hover: {
|
||||
// Function - called when the user hovers over the items.
|
||||
// Parameters: array of active elements
|
||||
onHover: null,
|
||||
|
||||
// Boolean - Whether to show labels on the scale
|
||||
scaleShowLabels: true,
|
||||
// String - hover mode. Options are 'single', 'label', and 'dataset'
|
||||
// 'single' gets the nearest element
|
||||
// 'label' gets all of the elements at the given dataset index (do not use for scatter charts)
|
||||
// 'dataset' gets all the elements in the given dataset
|
||||
mode: 'single',
|
||||
|
||||
// Interpolated JS string - can access value
|
||||
scaleLabel: "<%=value%>",
|
||||
// Number - duration (in ms) of the tooltip animation. 0 to disable
|
||||
animationDuration: 400,
|
||||
},
|
||||
|
||||
// Boolean - Whether the scale should stick to integers, not floats even if drawing space is there
|
||||
scaleIntegersOnly: true,
|
||||
// Function - click handler to bind to chart area
|
||||
onClick: null,
|
||||
|
||||
// Boolean - Whether the scale should start at zero, or an order of magnitude down from the lowest value
|
||||
scaleBeginAtZero: false,
|
||||
// Tooltip configuration
|
||||
tooltips: {
|
||||
// Boolean - if true show tooltips
|
||||
enabled: true,
|
||||
|
||||
// String - Scale label font declaration for the scale label
|
||||
scaleFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
|
||||
// Function - custom tooltip function to use
|
||||
custom: null,
|
||||
|
||||
// Number - Scale label font size in pixels
|
||||
scaleFontSize: 12,
|
||||
// String - color of tooltip background
|
||||
backgroundColor: "rgba(0,0,0,0.8)",
|
||||
|
||||
// String - Scale label font weight style
|
||||
scaleFontStyle: "normal",
|
||||
// String - fonts to use
|
||||
fontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
|
||||
|
||||
// String - Scale label font colour
|
||||
scaleFontColor: "#666",
|
||||
// Number - font size
|
||||
fontSize: 10,
|
||||
|
||||
// Boolean - whether or not the chart should be responsive and resize when the browser does.
|
||||
responsive: false,
|
||||
// String - font style
|
||||
fontStyle: "normal",
|
||||
|
||||
// Boolean - whether to maintain the starting aspect ratio or not when responsive, if set to false, will take up entire container
|
||||
maintainAspectRatio: true,
|
||||
// String - font color
|
||||
fontColor: "#fff",
|
||||
|
||||
// Boolean - Determines whether to draw tooltips on the canvas or not
|
||||
showTooltips: true,
|
||||
// String - title fonts
|
||||
titleFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
|
||||
|
||||
// Function - Determines whether to execute the customTooltips function instead of drawing the built in tooltips (See [Advanced - External Tooltips](#advanced-usage-custom-tooltips))
|
||||
customTooltips: false,
|
||||
// Number - title font size
|
||||
titleFontSize: 12,
|
||||
|
||||
// Array - Array of string names to attach tooltip events
|
||||
tooltipEvents: ["mousemove", "touchstart", "touchmove"],
|
||||
// String - title font style
|
||||
titleFontStyle: "bold",
|
||||
|
||||
// String - Tooltip background colour
|
||||
tooltipFillColor: "rgba(0,0,0,0.8)",
|
||||
// String - title font color
|
||||
titleFontColor: "#fff",
|
||||
|
||||
// String - Tooltip label font declaration for the scale label
|
||||
tooltipFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
|
||||
// Number -
|
||||
yPadding: 6,
|
||||
|
||||
// Number - Tooltip label font size in pixels
|
||||
tooltipFontSize: 14,
|
||||
// Number -
|
||||
xPadding: 6,
|
||||
|
||||
// String - Tooltip font weight style
|
||||
tooltipFontStyle: "normal",
|
||||
// Number -
|
||||
caretSize: 8,
|
||||
|
||||
// String - Tooltip label font colour
|
||||
tooltipFontColor: "#fff",
|
||||
// Number - radius of rounded corners
|
||||
cornerRadius: 6,
|
||||
|
||||
// String - Tooltip title font declaration for the scale label
|
||||
tooltipTitleFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
|
||||
// Number -
|
||||
xOffset: 10,
|
||||
|
||||
// Number - Tooltip title font size in pixels
|
||||
tooltipTitleFontSize: 14,
|
||||
// String - template string to use for tooltips in single mode
|
||||
template: [
|
||||
'<% if(label){ %>',
|
||||
'<%=label %>:',
|
||||
'<% } %>',
|
||||
'<%=value %>',
|
||||
].join(''),
|
||||
|
||||
// String - Tooltip title font weight style
|
||||
tooltipTitleFontStyle: "bold",
|
||||
// String - template string to use for tooltips in label mode
|
||||
multiTemplate: [
|
||||
'<%if (datasetLabel){ %>',
|
||||
'<%=datasetLabel %>:',
|
||||
'<% } %>',
|
||||
'<%=value %>'
|
||||
].join(''),
|
||||
|
||||
// String - Tooltip title font colour
|
||||
tooltipTitleFontColor: "#fff",
|
||||
// String -
|
||||
multiKeyBackground: '#fff',
|
||||
},
|
||||
|
||||
// Number - pixel width of padding around tooltip text
|
||||
tooltipYPadding: 6,
|
||||
// String - default grey color. 'rgba(0,0,0,0.1)'
|
||||
defaultColor: defaultColor,
|
||||
|
||||
// Number - pixel width of padding around tooltip text
|
||||
tooltipXPadding: 6,
|
||||
// Element defaults
|
||||
elements: {
|
||||
// Default settings for all line elements
|
||||
line: {
|
||||
// Number - Bezier curve tension. Set to 0 for no bezier curves
|
||||
tension: 0.4,
|
||||
|
||||
// Number - Size of the caret on the tooltip
|
||||
tooltipCaretSize: 8,
|
||||
// String - the fill color
|
||||
backgroundColor: defaultColor,
|
||||
|
||||
// Number - Pixel radius of the tooltip border
|
||||
tooltipCornerRadius: 6,
|
||||
// Number - width of the line
|
||||
borderWidth: 3,
|
||||
|
||||
// Number - Pixel offset from point x to tooltip edge
|
||||
tooltipXOffset: 10,
|
||||
{% raw %}
|
||||
// String - Template string for single tooltips
|
||||
tooltipTemplate: "<%if (label){%><%=label%>: <%}%><%= value %>",
|
||||
{% endraw %}
|
||||
// String - Template string for multiple tooltips
|
||||
multiTooltipTemplate: "<%= value %>",
|
||||
// String = color of the line
|
||||
borderColor: defaultColor,
|
||||
|
||||
// Boolean - if true fill in the area between the line and the x axis with the background color
|
||||
fill: true,
|
||||
|
||||
// Function - Will fire on animation progression.
|
||||
onAnimationProgress: function(){},
|
||||
// Boolean -
|
||||
skipNull: true,
|
||||
|
||||
// Function - Will fire on animation completion.
|
||||
onAnimationComplete: function(){}
|
||||
// Boolean -
|
||||
drawNull: false,
|
||||
},
|
||||
// Settings for all point elements
|
||||
point: {
|
||||
// Number - radius of point circle
|
||||
radius: 3,
|
||||
|
||||
// String - fill color of point
|
||||
backgroundColor: defaultColor,
|
||||
|
||||
// Number - width of stroke of point circle
|
||||
borderWidth: 1,
|
||||
|
||||
// String - stroke color for point
|
||||
borderColor: defaultColor,
|
||||
|
||||
// Number - extra radius added to radius for hit detection
|
||||
hitRadius: 6,
|
||||
|
||||
// Number - radius of point circle when hovered
|
||||
hoverRadius: 4,
|
||||
|
||||
// Number - radius of circle stroke when hovered
|
||||
hoverBorderWidth: 2,
|
||||
},
|
||||
// Settings for all bar elements
|
||||
bar: {
|
||||
// String - fill color of bar
|
||||
backgroundColor: defaultColor,
|
||||
|
||||
// Number - width of stroke of line surrounding bar fill
|
||||
borderWidth: 0,
|
||||
|
||||
// String - Border color
|
||||
borderColor: defaultColor,
|
||||
|
||||
// Number -
|
||||
valueSpacing: 5,
|
||||
|
||||
// Number -
|
||||
datasetSpacing: 1,
|
||||
},
|
||||
// Settings for all slice elements
|
||||
slice: {
|
||||
// String - fill color
|
||||
backgroundColor: defaultColor,
|
||||
|
||||
// String - border color
|
||||
borderColor: "#fff",
|
||||
|
||||
// Number - border stroke width
|
||||
borderWidth: 2,
|
||||
},
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@ -13,7 +13,10 @@ Often, it is used to show trend data, and the comparison of two data sets.
|
||||
|
||||
###Example usage
|
||||
```javascript
|
||||
var myLineChart = new Chart(ctx).Line(data, options);
|
||||
var myLineChart = new Chart(ctx).Line({
|
||||
data: data,
|
||||
options: options
|
||||
});
|
||||
```
|
||||
###Data structure
|
||||
|
||||
@ -23,22 +26,57 @@ var data = {
|
||||
datasets: [
|
||||
{
|
||||
label: "My First dataset",
|
||||
fillColor: "rgba(220,220,220,0.2)",
|
||||
strokeColor: "rgba(220,220,220,1)",
|
||||
pointColor: "rgba(220,220,220,1)",
|
||||
pointStrokeColor: "#fff",
|
||||
pointHighlightFill: "#fff",
|
||||
pointHighlightStroke: "rgba(220,220,220,1)",
|
||||
data: [65, 59, 80, 81, 56, 55, 40]
|
||||
|
||||
// Boolean - if true fill the area under the line
|
||||
fill: false,
|
||||
|
||||
// String - the color to fill the area under the line with if fill is true
|
||||
backgroundColor: "rgba(220,220,220,0.2)",
|
||||
|
||||
// The properties below allow an array to be specified to change the value of the item at the given index
|
||||
|
||||
// String or array - Line color
|
||||
borderColor: "rgba(220,220,220,1)",
|
||||
|
||||
// String or array - Point stroke color
|
||||
pointBorderColor: "rgba(220,220,220,1)",
|
||||
|
||||
// String or array - Point fill color
|
||||
pointBackgroundColor: "#fff",
|
||||
|
||||
// Number or array - Stroke width of point border
|
||||
pointBorderWidth: 1,
|
||||
|
||||
// Number or array - Radius of point when hovered
|
||||
pointHoverRadius: 5,
|
||||
|
||||
// String or array - point background color when hovered
|
||||
pointHoverBackgroundColor: "rgba(220,220,220,1)",
|
||||
|
||||
// Point border color when hovered
|
||||
pointHoverBorderColor: "rgba(220,220,220,1)",
|
||||
|
||||
// Number or array - border width of point when hovered
|
||||
pointBorderWidth: 2,
|
||||
|
||||
// The actual data
|
||||
data: [65, 59, 80, 81, 56, 55, 40],
|
||||
|
||||
// String - If specified, binds the dataset to a certain y-axis. If not specified, the first y-axis is used.
|
||||
yAxisID: "y-axis-1",
|
||||
},
|
||||
{
|
||||
label: "My Second dataset",
|
||||
fillColor: "rgba(151,187,205,0.2)",
|
||||
strokeColor: "rgba(151,187,205,1)",
|
||||
pointColor: "rgba(151,187,205,1)",
|
||||
pointStrokeColor: "#fff",
|
||||
pointHighlightFill: "#fff",
|
||||
pointHighlightStroke: "rgba(151,187,205,1)",
|
||||
fill: false,
|
||||
backgroundColor: "rgba(220,220,220,0.2)",
|
||||
borderColor: "rgba(220,220,220,1)",
|
||||
pointBorderColor: "rgba(220,220,220,1)",
|
||||
pointBackgroundColor: "#fff",
|
||||
pointBorderWidth: 1,
|
||||
pointHoverRadius: 5,
|
||||
pointHoverBackgroundColor: "rgba(220,220,220,1)",
|
||||
pointHoverBorderColor: "rgba(220,220,220,1)",
|
||||
pointBorderWidth: 2,
|
||||
data: [28, 48, 40, 19, 86, 27, 90]
|
||||
}
|
||||
]
|
||||
@ -56,65 +94,164 @@ These are the customisation options specific to Line charts. These options are m
|
||||
|
||||
```javascript
|
||||
{
|
||||
// Boolean - if true, line stack on top of each other along the y axis
|
||||
stacked: false,
|
||||
|
||||
///Boolean - Whether grid lines are shown across the chart
|
||||
scaleShowGridLines : true,
|
||||
hover: {
|
||||
// String - We use a label hover mode since the x axis displays data by the index in the dataset
|
||||
mode: "label"
|
||||
},
|
||||
|
||||
//String - Colour of the grid lines
|
||||
scaleGridLineColor : "rgba(0,0,0,.05)",
|
||||
scales: {
|
||||
// The line chart officially supports only 1 x-axis but uses an array to keep the API consistent. Use a scatter chart if you need multiple x axes.
|
||||
xAxes: [{
|
||||
// String - type of axis to use. Should not be changed from 'dataset'. To use a 'linear' axis on the x, use the scatter chart type
|
||||
scaleType: "dataset", // scatter should not use a dataset axis
|
||||
|
||||
//Number - Width of the grid lines
|
||||
scaleGridLineWidth : 1,
|
||||
// Boolean - if true, show the scale
|
||||
display: true,
|
||||
|
||||
//Boolean - Whether to show horizontal lines (except X axis)
|
||||
scaleShowHorizontalLines: true,
|
||||
// String - position of the scale. possible options are "top" and "bottom" for dataset scales
|
||||
position: "bottom",
|
||||
|
||||
//Boolean - Whether to show vertical lines (except Y axis)
|
||||
scaleShowVerticalLines: true,
|
||||
// String - id of the axis so that data can bind to it
|
||||
id: "x-axis-1", // need an ID so datasets can reference the scale
|
||||
|
||||
//Boolean - Whether the line is curved between points
|
||||
bezierCurve : true,
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
// Boolean - if true, show the grid lines
|
||||
show: true,
|
||||
|
||||
//Number - Tension of the bezier curve between points
|
||||
bezierCurveTension : 0.4,
|
||||
// String - color of the grid lines
|
||||
color: "rgba(0, 0, 0, 0.05)",
|
||||
|
||||
//Boolean - Whether to show a dot for each point
|
||||
pointDot : true,
|
||||
// Number - width of the grid lines
|
||||
lineWidth: 1,
|
||||
|
||||
//Number - Radius of each point dot in pixels
|
||||
pointDotRadius : 4,
|
||||
// Boolean - if true draw lines on the chart area
|
||||
drawOnChartArea: true,
|
||||
|
||||
//Number - Pixel width of point dot stroke
|
||||
pointDotStrokeWidth : 1,
|
||||
// Boolean - if true draw ticks in the axis area
|
||||
drawTicks: true,
|
||||
|
||||
//Number - amount extra to add to the radius to cater for hit detection outside the drawn point
|
||||
pointHitDetectionRadius : 20,
|
||||
// Number - width of the grid line for the first index (index 0)
|
||||
zeroLineWidth: 1,
|
||||
|
||||
//Boolean - Whether to show a stroke for datasets
|
||||
datasetStroke : true,
|
||||
// String - color of the grid line for the first index
|
||||
zeroLineColor: "rgba(0,0,0,0.25)",
|
||||
|
||||
//Number - Pixel width of dataset stroke
|
||||
datasetStrokeWidth : 2,
|
||||
// Boolean - if true, offset labels from grid lines
|
||||
offsetGridLines: false,
|
||||
},
|
||||
|
||||
//Boolean - Whether to fill the dataset with a colour
|
||||
datasetFill : true,
|
||||
{% raw %}
|
||||
//String - A legend template
|
||||
legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<datasets.length; i++){%><li><span style=\"background-color:<%=datasets[i].strokeColor%>\"></span><%if(datasets[i].label){%><%=datasets[i].label%><%}%></li><%}%></ul>"
|
||||
{% endraw %}
|
||||
// label settings
|
||||
labels: {
|
||||
// Boolean - if true show labels
|
||||
show: true,
|
||||
|
||||
//Boolean - Whether to horizontally center the label and point dot inside the grid
|
||||
offsetGridLines : false
|
||||
// String - template string for labels
|
||||
template: "<%=value%>",
|
||||
|
||||
// Number - label font size
|
||||
fontSize: 12,
|
||||
|
||||
// String - label font style
|
||||
fontStyle: "normal",
|
||||
|
||||
// String - label font color
|
||||
fontColor: "#666",
|
||||
|
||||
// String - label font family
|
||||
fontFamily: "Helvetica Neue",
|
||||
},
|
||||
}],
|
||||
yAxes: [{
|
||||
// String - type of axis. 'linear' is the default but extensions may provide other types such as logarithmic
|
||||
scaleType: "linear",
|
||||
|
||||
// Boolean - if true, show the scale
|
||||
display: true,
|
||||
|
||||
// String - position of axis. Vertical axes can have either "left" or "right"
|
||||
position: "left",
|
||||
|
||||
// ID of the axis for data binding
|
||||
id: "y-axis-1",
|
||||
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
// Boolean - if true, show the grid lines
|
||||
show: true,
|
||||
|
||||
// String - color of the grid lines
|
||||
color: "rgba(0, 0, 0, 0.05)",
|
||||
|
||||
// Number - width of the grid lines
|
||||
lineWidth: 1,
|
||||
|
||||
// Boolean - if true draw lines on the chart area
|
||||
drawOnChartArea: true,
|
||||
|
||||
// Boolean - if true draw ticks in the axis area
|
||||
drawTicks: true,
|
||||
|
||||
// Number - width of the grid line representing a numerical value of 0
|
||||
zeroLineWidth: 1,
|
||||
|
||||
// String - color of the grid line representing a numerical value of 0
|
||||
zeroLineColor: "rgba(0,0,0,0.25)",
|
||||
},
|
||||
|
||||
// Boolean - if true ensures that the scale always has a 0 point
|
||||
beginAtZero: false,
|
||||
|
||||
// Object - if specified, allows the user to override the step generation algorithm.
|
||||
// Contains the following values
|
||||
// start: // number to start at
|
||||
// stepWidth: // size of step
|
||||
// steps: // number of steps
|
||||
override: null,
|
||||
|
||||
// label settings
|
||||
labels: {
|
||||
// Boolean - if true show labels
|
||||
show: true,
|
||||
|
||||
// String - template string for labels
|
||||
template: "<%=value%>",
|
||||
|
||||
// Function - if specified this is passed the tick value, index, and the array of all tick values. Returns a string that is used as the label for that value
|
||||
userCallback: null,
|
||||
|
||||
// Number - label font size
|
||||
fontSize: 12,
|
||||
|
||||
// String - label font style
|
||||
fontStyle: "normal",
|
||||
|
||||
// String - label font color
|
||||
fontColor: "#666",
|
||||
|
||||
// String - label font family
|
||||
fontFamily: "Helvetica Neue",
|
||||
},
|
||||
}],
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
You can override these for your `Chart` instance by passing a second argument into the `Line` method as an object with the keys you want to override.
|
||||
You can override these for your `Chart` instance by passing a member `options` into the `Line` method.
|
||||
|
||||
For example, we could have a line chart without bezier curves between points by doing the following:
|
||||
For example, we could have a line chart display without an x axis by doing the following. The config merge is smart enough to handle arrays so that you do not need to specify all axis settings to change one thing.
|
||||
|
||||
```javascript
|
||||
new Chart(ctx).Line(data, {
|
||||
bezierCurve: false
|
||||
new Chart(ctx).Line({
|
||||
data: data,
|
||||
options: {
|
||||
xAxes: [{
|
||||
show: false
|
||||
}]
|
||||
}
|
||||
});
|
||||
// This will create a chart with all of the default options, merged from the global config,
|
||||
// and the Line chart defaults, but this particular instance will have `bezierCurve` set to false.
|
||||
|
||||
@ -14,7 +14,10 @@ It is sometimes used to show trend data, and the comparison of multiple data set
|
||||
|
||||
### Example usage
|
||||
```javascript
|
||||
var myBarChart = new Chart(ctx).Bar(data, options);
|
||||
var myBarChart = new Chart(ctx).Bar({
|
||||
data: data,
|
||||
options: options
|
||||
});
|
||||
```
|
||||
|
||||
### Data structure
|
||||
@ -25,18 +28,36 @@ var data = {
|
||||
datasets: [
|
||||
{
|
||||
label: "My First dataset",
|
||||
fillColor: "rgba(220,220,220,0.5)",
|
||||
strokeColor: "rgba(220,220,220,0.8)",
|
||||
highlightFill: "rgba(220,220,220,0.75)",
|
||||
highlightStroke: "rgba(220,220,220,1)",
|
||||
data: [65, 59, 80, 81, 56, 55, 40]
|
||||
|
||||
// The properties below allow an array to be specified to change the value of the item at the given index
|
||||
// String or array - the bar color
|
||||
backgroundColor: "rgba(220,220,220,0.2)",
|
||||
|
||||
// String or array - bar stroke color
|
||||
borderColor: "rgba(220,220,220,1)",
|
||||
|
||||
// Number or array - bar border width
|
||||
borderWidth: 1,
|
||||
|
||||
// String or array - fill color when hovered
|
||||
hoverBackgroundColor: "rgba(220,220,220,0.2)",
|
||||
|
||||
// String or array - border color when hovered
|
||||
hoverBorderColor: "rgba(220,220,220,1)",
|
||||
|
||||
// The actual data
|
||||
data: [65, 59, 80, 81, 56, 55, 40],
|
||||
|
||||
// String - If specified, binds the dataset to a certain y-axis. If not specified, the first y-axis is used.
|
||||
yAxisID: "y-axis-1",
|
||||
},
|
||||
{
|
||||
label: "My Second dataset",
|
||||
fillColor: "rgba(151,187,205,0.5)",
|
||||
strokeColor: "rgba(151,187,205,0.8)",
|
||||
highlightFill: "rgba(151,187,205,0.75)",
|
||||
highlightStroke: "rgba(151,187,205,1)",
|
||||
backgroundColor: "rgba(220,220,220,0.2)",
|
||||
borderColor: "rgba(220,220,220,1)",
|
||||
borderWidth: 1,
|
||||
hoverBackgroundColor: "rgba(220,220,220,0.2)",
|
||||
hoverBorderColor: "rgba(220,220,220,1)",
|
||||
data: [28, 48, 40, 19, 86, 27, 90]
|
||||
}
|
||||
]
|
||||
@ -53,40 +74,150 @@ These are the customisation options specific to Bar charts. These options are me
|
||||
|
||||
```javascript
|
||||
{
|
||||
//Boolean - Whether the scale should start at zero, or an order of magnitude down from the lowest value
|
||||
scaleBeginAtZero : true,
|
||||
// Boolean - if true, bars stack on top of each other
|
||||
stacked: false,
|
||||
|
||||
//Boolean - Whether grid lines are shown across the chart
|
||||
scaleShowGridLines : true,
|
||||
hover: {
|
||||
// String - We use a label hover mode since the x axis displays data by the index in the dataset
|
||||
mode: "label"
|
||||
},
|
||||
|
||||
//String - Colour of the grid lines
|
||||
scaleGridLineColor : "rgba(0,0,0,.05)",
|
||||
scales: {
|
||||
// The bar chart officially supports only 1 x-axis but uses an array to keep the API consistent. Use a scatter chart if you need multiple x axes.
|
||||
xAxes: [{
|
||||
// String - type of axis to use. Should not be changed from 'dataset'.
|
||||
scaleType: "dataset", // scatter should not use a dataset axis
|
||||
|
||||
//Number - Width of the grid lines
|
||||
scaleGridLineWidth : 1,
|
||||
// Boolean - if true, show the scale
|
||||
display: true,
|
||||
|
||||
//Boolean - Whether to show horizontal lines (except X axis)
|
||||
scaleShowHorizontalLines: true,
|
||||
// String - position of the scale. possible options are "top" and "bottom" for dataset scales
|
||||
position: "bottom",
|
||||
|
||||
//Boolean - Whether to show vertical lines (except Y axis)
|
||||
scaleShowVerticalLines: true,
|
||||
// String - id of the axis so that data can bind to it
|
||||
id: "x-axis-1", // need an ID so datasets can reference the scale
|
||||
|
||||
//Boolean - If there is a stroke on each bar
|
||||
barShowStroke : true,
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
// Boolean - if true, show the grid lines
|
||||
show: true,
|
||||
|
||||
//Number - Pixel width of the bar stroke
|
||||
barStrokeWidth : 2,
|
||||
// String - color of the grid lines
|
||||
color: "rgba(0, 0, 0, 0.05)",
|
||||
|
||||
//Number - Spacing between each of the X value sets
|
||||
barValueSpacing : 5,
|
||||
// Number - width of the grid lines
|
||||
lineWidth: 1,
|
||||
|
||||
//Number - Spacing between data sets within X values
|
||||
barDatasetSpacing : 1,
|
||||
{% raw %}
|
||||
//String - A legend template
|
||||
legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<datasets.length; i++){%><li><span style=\"background-color:<%=datasets[i].fillColor%>\"></span><%if(datasets[i].label){%><%=datasets[i].label%><%}%></li><%}%></ul>"
|
||||
{% endraw %}
|
||||
}
|
||||
// Boolean - if true draw lines on the chart area
|
||||
drawOnChartArea: true,
|
||||
|
||||
// Boolean - if true draw ticks in the axis area
|
||||
drawTicks: true,
|
||||
|
||||
// Number - width of the grid line for the first index (index 0)
|
||||
zeroLineWidth: 1,
|
||||
|
||||
// String - color of the grid line for the first index
|
||||
zeroLineColor: "rgba(0,0,0,0.25)",
|
||||
|
||||
// Boolean - if true, offset labels from grid lines
|
||||
offsetGridLines: false,
|
||||
},
|
||||
|
||||
// label settings
|
||||
labels: {
|
||||
// Boolean - if true show labels
|
||||
show: true,
|
||||
|
||||
// String - template string for labels
|
||||
template: "<%=value%>",
|
||||
|
||||
// Number - label font size
|
||||
fontSize: 12,
|
||||
|
||||
// String - label font style
|
||||
fontStyle: "normal",
|
||||
|
||||
// String - label font color
|
||||
fontColor: "#666",
|
||||
|
||||
// String - label font family
|
||||
fontFamily: "Helvetica Neue",
|
||||
},
|
||||
}],
|
||||
yAxes: [{
|
||||
// String - type of axis. 'linear' is the default but extensions may provide other types such as logarithmic
|
||||
scaleType: "linear",
|
||||
|
||||
// Boolean - if true, show the scale
|
||||
display: true,
|
||||
|
||||
// String - position of axis. Vertical axes can have either "left" or "right"
|
||||
position: "left",
|
||||
|
||||
// ID of the axis for data binding
|
||||
id: "y-axis-1",
|
||||
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
// Boolean - if true, show the grid lines
|
||||
show: true,
|
||||
|
||||
// String - color of the grid lines
|
||||
color: "rgba(0, 0, 0, 0.05)",
|
||||
|
||||
// Number - width of the grid lines
|
||||
lineWidth: 1,
|
||||
|
||||
// Boolean - if true draw lines on the chart area
|
||||
drawOnChartArea: true,
|
||||
|
||||
// Boolean - if true draw ticks in the axis area
|
||||
drawTicks: true,
|
||||
|
||||
// Number - width of the grid line representing a numerical value of 0
|
||||
zeroLineWidth: 1,
|
||||
|
||||
// String - color of the grid line representing a numerical value of 0
|
||||
zeroLineColor: "rgba(0,0,0,0.25)",
|
||||
},
|
||||
|
||||
// Boolean - if true ensures that the scale always has a 0 point
|
||||
beginAtZero: false,
|
||||
|
||||
// Object - if specified, allows the user to override the step generation algorithm.
|
||||
// Contains the following values
|
||||
// start: // number to start at
|
||||
// stepWidth: // size of step
|
||||
// steps: // number of steps
|
||||
override: null,
|
||||
|
||||
// label settings
|
||||
labels: {
|
||||
// Boolean - if true show labels
|
||||
show: true,
|
||||
|
||||
// String - template string for labels
|
||||
template: "<%=value%>",
|
||||
|
||||
// Function - if specified this is passed the tick value, index, and the array of all tick values. Returns a string that is used as the label for that value
|
||||
userCallback: null,
|
||||
|
||||
// Number - label font size
|
||||
fontSize: 12,
|
||||
|
||||
// String - label font style
|
||||
fontStyle: "normal",
|
||||
|
||||
// String - label font color
|
||||
fontColor: "#666",
|
||||
|
||||
// String - label font family
|
||||
fontFamily: "Helvetica Neue",
|
||||
},
|
||||
}],
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
You can override these for your `Chart` instance by passing a second argument into the `Bar` method as an object with the keys you want to override.
|
||||
@ -94,8 +225,11 @@ You can override these for your `Chart` instance by passing a second argument in
|
||||
For example, we could have a bar chart without a stroke on each bar by doing the following:
|
||||
|
||||
```javascript
|
||||
new Chart(ctx).Bar(data, {
|
||||
barShowStroke: false
|
||||
new Chart(ctx).Bar({
|
||||
data: data,
|
||||
options: {
|
||||
barShowStroke: false
|
||||
}
|
||||
});
|
||||
// This will create a chart with all of the default options, merged from the global config,
|
||||
// and the Bar chart defaults but this particular instance will have `barShowStroke` set to false.
|
||||
|
||||
58
package.json
58
package.json
@ -1,31 +1,31 @@
|
||||
{
|
||||
"name": "chart.js",
|
||||
"homepage": "http://www.chartjs.org",
|
||||
"description": "Simple HTML5 charts using the canvas element.",
|
||||
"version": "1.0.2",
|
||||
"main": "Chart.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nnnick/Chart.js.git"
|
||||
},
|
||||
"dependences": {},
|
||||
"devDependencies": {
|
||||
"color": "git://github.com/chartjs/color",
|
||||
"gulp": "3.5.x",
|
||||
"gulp-concat": "~2.1.x",
|
||||
"gulp-connect": "~2.0.5",
|
||||
"gulp-html-validator": "^0.0.2",
|
||||
"gulp-jshint": "~1.5.1",
|
||||
"gulp-replace": "^0.4.0",
|
||||
"gulp-size": "~0.4.0",
|
||||
"gulp-uglify": "~0.2.x",
|
||||
"gulp-util": "~2.2.x",
|
||||
"inquirer": "^0.5.1",
|
||||
"jquery": "^2.1.4",
|
||||
"onecolor": "^2.5.0",
|
||||
"semver": "^3.0.1"
|
||||
},
|
||||
"spm": {
|
||||
"main": "Chart.js"
|
||||
}
|
||||
"name": "chart.js",
|
||||
"homepage": "http://www.chartjs.org",
|
||||
"description": "Simple HTML5 charts using the canvas element.",
|
||||
"version": "2.0.0-alpha",
|
||||
"main": "Chart.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nnnick/Chart.js.git"
|
||||
},
|
||||
"dependences": {},
|
||||
"devDependencies": {
|
||||
"color": "git://github.com/chartjs/color",
|
||||
"gulp": "3.5.x",
|
||||
"gulp-concat": "~2.1.x",
|
||||
"gulp-connect": "~2.0.5",
|
||||
"gulp-html-validator": "^0.0.2",
|
||||
"gulp-jshint": "~1.5.1",
|
||||
"gulp-replace": "^0.4.0",
|
||||
"gulp-size": "~0.4.0",
|
||||
"gulp-uglify": "~0.2.x",
|
||||
"gulp-util": "~2.2.x",
|
||||
"inquirer": "^0.5.1",
|
||||
"jquery": "^2.1.4",
|
||||
"onecolor": "^2.5.0",
|
||||
"semver": "^3.0.1"
|
||||
},
|
||||
"spm": {
|
||||
"main": "Chart.js"
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,6 +19,9 @@
|
||||
var randomColorFactor = function() {
|
||||
return Math.round(Math.random() * 255);
|
||||
};
|
||||
var randomColor = function() {
|
||||
return 'rgba(' + randomColorFactor() + ',' + randomColorFactor() + ',' + randomColorFactor() + ',.7)';
|
||||
};
|
||||
|
||||
var barChartData = {
|
||||
labels: ["January", "February", "March", "April", "May", "June", "July"],
|
||||
@ -34,7 +37,7 @@
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
|
||||
}, {
|
||||
label: 'Dataset 3',
|
||||
backgroundColor: "rgba(151,187,205,0.5)",
|
||||
backgroundColor: [randomColor(), randomColor(), randomColor(), randomColor(), randomColor(), randomColor(), randomColor()],
|
||||
yAxisID: "y-axis-1",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
|
||||
}]
|
||||
@ -120,7 +123,12 @@
|
||||
|
||||
$('#randomizeData').click(function() {
|
||||
$.each(barChartData.datasets, function(i, dataset) {
|
||||
dataset.backgroundColor = 'rgba(' + randomColorFactor() + ',' + randomColorFactor() + ',' + randomColorFactor() + ',.7)';
|
||||
if (Chart.helpers.isArray(dataset.backgroundColor)) {
|
||||
dataset.backgroundColor= [randomColor(), randomColor(), randomColor(), randomColor(), randomColor(), randomColor(), randomColor()];
|
||||
} else {
|
||||
dataset.backgroundColor= randomColor();
|
||||
}
|
||||
|
||||
dataset.data = [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()];
|
||||
|
||||
});
|
||||
|
||||
@ -34,27 +34,15 @@
|
||||
label: 'Dataset 3',
|
||||
backgroundColor: "rgba(151,187,205,0.5)",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
|
||||
}, {
|
||||
label: 'Dataset 4',
|
||||
backgroundColor: "rgba(151,187,205,0.5)",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
|
||||
}, {
|
||||
label: 'Dataset 3',
|
||||
backgroundColor: "rgba(151,187,205,0.5)",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
|
||||
}]
|
||||
|
||||
};
|
||||
window.onload = function() {
|
||||
var ctx = document.getElementById("canvas").getContext("2d");
|
||||
window.myBar = new Chart(ctx).Bar({
|
||||
data: barChartData,
|
||||
data: barChartData,
|
||||
options: {
|
||||
responsive: true,
|
||||
hoverMode: 'label',
|
||||
scaleBeginAtZero: false,
|
||||
hoverAnimationDuration: 400,
|
||||
stacked: true,
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@ -24,52 +24,83 @@
|
||||
<button id="randomizeData">Randomize Data</button>
|
||||
<script>
|
||||
var randomScalingFactor = function() {
|
||||
return Math.round(Math.random() * 100)
|
||||
return Math.round(Math.random() * 100);
|
||||
};
|
||||
var randomColorFactor = function() {
|
||||
return Math.round(Math.random() * 255)
|
||||
return Math.round(Math.random() * 255);
|
||||
};
|
||||
|
||||
var doughnutData = {
|
||||
data: [{
|
||||
value: randomScalingFactor(),
|
||||
backgroundColor: "#F7464A",
|
||||
label: "Red"
|
||||
var config = {
|
||||
data: {
|
||||
datasets: [{
|
||||
data: [
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
],
|
||||
backgroundColor: [
|
||||
"#F7464A",
|
||||
"#46BFBD",
|
||||
"#FDB45C",
|
||||
"#949FB1",
|
||||
"#4D5360",
|
||||
],
|
||||
}, {
|
||||
value: randomScalingFactor(),
|
||||
backgroundColor: "#46BFBD",
|
||||
label: "Green"
|
||||
data: [
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
],
|
||||
backgroundColor: [
|
||||
"#F7464A",
|
||||
"#46BFBD",
|
||||
"#FDB45C",
|
||||
"#949FB1",
|
||||
"#4D5360",
|
||||
],
|
||||
}, {
|
||||
value: randomScalingFactor(),
|
||||
backgroundColor: "#FDB45C",
|
||||
label: "Yellow"
|
||||
}, {
|
||||
value: randomScalingFactor(),
|
||||
backgroundColor: "#949FB1",
|
||||
label: "Grey"
|
||||
}, {
|
||||
value: randomScalingFactor(),
|
||||
backgroundColor: "#4D5360",
|
||||
label: "Dark Grey"
|
||||
}
|
||||
|
||||
]
|
||||
data: [
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
],
|
||||
backgroundColor: [
|
||||
"#F7464A",
|
||||
"#46BFBD",
|
||||
"#FDB45C",
|
||||
"#949FB1",
|
||||
"#4D5360",
|
||||
],
|
||||
}],
|
||||
labels: [
|
||||
"Red",
|
||||
"Green",
|
||||
"Yellow",
|
||||
"Grey",
|
||||
"Dark Grey"
|
||||
]
|
||||
},
|
||||
options: {
|
||||
responsive: true
|
||||
}
|
||||
};
|
||||
|
||||
window.onload = function() {
|
||||
var ctx = document.getElementById("chart-area").getContext("2d");
|
||||
window.myDoughnut = new Chart(ctx).Doughnut({
|
||||
data: doughnutData,
|
||||
options: {
|
||||
responsive: true
|
||||
}
|
||||
});
|
||||
window.myDoughnut = new Chart(ctx).Doughnut(config);
|
||||
};
|
||||
|
||||
$('#randomizeData').click(function() {
|
||||
$.each(doughnutData.data, function(i, datapoint) {
|
||||
datapoint.value = randomScalingFactor();
|
||||
datapoint.backgroundColor = 'rgba(' + randomColorFactor() + ',' + randomColorFactor() + ',' + randomColorFactor() + ',.7)';
|
||||
$.each(config.data.datasets, function(i, piece) {
|
||||
$.each(piece.data, function(j, value) {
|
||||
config.data.datasets[i].data[j] = randomScalingFactor();
|
||||
});
|
||||
});
|
||||
window.myDoughnut.update();
|
||||
});
|
||||
|
||||
@ -22,50 +22,42 @@
|
||||
return 'rgba(' + Math.round(Math.random() * 255) + ',' + Math.round(Math.random() * 255) + ',' + Math.round(Math.random() * 255) + ',' + (opacity || '.3') + ')';
|
||||
};
|
||||
|
||||
var lineChartData = {
|
||||
labels: ["January", "February", "March", "April", "May", "June", "July"],
|
||||
datasets: [{
|
||||
label: "My First dataset",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
|
||||
}, {
|
||||
label: "My Second dataset",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
|
||||
}]
|
||||
var config = {
|
||||
data: {
|
||||
labels: ["January", "February", "March", "April", "May", "June", "July"],
|
||||
datasets: [{
|
||||
label: "My First dataset",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()],
|
||||
fill: false,
|
||||
}, {
|
||||
label: "My Second dataset",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()],
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true
|
||||
}
|
||||
};
|
||||
|
||||
$.each(lineChartData.datasets, function(i, dataset) {
|
||||
$.each(config.data.datasets, function(i, dataset) {
|
||||
dataset.borderColor = randomColor(0.4);
|
||||
dataset.backgroundColor = randomColor(1);
|
||||
dataset.backgroundColor = randomColor(0.5);
|
||||
dataset.pointBorderColor = randomColor(0.7);
|
||||
dataset.pointBackgroundColor = randomColor(0.5);
|
||||
dataset.pointBorderWidth = 1;
|
||||
});
|
||||
|
||||
console.log(lineChartData);
|
||||
console.log(config.data);
|
||||
|
||||
window.onload = function() {
|
||||
var ctx = document.getElementById("canvas").getContext("2d");
|
||||
window.myLine = new Chart(ctx).Line({
|
||||
data: lineChartData,
|
||||
options: {
|
||||
responsive: true,
|
||||
hoverMode: 'label',
|
||||
stacked: false,
|
||||
scales: {
|
||||
xAxes: [{
|
||||
gridLines: {
|
||||
offsetGridLines: false
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
});
|
||||
window.myLine = new Chart(ctx).Line(config);
|
||||
};
|
||||
|
||||
$('#randomizeData').click(function() {
|
||||
lineChartData.datasets[0].data = [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()];
|
||||
config.data.datasets[0].data = [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()];
|
||||
|
||||
lineChartData.datasets[1].data = [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()];
|
||||
config.data.datasets[1].data = [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()];
|
||||
|
||||
window.myLine.update();
|
||||
});
|
||||
|
||||
155
samples/pie.html
155
samples/pie.html
@ -1,72 +1,101 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Pie Chart</title>
|
||||
<script src="../Chart.js"></script>
|
||||
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="canvas-holder">
|
||||
<canvas id="chart-area" width="300" height="300"/>
|
||||
</div>
|
||||
<button id="randomizeData">Randomize Data</button>
|
||||
|
||||
<script>
|
||||
<head>
|
||||
<title>Pie Chart</title>
|
||||
<script src="../Chart.js"></script>
|
||||
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
|
||||
</head>
|
||||
|
||||
var randomScalingFactor = function(){ return Math.round(Math.random()*100)};
|
||||
var randomColorFactor = function(){ return Math.round(Math.random()*255)};
|
||||
<body>
|
||||
<div id="canvas-holder">
|
||||
<canvas id="chart-area" width="300" height="300" />
|
||||
</div>
|
||||
<button id="randomizeData">Randomize Data</button>
|
||||
<script>
|
||||
var randomScalingFactor = function() {
|
||||
return Math.round(Math.random() * 100);
|
||||
};
|
||||
var randomColorFactor = function() {
|
||||
return Math.round(Math.random() * 255);
|
||||
};
|
||||
|
||||
var pieData = [
|
||||
{
|
||||
value: randomScalingFactor(),
|
||||
color:"#F7464A",
|
||||
highlight: "#FF5A5E",
|
||||
label: "Red"
|
||||
},
|
||||
{
|
||||
value: randomScalingFactor(),
|
||||
color: "#46BFBD",
|
||||
highlight: "#5AD3D1",
|
||||
label: "Green"
|
||||
},
|
||||
{
|
||||
value: randomScalingFactor(),
|
||||
color: "#FDB45C",
|
||||
highlight: "#FFC870",
|
||||
label: "Yellow"
|
||||
},
|
||||
{
|
||||
value: randomScalingFactor(),
|
||||
color: "#949FB1",
|
||||
highlight: "#A8B3C5",
|
||||
label: "Grey"
|
||||
},
|
||||
{
|
||||
value: randomScalingFactor(),
|
||||
color: "#4D5360",
|
||||
highlight: "#616774",
|
||||
label: "Dark Grey"
|
||||
}
|
||||
var config = {
|
||||
data: {
|
||||
datasets: [{
|
||||
data: [
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
],
|
||||
backgroundColor: [
|
||||
"#F7464A",
|
||||
"#46BFBD",
|
||||
"#FDB45C",
|
||||
"#949FB1",
|
||||
"#4D5360",
|
||||
],
|
||||
}, {
|
||||
data: [
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
],
|
||||
backgroundColor: [
|
||||
"#F7464A",
|
||||
"#46BFBD",
|
||||
"#FDB45C",
|
||||
"#949FB1",
|
||||
"#4D5360",
|
||||
],
|
||||
}, {
|
||||
data: [
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
],
|
||||
backgroundColor: [
|
||||
"#F7464A",
|
||||
"#46BFBD",
|
||||
"#FDB45C",
|
||||
"#949FB1",
|
||||
"#4D5360",
|
||||
],
|
||||
}],
|
||||
labels: [
|
||||
"Red",
|
||||
"Green",
|
||||
"Yellow",
|
||||
"Grey",
|
||||
"Dark Grey"
|
||||
]
|
||||
},
|
||||
options: {
|
||||
responsive: true
|
||||
}
|
||||
};
|
||||
|
||||
];
|
||||
window.onload = function() {
|
||||
var ctx = document.getElementById("chart-area").getContext("2d");
|
||||
window.myPie = new Chart(ctx).Pie(config);
|
||||
};
|
||||
|
||||
window.onload = function(){
|
||||
var ctx = document.getElementById("chart-area").getContext("2d");
|
||||
window.myPie = new Chart(ctx).Pie({
|
||||
data: pieData
|
||||
});
|
||||
};
|
||||
$('#randomizeData').click(function() {
|
||||
$.each(config.data.datasets, function(i, piece) {
|
||||
$.each(piece.data, function(j, value) {
|
||||
config.data.datasets[i].data[j] = randomScalingFactor();
|
||||
//config.data.datasets.backgroundColor[i] = 'rgba(' + randomColorFactor() + ',' + randomColorFactor() + ',' + randomColorFactor() + ',.7)';
|
||||
});
|
||||
});
|
||||
window.myPie.update();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
$('#randomizeData').click(function(){
|
||||
$.each(pieData, function(i, piece){
|
||||
pieData[i].value = randomScalingFactor();
|
||||
pieData[i].color = 'rgba(' + randomColorFactor() + ',' + randomColorFactor() + ',' + randomColorFactor() + ',.7)';
|
||||
});
|
||||
window.myPie.update();
|
||||
});
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@ -1,72 +0,0 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Line Chart</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link href="../bower_components/flatstrap/dist/css/flatstrap.min.css" rel="stylesheet" type="text/css" />
|
||||
<style>
|
||||
input[type="color"] {
|
||||
border: none;
|
||||
color: transparent;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
width: 60px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.options,
|
||||
.datasets {
|
||||
width: 50%;
|
||||
float: left;
|
||||
padding: 10px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body ng-app="app" ng-controller="mainController" class="container">
|
||||
<div style="width:100%">
|
||||
<div>
|
||||
<canvas id="canvas" height="350" width="600"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<label>Chart Type: </label>
|
||||
<select ng-model="chartType" ng-change="init()">
|
||||
<option ng-repeat="type in chartTypes" ng-selected="type.selected" value="{{type.value}}">{{type.name}}</option>
|
||||
</select>
|
||||
<hr>
|
||||
<div class="options">
|
||||
<textarea ng-model="options" style="width:100%;height:400px;"></textarea>
|
||||
</div>
|
||||
<div class="datasets">
|
||||
<button ng-click="randomizeData()" class="btn btn-primary">Randomize Data</button>
|
||||
<hr>
|
||||
<div ng-repeat="dataset in chart.data.datasets">
|
||||
<pre>{{dataset.data}}</pre>
|
||||
<div>
|
||||
borderWidth:
|
||||
<input type="number" ng-model="dataset.borderWidth" ng-change="_chart.update()" />
|
||||
</div>
|
||||
<div>
|
||||
hoverBorderWidth:
|
||||
<input type="number" ng-model="dataset.hoverBorderWidth" ng-change="_chart.update()" />
|
||||
</div>
|
||||
<div>
|
||||
pointBorderWidth:
|
||||
<input type="number" ng-model="dataset.pointBorderWidth" ng-change="_chart.update()" />
|
||||
</div>
|
||||
<div>
|
||||
pointHoverBorderWidth:
|
||||
<input type="number" ng-model="dataset.pointHoverBorderWidth" ng-change="_chart.update()" />
|
||||
</div>
|
||||
<hr>
|
||||
</div>
|
||||
</div>
|
||||
<script src="../Chart.js"></script>
|
||||
<script src="../bower_components/jquery/dist/jquery.min.js"></script>
|
||||
<script src="../bower_components/angular/angular.min.js"></script>
|
||||
<script src="playground.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@ -1,156 +0,0 @@
|
||||
(function() {
|
||||
|
||||
var module = angular.module('app', []);
|
||||
|
||||
module.controller('mainController', function($scope, $timeout) {
|
||||
angular.extend($scope, {
|
||||
|
||||
chart: {},
|
||||
chartTypes: chartTypes(),
|
||||
|
||||
init: init,
|
||||
randomizeData: randomizeData,
|
||||
});
|
||||
|
||||
|
||||
init();
|
||||
|
||||
|
||||
|
||||
function init() {
|
||||
if ($scope._chart) {
|
||||
$scope._chart.destroy();
|
||||
}
|
||||
$scope.ctx = $('canvas')[0].getContext("2d");
|
||||
buildData();
|
||||
buildChart();
|
||||
|
||||
$scope.$watch('options', function(val) {
|
||||
try {
|
||||
angular.extend($scope.chart.options, JSON.parse(val));
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}, true);
|
||||
|
||||
$scope.$watch('chart.options', function() {
|
||||
if ($scope._chart) {
|
||||
$scope._chart.update();
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
function chartTypes() {
|
||||
return [{
|
||||
name: 'Line/Scatter',
|
||||
value: 'Line/Scatter',
|
||||
selected: true
|
||||
}, {
|
||||
name: 'Bar',
|
||||
value: 'Bar',
|
||||
}, {
|
||||
name: 'Pie/Doughnut',
|
||||
value: 'Pie/Doughnut',
|
||||
}, {
|
||||
name: 'Polar',
|
||||
value: 'Polar',
|
||||
}, {
|
||||
name: 'Radar',
|
||||
value: 'Radar',
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
|
||||
function buildData() {
|
||||
$scope.chart.data = groupData();
|
||||
$scope.chart.options = {
|
||||
stacked: true,
|
||||
hoverMode: 'label'
|
||||
};
|
||||
$scope.options = stringify($scope.chart.options);
|
||||
}
|
||||
|
||||
function buildChart() {
|
||||
$scope._chart = new Chart($scope.ctx)[$scope.chartType || 'Line']($scope.chart);
|
||||
}
|
||||
|
||||
function stringify(o) {
|
||||
var cache = [];
|
||||
var result = JSON.stringify(o, function(key, value) {
|
||||
if (typeof value === 'object' && value !== null) {
|
||||
if (cache.indexOf(value) !== -1) {
|
||||
// Circular reference found, discard key
|
||||
return;
|
||||
}
|
||||
// Store value in our collection
|
||||
cache.push(value);
|
||||
}
|
||||
return value;
|
||||
}, 1);
|
||||
cache = null; // Enable garbage collection
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
function groupData() {
|
||||
return {
|
||||
labels: ["January", "February", "March", "April", "May", "June", "July"],
|
||||
datasets: [{
|
||||
label: "Dataset 1",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()],
|
||||
backgroundColor: randomColor(0.8),
|
||||
}, {
|
||||
label: "Dataset 2",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()],
|
||||
backgroundColor: randomColor(0.8),
|
||||
}, {
|
||||
label: "Dataset 3",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()],
|
||||
backgroundColor: randomColor(0.8),
|
||||
}]
|
||||
};
|
||||
}
|
||||
|
||||
function flatData() {
|
||||
return {
|
||||
labels: ["January", "February", "March", "April", "May", "June", "July"],
|
||||
datasets: [{
|
||||
label: "My First dataset",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
|
||||
}, {
|
||||
label: "My Second dataset",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
|
||||
}]
|
||||
};
|
||||
}
|
||||
|
||||
function randomizeData() {
|
||||
|
||||
angular.forEach($scope.chart.data.datasets, function(dataset) {
|
||||
|
||||
dataset.data = [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()];
|
||||
dataset.backgroundColor = randomColor(.8);
|
||||
dataset.borderColor = randomColor(0.7);
|
||||
dataset.pointBackgroundColor = randomColor(1);
|
||||
dataset.pointBorderColor = randomColor(1);
|
||||
|
||||
|
||||
$scope._chart.update();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function randomScalingFactor() {
|
||||
return Math.round(Math.random() * 100 * (Math.random() > 0.5 ? -1 : 1));
|
||||
}
|
||||
|
||||
function randomColor(opacity) {
|
||||
return 'rgba(' + Math.round(Math.random() * 255) + ',' + Math.round(Math.random() * 255) + ',' + Math.round(Math.random() * 255) + ',' + (typeof opacity != "undefined" ? opacity : '.3') + ')';
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
})();
|
||||
@ -22,27 +22,29 @@
|
||||
|
||||
var config = {
|
||||
data: {
|
||||
data: [{
|
||||
value: randomScalingFactor(),
|
||||
backgroundColor: "#F7464A",
|
||||
label: "Red"
|
||||
}, {
|
||||
value: randomScalingFactor(),
|
||||
backgroundColor: "#46BFBD",
|
||||
label: "Green"
|
||||
}, {
|
||||
value: randomScalingFactor(),
|
||||
backgroundColor: "#FDB45C",
|
||||
label: "Yellow"
|
||||
}, {
|
||||
value: randomScalingFactor(),
|
||||
backgroundColor: "#949FB1",
|
||||
label: "Grey"
|
||||
}, {
|
||||
value: randomScalingFactor(),
|
||||
backgroundColor: "#4D5360",
|
||||
label: "Dark Grey"
|
||||
}]
|
||||
datasets: [{
|
||||
data: [
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
],
|
||||
backgroundColor: [
|
||||
"#F7464A",
|
||||
"#46BFBD",
|
||||
"#FDB45C",
|
||||
"#949FB1",
|
||||
"#4D5360",
|
||||
],
|
||||
labels: [
|
||||
"Red",
|
||||
"Green",
|
||||
"Yellow",
|
||||
"Grey",
|
||||
"Dark Grey"
|
||||
]
|
||||
}],
|
||||
},
|
||||
options: {
|
||||
responsive: true
|
||||
@ -55,9 +57,11 @@
|
||||
};
|
||||
|
||||
$('#randomizeData').click(function() {
|
||||
$.each(polarData, function(i, piece) {
|
||||
polarData[i].value = randomScalingFactor();
|
||||
polarData[i].color = 'rgba(' + randomColorFactor() + ',' + randomColorFactor() + ',' + randomColorFactor() + ',.7)';
|
||||
$.each(config.data.datasets, function(i, piece) {
|
||||
$.each(piece.data, function(j, value) {
|
||||
config.data.datasets[i].data[j] = randomScalingFactor();
|
||||
//config.data.datasets.backgroundColor[i] = 'rgba(' + randomColorFactor() + ',' + randomColorFactor() + ',' + randomColorFactor() + ',.7)';
|
||||
});
|
||||
});
|
||||
window.myPolarArea.update();
|
||||
});
|
||||
|
||||
@ -27,14 +27,14 @@
|
||||
label: "My First dataset",
|
||||
backgroundColor: "rgba(220,220,220,0.2)",
|
||||
pointBackgroundColor: "rgba(220,220,220,1)",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
|
||||
data: [null, randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
|
||||
}, {
|
||||
label: "My Second dataset",
|
||||
backgroundColor: "rgba(151,187,205,0.2)",
|
||||
pointBackgroundColor: "rgba(151,187,205,1)",
|
||||
hoverPointBackgroundColor: "#fff",
|
||||
pointHighlightStroke: "rgba(151,187,205,1)",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
|
||||
data: [null, randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
|
||||
472
src/Chart.Bar.js
472
src/Chart.Bar.js
@ -5,15 +5,21 @@
|
||||
Chart = root.Chart,
|
||||
helpers = Chart.helpers;
|
||||
|
||||
|
||||
var defaultConfig = {
|
||||
|
||||
stacked: false,
|
||||
|
||||
hover: {
|
||||
mode: "label"
|
||||
},
|
||||
|
||||
scales: {
|
||||
xAxes: [{
|
||||
scaleType: "dataset", // scatter should not use a dataset axis
|
||||
display: true,
|
||||
position: "bottom",
|
||||
id: "x-axis-1", // need an ID so datasets can reference the scale
|
||||
|
||||
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
show: true,
|
||||
@ -26,11 +32,6 @@
|
||||
offsetGridLines: true,
|
||||
},
|
||||
|
||||
// scale numbers
|
||||
beginAtZero: false,
|
||||
integersOnly: false,
|
||||
override: null,
|
||||
|
||||
// label settings
|
||||
labels: {
|
||||
show: true,
|
||||
@ -46,7 +47,7 @@
|
||||
display: true,
|
||||
position: "left",
|
||||
id: "y-axis-1",
|
||||
|
||||
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
show: true,
|
||||
@ -60,7 +61,6 @@
|
||||
|
||||
// scale numbers
|
||||
beginAtZero: false,
|
||||
integersOnly: false,
|
||||
override: null,
|
||||
|
||||
// label settings
|
||||
@ -75,20 +75,6 @@
|
||||
}],
|
||||
},
|
||||
|
||||
bars: {
|
||||
//Number - Pixel width of the bar border
|
||||
borderWidth: 2,
|
||||
|
||||
//Number - Spacing between each of the X value sets
|
||||
valueSpacing: 5,
|
||||
|
||||
//Number - Spacing between data sets within X values
|
||||
datasetSpacing: 1,
|
||||
},
|
||||
|
||||
//String - A legend template
|
||||
legendTemplate: "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<datasets.length; i++){%><li><span style=\"background-color:<%=datasets[i].backgroundColor%>\"></span><%if(datasets[i].label){%><%=datasets[i].label%><%}%></li><%}%></ul>"
|
||||
|
||||
};
|
||||
|
||||
|
||||
@ -96,22 +82,24 @@
|
||||
name: "Bar",
|
||||
defaults: defaultConfig,
|
||||
initialize: function() {
|
||||
// Events
|
||||
helpers.bindEvents(this, this.options.events, this.onHover);
|
||||
|
||||
//Declare the extension of the default point, to cater for the options passed in to the constructor
|
||||
this.BarClass = Chart.Rectangle.extend({
|
||||
ctx: this.chart.ctx,
|
||||
});
|
||||
var _this = this;
|
||||
|
||||
// Events
|
||||
helpers.bindEvents(this, this.options.events, this.events);
|
||||
|
||||
//Create a new bar for each piece of data
|
||||
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
|
||||
dataset.metaData = [];
|
||||
helpers.each(dataset.data, function(dataPoint, index) {
|
||||
dataset.metaData.push(new this.BarClass());
|
||||
dataset.metaData.push(new Chart.Rectangle({
|
||||
_chart: this.chart,
|
||||
_datasetIndex: datasetIndex,
|
||||
_index: index,
|
||||
}));
|
||||
}, this);
|
||||
|
||||
// The bar chart only supports a single x axis because the x axis is always a dataset axis
|
||||
// The bar chart only supports a single x axis because the x axis is always a dataset axis
|
||||
dataset.xAxisID = this.options.scales.xAxes[0].id;
|
||||
|
||||
if (!dataset.yAxisID) {
|
||||
@ -121,24 +109,6 @@
|
||||
|
||||
// Build and fit the scale. Needs to happen after the axis IDs have been set
|
||||
this.buildScale();
|
||||
Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height);
|
||||
|
||||
// Set defaults for bars
|
||||
this.eachElement(function(bar, index, dataset, datasetIndex) {
|
||||
var xScale = this.scales[this.data.datasets[datasetIndex].xAxisID];
|
||||
var yScale = this.scales[this.data.datasets[datasetIndex].yAxisID];
|
||||
|
||||
helpers.extend(bar, {
|
||||
base: yScale.getPixelForValue(0),
|
||||
width: xScale.calculateBarWidth(this.data.datasets.length),
|
||||
x: xScale.calculateBarX(this.data.datasets.length, datasetIndex, index),
|
||||
y: yScale.calculateBarY(this.data.datasets, datasetIndex, index, this.data.datasets[datasetIndex].data[index]),
|
||||
_datasetIndex: datasetIndex,
|
||||
_index: index,
|
||||
});
|
||||
// Copy to view model
|
||||
bar.save();
|
||||
}, this);
|
||||
|
||||
// Create tooltip instance exclusively for this chart with some defaults.
|
||||
this.tooltip = new Chart.Tooltip({
|
||||
@ -147,165 +117,102 @@
|
||||
_options: this.options,
|
||||
}, this);
|
||||
|
||||
// Need to fit scales before we reset elements.
|
||||
Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height);
|
||||
|
||||
// So that we animate from the baseline
|
||||
this.resetElements();
|
||||
|
||||
// Update the chart with the latest data.
|
||||
this.update();
|
||||
},
|
||||
onHover: function(e) {
|
||||
resetElements: function() {
|
||||
// Update the points
|
||||
this.eachElement(function(bar, index, dataset, datasetIndex) {
|
||||
var xScale = this.scales[this.data.datasets[datasetIndex].xAxisID];
|
||||
var yScale = this.scales[this.data.datasets[datasetIndex].yAxisID];
|
||||
|
||||
var yScalePoint;
|
||||
|
||||
// If exiting chart
|
||||
if (e.type == 'mouseout') {
|
||||
return this;
|
||||
}
|
||||
|
||||
this.lastActive = this.lastActive || [];
|
||||
|
||||
// Find Active Elements
|
||||
this.active = function() {
|
||||
switch (this.options.hover.mode) {
|
||||
case 'single':
|
||||
return this.getElementAtEvent(e);
|
||||
case 'label':
|
||||
return this.getElementsAtEvent(e);
|
||||
case 'dataset':
|
||||
return this.getDatasetAtEvent(e);
|
||||
default:
|
||||
return e;
|
||||
}
|
||||
}.call(this);
|
||||
|
||||
// On Hover hook
|
||||
if (this.options.onHover) {
|
||||
this.options.onHover.call(this, this.active);
|
||||
}
|
||||
|
||||
// Remove styling for last active (even if it may still be active)
|
||||
if (this.lastActive.length) {
|
||||
switch (this.options.hover.mode) {
|
||||
case 'single':
|
||||
this.lastActive[0].backgroundColor = this.data.datasets[this.lastActive[0]._datasetIndex].backgroundColor;
|
||||
this.lastActive[0].borderColor = this.data.datasets[this.lastActive[0]._datasetIndex].borderColor;
|
||||
break;
|
||||
case 'label':
|
||||
for (var i = 0; i < this.lastActive.length; i++) {
|
||||
this.lastActive[i].backgroundColor = this.data.datasets[this.lastActive[i]._datasetIndex].backgroundColor;
|
||||
this.lastActive[i].borderColor = this.data.datasets[this.lastActive[i]._datasetIndex].borderColor;
|
||||
}
|
||||
break;
|
||||
case 'dataset':
|
||||
break;
|
||||
default:
|
||||
// Don't change anything
|
||||
}
|
||||
}
|
||||
|
||||
// Built in hover styling
|
||||
if (this.active.length && this.options.hover.mode) {
|
||||
switch (this.options.hover.mode) {
|
||||
case 'single':
|
||||
this.active[0].backgroundColor = this.data.datasets[this.active[0]._datasetIndex].hoverBackgroundColor || helpers.color(this.active[0].backgroundColor).saturate(0.8).darken(0.2).rgbString();
|
||||
this.active[0].borderColor = this.data.datasets[this.active[0]._datasetIndex].hoverBorderColor || helpers.color(this.active[0].borderColor).saturate(0.8).darken(0.2).rgbString();
|
||||
break;
|
||||
case 'label':
|
||||
for (var i = 0; i < this.active.length; i++) {
|
||||
this.active[i].backgroundColor = this.data.datasets[this.active[i]._datasetIndex].hoverBackgroundColor || helpers.color(this.active[i].backgroundColor).saturate(0.8).darken(0.2).rgbString();
|
||||
this.active[i].borderColor = this.data.datasets[this.active[i]._datasetIndex].hoverBorderColor || helpers.color(this.active[i].borderColor).saturate(0.8).darken(0.2).rgbString();
|
||||
}
|
||||
break;
|
||||
case 'dataset':
|
||||
break;
|
||||
default:
|
||||
// Don't change anything
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Built in Tooltips
|
||||
if (this.options.tooltips.enabled) {
|
||||
|
||||
// The usual updates
|
||||
this.tooltip.initialize();
|
||||
|
||||
// Active
|
||||
if (this.active.length) {
|
||||
helpers.extend(this.tooltip, {
|
||||
opacity: 1,
|
||||
_active: this.active,
|
||||
});
|
||||
|
||||
this.tooltip.update();
|
||||
if (yScale.min < 0 && yScale.max <0) {
|
||||
// all less than 0. use the top
|
||||
yScalePoint = yScale.getPixelForValue(yScale.max);
|
||||
} else if (yScale.min > 0 && yScale.max > 0) {
|
||||
yScalePoint = yScale.getPixelForValue(yScale.min);
|
||||
} else {
|
||||
// Inactive
|
||||
helpers.extend(this.tooltip, {
|
||||
opacity: 0,
|
||||
});
|
||||
yScalePoint = yScale.getPixelForValue(0);
|
||||
}
|
||||
}
|
||||
|
||||
helpers.extend(bar, {
|
||||
// Utility
|
||||
_chart: this.chart,
|
||||
_xScale: xScale,
|
||||
_yScale: yScale,
|
||||
_datasetIndex: datasetIndex,
|
||||
_index: index,
|
||||
|
||||
this.tooltip.pivot();
|
||||
// Desired view properties
|
||||
_model: {
|
||||
x: xScale.calculateBarX(this.data.datasets.length, datasetIndex, index),
|
||||
y: yScalePoint,
|
||||
|
||||
// Hover animations
|
||||
if (!this.animating) {
|
||||
var changed;
|
||||
// Appearance
|
||||
base: yScale.calculateBarBase(datasetIndex, index),
|
||||
width: xScale.calculateBarWidth(this.data.datasets.length),
|
||||
backgroundColor: bar.custom && bar.custom.backgroundColor ? bar.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].backgroundColor, index, this.options.elements.bar.backgroundColor),
|
||||
borderColor: bar.custom && bar.custom.borderColor ? bar.custom.borderColor : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].borderColor, index, this.options.elements.bar.borderColor),
|
||||
borderWidth: bar.custom && bar.custom.borderWidth ? bar.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].borderWidth, index, this.options.elements.bar.borderWidth),
|
||||
|
||||
helpers.each(this.active, function(element, index) {
|
||||
if (element !== this.lastActive[index]) {
|
||||
changed = true;
|
||||
}
|
||||
}, this);
|
||||
|
||||
// If entering, leaving, or changing elements, animate the change via pivot
|
||||
if ((!this.lastActive.length && this.active.length) ||
|
||||
(this.lastActive.length && !this.active.length) ||
|
||||
(this.lastActive.length && this.active.length && changed)) {
|
||||
|
||||
this.stop();
|
||||
this.render(this.options.hoverAnimationDuration);
|
||||
}
|
||||
}
|
||||
|
||||
// Remember Last Active
|
||||
this.lastActive = this.active;
|
||||
return this;
|
||||
// Tooltip
|
||||
label: this.data.labels[index],
|
||||
datasetLabel: this.data.datasets[datasetIndex].label,
|
||||
},
|
||||
});
|
||||
bar.pivot();
|
||||
}, this);
|
||||
},
|
||||
update: function() {
|
||||
// Update the scale sizes
|
||||
Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height);
|
||||
|
||||
this.eachElement(function(bar, index, dataset, datasetIndex) {
|
||||
helpers.extend(bar, {
|
||||
value: this.data.datasets[datasetIndex].data[index],
|
||||
});
|
||||
|
||||
bar.pivot();
|
||||
}, this);
|
||||
|
||||
// Update the points
|
||||
this.eachElement(function(bar, index, dataset, datasetIndex) {
|
||||
var xScale = this.scales[this.data.datasets[datasetIndex].xAxisID];
|
||||
var yScale = this.scales[this.data.datasets[datasetIndex].yAxisID];
|
||||
|
||||
helpers.extend(bar, {
|
||||
base: yScale.calculateBarBase(datasetIndex, index),
|
||||
x: xScale.calculateBarX(this.data.datasets.length, datasetIndex, index),
|
||||
y: yScale.calculateBarY(this.data.datasets, datasetIndex, index, this.data.datasets[datasetIndex].data[index]),
|
||||
width: xScale.calculateBarWidth(this.data.datasets.length),
|
||||
label: this.data.labels[index],
|
||||
datasetLabel: this.data.datasets[datasetIndex].label,
|
||||
borderColor: this.data.datasets[datasetIndex].borderColor,
|
||||
borderWidth: this.data.datasets[datasetIndex].borderWidth,
|
||||
backgroundColor: this.data.datasets[datasetIndex].backgroundColor,
|
||||
// Utility
|
||||
_chart: this.chart,
|
||||
_xScale: xScale,
|
||||
_yScale: yScale,
|
||||
_datasetIndex: datasetIndex,
|
||||
_index: index,
|
||||
});
|
||||
|
||||
// Desired view properties
|
||||
_model: {
|
||||
x: xScale.calculateBarX(this.data.datasets.length, datasetIndex, index),
|
||||
y: yScale.calculateBarY(datasetIndex, index),
|
||||
|
||||
// Appearance
|
||||
base: yScale.calculateBarBase(datasetIndex, index),
|
||||
width: xScale.calculateBarWidth(this.data.datasets.length),
|
||||
backgroundColor: bar.custom && bar.custom.backgroundColor ? bar.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].backgroundColor, index, this.options.elements.bar.backgroundColor),
|
||||
borderColor: bar.custom && bar.custom.borderColor ? bar.custom.borderColor : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].borderColor, index, this.options.elements.bar.borderColor),
|
||||
borderWidth: bar.custom && bar.custom.borderWidth ? bar.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].borderWidth, index, this.options.elements.bar.borderWidth),
|
||||
|
||||
// Tooltip
|
||||
label: this.data.labels[index],
|
||||
datasetLabel: this.data.datasets[datasetIndex].label,
|
||||
},
|
||||
});
|
||||
bar.pivot();
|
||||
}, this);
|
||||
|
||||
|
||||
this.render();
|
||||
},
|
||||
buildScale: function(labels) {
|
||||
var self = this;
|
||||
var self = this;
|
||||
|
||||
// Function to determine the range of all the
|
||||
var calculateYRange = function() {
|
||||
@ -338,6 +245,7 @@
|
||||
var values = positiveValues.concat(negativeValues);
|
||||
this.min = helpers.min(values);
|
||||
this.max = helpers.max(values);
|
||||
|
||||
} else {
|
||||
helpers.each(self.data.datasets, function(dataset) {
|
||||
if (dataset.yAxisID === this.id) {
|
||||
@ -347,7 +255,7 @@
|
||||
} else if (value < this.min) {
|
||||
this.min = value;
|
||||
}
|
||||
|
||||
|
||||
if (this.max === null) {
|
||||
this.max = value;
|
||||
} else if (value > this.max) {
|
||||
@ -374,11 +282,11 @@
|
||||
this.max = this.labels.length;
|
||||
},
|
||||
calculateBaseWidth: function() {
|
||||
return (this.getPixelForValue(null, 1, true) - this.getPixelForValue(null, 0, true)) - (2 * self.options.bars.valueSpacing);
|
||||
return (this.getPixelForValue(null, 1, true) - this.getPixelForValue(null, 0, true)) - (2 * self.options.elements.bar.valueSpacing);
|
||||
},
|
||||
calculateBarWidth: function(datasetCount) {
|
||||
//The padding between datasets is to the right of each bar, providing that there are more than 1 dataset
|
||||
var baseWidth = this.calculateBaseWidth() - ((datasetCount - 1) * self.options.bars.datasetSpacing);
|
||||
var baseWidth = this.calculateBaseWidth() - ((datasetCount - 1) * self.options.elements.bar.datasetSpacing);
|
||||
|
||||
if (self.options.stacked) {
|
||||
return baseWidth;
|
||||
@ -394,7 +302,7 @@
|
||||
return xAbsolute + barWidth / 2;
|
||||
}
|
||||
|
||||
return xAbsolute + (barWidth * datasetIndex) + (datasetIndex * self.options.bars.datasetSpacing) + barWidth / 2;
|
||||
return xAbsolute + (barWidth * datasetIndex) + (datasetIndex * self.options.elements.bar.datasetSpacing) + barWidth / 2;
|
||||
},
|
||||
});
|
||||
this.scales[xScale.id] = xScale;
|
||||
@ -410,18 +318,19 @@
|
||||
var base = 0;
|
||||
|
||||
if (self.options.stacked) {
|
||||
var bar = self.data.datasets[datasetIndex].metaData[index];
|
||||
|
||||
if (bar.value < 0) {
|
||||
var value = self.data.datasets[datasetIndex].data[index];
|
||||
|
||||
if (value < 0) {
|
||||
for (var i = 0; i < datasetIndex; i++) {
|
||||
if (self.data.datasets[i].yAxisID === this.id) {
|
||||
base += self.data.datasets[i].metaData[index].value < base ? self.data.datasets[i].metaData[index].value : 0;
|
||||
base += self.data.datasets[i].data[index] < 0 ? self.data.datasets[i].data[index] : 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (var i = 0; i < datasetIndex; i++) {
|
||||
if (self.data.datasets[i].yAxisID === this.id) {
|
||||
base += self.data.datasets[i].metaData[index].value > base ? self.data.datasets[i].metaData[index].value : 0;
|
||||
for (var j = 0; j < datasetIndex; j++) {
|
||||
if (self.data.datasets[j].yAxisID === this.id) {
|
||||
base += self.data.datasets[j].data[index] > 0 ? self.data.datasets[j].data[index] : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -442,7 +351,9 @@
|
||||
return base;
|
||||
|
||||
},
|
||||
calculateBarY: function(datasets, datasetIndex, barIndex, value) {
|
||||
calculateBarY: function(datasetIndex, index) {
|
||||
|
||||
var value = self.data.datasets[datasetIndex].data[index];
|
||||
|
||||
if (self.options.stacked) {
|
||||
|
||||
@ -450,10 +361,10 @@
|
||||
sumNeg = 0;
|
||||
|
||||
for (var i = 0; i < datasetIndex; i++) {
|
||||
if (datasets[i].metaData[barIndex].value < 0) {
|
||||
sumNeg += datasets[i].metaData[barIndex].value || 0;
|
||||
if (self.data.datasets[i].data[index] < 0) {
|
||||
sumNeg += self.data.datasets[i].data[index] || 0;
|
||||
} else {
|
||||
sumPos += datasets[i].metaData[barIndex].value || 0;
|
||||
sumPos += self.data.datasets[i].data[index] || 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -463,52 +374,27 @@
|
||||
return this.getPixelForValue(sumPos + value);
|
||||
}
|
||||
|
||||
return this.getPixelForValue(0);
|
||||
return this.getPixelForValue(value);
|
||||
}
|
||||
|
||||
var offset = 0;
|
||||
|
||||
for (i = datasetIndex; i < datasets.length; i++) {
|
||||
if (i === datasetIndex && value) {
|
||||
for (var j = datasetIndex; j < self.data.datasets.length; j++) {
|
||||
if (j === datasetIndex && value) {
|
||||
offset += value;
|
||||
} else {
|
||||
offset = offset + (datasets[i].metaData[barIndex].value);
|
||||
offset = offset + value;
|
||||
}
|
||||
}
|
||||
|
||||
return this.getPixelForValue(value);
|
||||
},
|
||||
|
||||
calculateBaseHeight: function() {
|
||||
return (this.getPixelForValue(1) - this.getPixelForValue(0));
|
||||
},
|
||||
id: yAxisOptions.id,
|
||||
});
|
||||
|
||||
this.scales[scale.id] = scale;
|
||||
}, this);
|
||||
},
|
||||
// This should be incorportated into the init as something like a default value. "Reflow" seems like a weird word for a fredraw function
|
||||
redraw: function() {
|
||||
this.eachElement(function(element, index, datasetIndex) {
|
||||
var yScale = this.scales[this.data.datasets[datasetIndex].yAxisID];
|
||||
var base = yScale.getPixelForValue(yScale.min);
|
||||
|
||||
if (yScale.min <= 0 && yScale.max >= 0) {
|
||||
// have a 0 point
|
||||
base = yScale.getPixelForValue(0);
|
||||
} else if (yScale.min < 0 && yScale.max < 0) {
|
||||
// all megative
|
||||
base = yScale.getPixelForValue(yScale.max);
|
||||
}
|
||||
|
||||
helpers.extend(element, {
|
||||
y: base,
|
||||
base: base
|
||||
});
|
||||
});
|
||||
this.render();
|
||||
},
|
||||
draw: function(ease) {
|
||||
|
||||
var easingDecimal = ease || 1;
|
||||
@ -526,7 +412,149 @@
|
||||
|
||||
// Finally draw the tooltip
|
||||
this.tooltip.transition(easingDecimal).draw();
|
||||
}
|
||||
},
|
||||
events: function(e) {
|
||||
|
||||
|
||||
// If exiting chart
|
||||
if (e.type == 'mouseout') {
|
||||
return this;
|
||||
}
|
||||
|
||||
this.lastActive = this.lastActive || [];
|
||||
|
||||
// Find Active Elements
|
||||
this.active = function() {
|
||||
switch (this.options.hover.mode) {
|
||||
case 'single':
|
||||
return this.getElementAtEvent(e);
|
||||
case 'label':
|
||||
return this.getElementsAtEvent(e);
|
||||
case 'dataset':
|
||||
return this.getDatasetAtEvent(e);
|
||||
default:
|
||||
return e;
|
||||
}
|
||||
}.call(this);
|
||||
|
||||
// On Hover hook
|
||||
if (this.options.hover.onHover) {
|
||||
this.options.hover.onHover.call(this, this.active);
|
||||
}
|
||||
|
||||
if (e.type == 'mouseup' || e.type == 'click') {
|
||||
if (this.options.onClick) {
|
||||
this.options.onClick.call(this, e, this.active);
|
||||
}
|
||||
}
|
||||
|
||||
var dataset;
|
||||
var index;
|
||||
// Remove styling for last active (even if it may still be active)
|
||||
if (this.lastActive.length) {
|
||||
switch (this.options.hover.mode) {
|
||||
case 'single':
|
||||
dataset = this.data.datasets[this.lastActive[0]._datasetIndex];
|
||||
index = this.lastActive[0]._index;
|
||||
|
||||
this.lastActive[0]._model.backgroundColor = this.lastActive[0].custom && this.lastActive[0].custom.backgroundColor ? this.lastActive[0].custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, this.options.elements.bar.backgroundColor);
|
||||
this.lastActive[0]._model.borderColor = this.lastActive[0].custom && this.lastActive[0].custom.borderColor ? this.lastActive[0].custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, this.options.elements.bar.borderColor);
|
||||
this.lastActive[0]._model.borderWidth = this.lastActive[0].custom && this.lastActive[0].custom.borderWidth ? this.lastActive[0].custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, this.options.elements.bar.borderWidth);
|
||||
break;
|
||||
case 'label':
|
||||
for (var i = 0; i < this.lastActive.length; i++) {
|
||||
dataset = this.data.datasets[this.lastActive[i]._datasetIndex];
|
||||
index = this.lastActive[i]._index;
|
||||
|
||||
this.lastActive[i]._model.backgroundColor = this.lastActive[i].custom && this.lastActive[i].custom.backgroundColor ? this.lastActive[i].custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, this.options.elements.bar.backgroundColor);
|
||||
this.lastActive[i]._model.borderColor = this.lastActive[i].custom && this.lastActive[i].custom.borderColor ? this.lastActive[i].custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, this.options.elements.bar.borderColor);
|
||||
this.lastActive[i]._model.borderWidth = this.lastActive[i].custom && this.lastActive[i].custom.borderWidth ? this.lastActive[i].custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, this.options.elements.bar.borderWidth);
|
||||
}
|
||||
break;
|
||||
case 'dataset':
|
||||
break;
|
||||
default:
|
||||
// Don't change anything
|
||||
}
|
||||
}
|
||||
|
||||
// Built in hover styling
|
||||
if (this.active.length && this.options.hover.mode) {
|
||||
switch (this.options.hover.mode) {
|
||||
case 'single':
|
||||
dataset = this.data.datasets[this.active[0]._datasetIndex];
|
||||
index = this.active[0]._index;
|
||||
|
||||
this.active[0]._model.backgroundColor = this.active[0].custom && this.active[0].custom.hoverBackgroundColor ? this.active[0].custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.color(this.active[0]._model.backgroundColor).saturate(0.5).darken(0.35).rgbString());
|
||||
this.active[0]._model.borderColor = this.active[0].custom && this.active[0].custom.hoverBorderColor ? this.active[0].custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.hoverBorderColor, index, helpers.color(this.active[0]._model.borderColor).saturate(0.5).darken(0.35).rgbString());
|
||||
this.active[0]._model.borderWidth = this.active[0].custom && this.active[0].custom.hoverBorderWidth ? this.active[0].custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, this.active[0]._model.borderWidth);
|
||||
break;
|
||||
case 'label':
|
||||
for (var i = 0; i < this.active.length; i++) {
|
||||
dataset = this.data.datasets[this.active[i]._datasetIndex];
|
||||
index = this.active[i]._index;
|
||||
|
||||
this.active[i]._model.backgroundColor = this.active[i].custom && this.active[i].custom.hoverBackgroundColor ? this.active[i].custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.color(this.active[i]._model.backgroundColor).saturate(0.5).darken(0.35).rgbString());
|
||||
this.active[i]._model.borderColor = this.active[i].custom && this.active[i].custom.hoverBorderColor ? this.active[i].custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.hoverBorderColor, index, helpers.color(this.active[i]._model.borderColor).saturate(0.5).darken(0.35).rgbString());
|
||||
this.active[i]._model.borderWidth = this.active[i].custom && this.active[i].custom.hoverBorderWidth ? this.active[i].custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, this.active[i]._model.borderWidth);
|
||||
}
|
||||
break;
|
||||
case 'dataset':
|
||||
break;
|
||||
default:
|
||||
// Don't change anything
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Built in Tooltips
|
||||
if (this.options.tooltips.enabled) {
|
||||
|
||||
// The usual updates
|
||||
this.tooltip.initialize();
|
||||
|
||||
// Active
|
||||
if (this.active.length) {
|
||||
this.tooltip._model.opacity = 1;
|
||||
|
||||
helpers.extend(this.tooltip, {
|
||||
_active: this.active,
|
||||
});
|
||||
|
||||
this.tooltip.update();
|
||||
} else {
|
||||
// Inactive
|
||||
this.tooltip._model.opacity = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
this.tooltip.pivot();
|
||||
|
||||
// Hover animations
|
||||
if (!this.animating) {
|
||||
var changed;
|
||||
|
||||
helpers.each(this.active, function(element, index) {
|
||||
if (element !== this.lastActive[index]) {
|
||||
changed = true;
|
||||
}
|
||||
}, this);
|
||||
|
||||
// If entering, leaving, or changing elements, animate the change via pivot
|
||||
if ((!this.lastActive.length && this.active.length) ||
|
||||
(this.lastActive.length && !this.active.length) ||
|
||||
(this.lastActive.length && this.active.length && changed)) {
|
||||
|
||||
this.stop();
|
||||
this.render(this.options.hoverAnimationDuration);
|
||||
}
|
||||
}
|
||||
|
||||
// Remember Last Active
|
||||
this.lastActive = this.active;
|
||||
return this;
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
|
||||
@ -48,116 +48,96 @@
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
var defaultColor = 'rgba(0,0,0,0.1)';
|
||||
|
||||
//Globally expose the defaults to allow for user updating/changing
|
||||
Chart.defaults = {
|
||||
global: {
|
||||
|
||||
// Animation defaults
|
||||
animation: {
|
||||
// Number - Number of animation steps
|
||||
duration: 1000,
|
||||
|
||||
// String - Animation easing effect
|
||||
easing: "easeOutQuart",
|
||||
|
||||
// Function - Will fire on animation progression.
|
||||
onProgress: function() {},
|
||||
|
||||
// Function - Will fire on animation completion.
|
||||
onComplete: function() {},
|
||||
},
|
||||
|
||||
// Boolean - whether or not the chart should be responsive and resize when the browser does.
|
||||
responsive: false,
|
||||
|
||||
// Boolean - whether to maintain the starting aspect ratio or not when responsive, if set to false, will take up entire container
|
||||
responsive: true,
|
||||
maintainAspectRatio: true,
|
||||
|
||||
// Array - Array of string names to attach interaction events
|
||||
events: ["mousemove", "mouseout", "click", "touchstart", "touchmove", "touchend"],
|
||||
|
||||
// Hover defaults
|
||||
hover: {
|
||||
|
||||
// String || boolean
|
||||
mode: 'label', // 'label', 'dataset', 'false'
|
||||
|
||||
//Function(event, activeElements) - Custom hover handler
|
||||
onHover: null,
|
||||
|
||||
//Function - Custom hover handler
|
||||
mode: 'single',
|
||||
animationDuration: 400,
|
||||
},
|
||||
|
||||
//Function(event, clickedElements) - Custom click handler
|
||||
onClick: null,
|
||||
|
||||
// Tooltip Defaults
|
||||
tooltips: {
|
||||
|
||||
// Boolean - Determines whether to draw tooltips on the canvas or not - attaches events to touchmove & mousemove
|
||||
enabled: true,
|
||||
|
||||
// Function - Determines whether to draw built-in tooltip or call custom tooltip function
|
||||
custom: null,
|
||||
|
||||
// String - Tooltip background colour
|
||||
backgroundColor: "rgba(0,0,0,0.8)",
|
||||
|
||||
// String - Tooltip label font declaration for the scale label
|
||||
fontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
|
||||
|
||||
// Number - Tooltip label font size in pixels
|
||||
fontSize: 14,
|
||||
|
||||
// String - Tooltip font weight style
|
||||
fontSize: 10,
|
||||
fontStyle: "normal",
|
||||
|
||||
// String - Tooltip label font colour
|
||||
fontColor: "#fff",
|
||||
|
||||
// String - Tooltip title font declaration for the scale label
|
||||
titleFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
|
||||
|
||||
// Number - Tooltip title font size in pixels
|
||||
titleFontSize: 14,
|
||||
|
||||
// String - Tooltip title font weight style
|
||||
titleFontSize: 12,
|
||||
titleFontStyle: "bold",
|
||||
|
||||
// String - Tooltip title font colour
|
||||
titleFontColor: "#fff",
|
||||
|
||||
// Number - pixel width of padding around text
|
||||
yPadding: 6,
|
||||
|
||||
// Number - pixel width of padding around text
|
||||
xPadding: 6,
|
||||
|
||||
// Number - Size of the caret on the
|
||||
caretSize: 8,
|
||||
|
||||
// Number - Pixel radius of the border
|
||||
cornerRadius: 6,
|
||||
|
||||
// Number - Pixel offset from point x to edge
|
||||
xOffset: 10,
|
||||
|
||||
// String - Template string for singles
|
||||
template: "<%if (label){%><%=label%>: <%}%><%= value %>",
|
||||
|
||||
// String - Template string for singles
|
||||
multiTemplate: "<%if (datasetLabel){%><%=datasetLabel%>: <%}%><%= value %>",
|
||||
|
||||
// String - Colour behind the legend colour block
|
||||
template: [
|
||||
'<% if(label){ %>',
|
||||
'<%=label %>:',
|
||||
'<% } %>',
|
||||
'<%=value %>',
|
||||
].join(''),
|
||||
multiTemplate: [
|
||||
'<%if (datasetLabel){ %>',
|
||||
'<%=datasetLabel %>:',
|
||||
'<% } %>',
|
||||
'<%=value %>'
|
||||
].join(''),
|
||||
multiKeyBackground: '#fff',
|
||||
|
||||
},
|
||||
defaultColor: defaultColor,
|
||||
|
||||
// Color String - Used for undefined Colros
|
||||
defaultColor: 'rgba(0,0,0,0.1)',
|
||||
|
||||
}
|
||||
// Element defaults
|
||||
elements: {
|
||||
line: {
|
||||
tension: 0.4,
|
||||
backgroundColor: defaultColor,
|
||||
borderWidth: 3,
|
||||
borderColor: defaultColor,
|
||||
fill: true, // do we fill in the area between the line and the x axis
|
||||
skipNull: true,
|
||||
drawNull: false,
|
||||
},
|
||||
point: {
|
||||
radius: 3,
|
||||
backgroundColor: defaultColor,
|
||||
borderWidth: 1,
|
||||
borderColor: defaultColor,
|
||||
// Hover
|
||||
hitRadius: 6,
|
||||
hoverRadius: 4,
|
||||
hoverBorderWidth: 2,
|
||||
},
|
||||
bar: {
|
||||
backgroundColor: defaultColor,
|
||||
borderWidth: 0,
|
||||
borderColor: defaultColor,
|
||||
valueSpacing: 5,
|
||||
datasetSpacing: 1,
|
||||
},
|
||||
slice: {
|
||||
backgroundColor: defaultColor,
|
||||
borderColor: "#fff",
|
||||
borderWidth: 2,
|
||||
},
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
//Create a dictionary of chart types, to allow for extension of existing types
|
||||
@ -238,6 +218,17 @@
|
||||
|
||||
return base;
|
||||
},
|
||||
getValueAtIndexOrDefault = helpers.getValueAtIndexOrDefault = function(value, index, defaultValue) {
|
||||
if (!value) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
if (helpers.isArray(value) && index < value.length) {
|
||||
return value[index];
|
||||
}
|
||||
|
||||
return value;
|
||||
},
|
||||
indexOf = helpers.indexOf = function(arrayToSearch, item) {
|
||||
if (Array.prototype.indexOf) {
|
||||
return arrayToSearch.indexOf(item);
|
||||
@ -403,11 +394,11 @@
|
||||
fa = t * d01 / (d01 + d12), // scaling factor for triangle Ta
|
||||
fb = t * d12 / (d01 + d12);
|
||||
return {
|
||||
next: {
|
||||
previous: {
|
||||
x: MiddlePoint.x - fa * (AfterPoint.x - FirstPoint.x),
|
||||
y: MiddlePoint.y - fa * (AfterPoint.y - FirstPoint.y)
|
||||
},
|
||||
previous: {
|
||||
next: {
|
||||
x: MiddlePoint.x + fb * (AfterPoint.x - FirstPoint.x),
|
||||
y: MiddlePoint.y + fb * (AfterPoint.y - FirstPoint.y)
|
||||
}
|
||||
@ -1142,67 +1133,63 @@
|
||||
};
|
||||
|
||||
Chart.Element = function(configuration) {
|
||||
extend(this, {
|
||||
_vm: {},
|
||||
});
|
||||
extend(this, configuration);
|
||||
this.initialize.apply(this, arguments);
|
||||
};
|
||||
extend(Chart.Element.prototype, {
|
||||
initialize: function() {},
|
||||
save: function() {
|
||||
this._vm = clone(this);
|
||||
delete this._vm._vm;
|
||||
delete this._vm._start;
|
||||
return this;
|
||||
},
|
||||
pivot: function() {
|
||||
if (this._start) {
|
||||
this._start = clone(this);
|
||||
helpers.extend(this._start, this._vm);
|
||||
if (!this._view) {
|
||||
this._view = clone(this._model);
|
||||
}
|
||||
this._start = clone(this._view);
|
||||
return this;
|
||||
},
|
||||
transition: function(ease) {
|
||||
if (!this._view) {
|
||||
this._view = clone(this._model);
|
||||
}
|
||||
if (!this._start) {
|
||||
if (!this._vm) {
|
||||
this.save();
|
||||
}
|
||||
this._start = clone(this._vm);
|
||||
this.pivot();
|
||||
}
|
||||
|
||||
each(this, function(value, key) {
|
||||
each(this._model, function(value, key) {
|
||||
|
||||
if (key[0] === '_' || !this.hasOwnProperty(key)) {
|
||||
if (key[0] === '_' || !this._model.hasOwnProperty(key)) {
|
||||
// Only non-underscored properties
|
||||
}
|
||||
|
||||
// Init if doesn't exist
|
||||
else if (!this._vm[key]) {
|
||||
this._vm[key] = value || null;
|
||||
else if (!this._view[key]) {
|
||||
if (typeof value === 'number') {
|
||||
this._view[key] = value * ease;
|
||||
} else {
|
||||
this._view[key] = value || null;
|
||||
}
|
||||
}
|
||||
|
||||
// No unnecessary computations
|
||||
else if (this[key] === this._vm[key]) {
|
||||
else if (this._model[key] === this._view[key]) {
|
||||
// It's the same! Woohoo!
|
||||
}
|
||||
|
||||
// Color transitions if possible
|
||||
else if (typeof value === 'string') {
|
||||
try {
|
||||
var color = helpers.color(this._start[key]).mix(helpers.color(this[key]), ease);
|
||||
this._vm[key] = color.rgbString();
|
||||
var color = helpers.color(this._start[key]).mix(helpers.color(this._model[key]), ease);
|
||||
this._view[key] = color.rgbString();
|
||||
} catch (err) {
|
||||
this._vm[key] = value;
|
||||
this._view[key] = value;
|
||||
}
|
||||
}
|
||||
// Number transitions
|
||||
else if (typeof value === 'number') {
|
||||
|
||||
this._vm[key] = ((this[key] - this._start[key]) * ease) + this._start[key];
|
||||
} else {
|
||||
// Everything else
|
||||
this._vm[key] = value;
|
||||
var startVal = this._start[key] !== undefined ? this._start[key] : 0;
|
||||
this._view[key] = ((this._model[key] - startVal) * ease) + startVal;
|
||||
}
|
||||
// Everything else
|
||||
else {
|
||||
this._view[key] = value;
|
||||
}
|
||||
|
||||
}, this);
|
||||
@ -1214,12 +1201,12 @@
|
||||
},
|
||||
tooltipPosition: function() {
|
||||
return {
|
||||
x: this.x,
|
||||
y: this.y
|
||||
x: this._model.x,
|
||||
y: this._model.y
|
||||
};
|
||||
},
|
||||
hasValue: function() {
|
||||
return isNumber(this.value);
|
||||
return isNumber(this._model.x) && isNumber(this._model.y);
|
||||
}
|
||||
});
|
||||
|
||||
@ -1228,16 +1215,21 @@
|
||||
|
||||
Chart.Point = Chart.Element.extend({
|
||||
inRange: function(mouseX, mouseY) {
|
||||
var vm = this._vm;
|
||||
var vm = this._view;
|
||||
var hoverRange = vm.hoverRadius + vm.radius;
|
||||
return ((Math.pow(mouseX - vm.x, 2) + Math.pow(mouseY - vm.y, 2)) < Math.pow(hoverRange, 2));
|
||||
},
|
||||
inGroupRange: function(mouseX) {
|
||||
var vm = this._vm;
|
||||
return (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + this.hoverRadius, 2));
|
||||
var vm = this._view;
|
||||
|
||||
if (vm) {
|
||||
return (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hoverRadius, 2));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
tooltipPosition: function() {
|
||||
var vm = this._vm;
|
||||
var vm = this._view;
|
||||
return {
|
||||
x: vm.x,
|
||||
y: vm.y,
|
||||
@ -1246,9 +1238,14 @@
|
||||
},
|
||||
draw: function() {
|
||||
|
||||
var vm = this._vm;
|
||||
var vm = this._view;
|
||||
var ctx = this._chart.ctx;
|
||||
|
||||
|
||||
if (vm.skip) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (vm.radius > 0 || vm.borderWidth > 0) {
|
||||
|
||||
ctx.beginPath();
|
||||
@ -1271,54 +1268,75 @@
|
||||
Chart.Line = Chart.Element.extend({
|
||||
draw: function() {
|
||||
|
||||
var vm = this._vm;
|
||||
var vm = this._view;
|
||||
var ctx = this._chart.ctx;
|
||||
var first = vm._points[0];
|
||||
var last = vm._points[vm._points.length - 1];
|
||||
var first = this._children[0];
|
||||
var last = this._children[this._children.length - 1];
|
||||
|
||||
// Draw the background first (so the border is always on top)
|
||||
helpers.each(vm._points, function(point, index) {
|
||||
if (index === 0) {
|
||||
ctx.moveTo(point._vm.x, point._vm.y);
|
||||
} else {
|
||||
if (vm._tension > 0 || 1) {
|
||||
var previous = this.previousPoint(point, vm._points, index);
|
||||
helpers.each(this._children, function(point, index) {
|
||||
var previous = this.previousPoint(point, this._children, index);
|
||||
var next = this.nextPoint(point, this._children, index);
|
||||
|
||||
// First point only
|
||||
if (index === 0) {
|
||||
ctx.moveTo(point._view.x, point._view.y);
|
||||
return;
|
||||
}
|
||||
|
||||
// Start Skip and drag along scale baseline
|
||||
if (point._view.skip && vm.skipNull && !this._loop) {
|
||||
ctx.lineTo(previous._view.x, point._view.y);
|
||||
ctx.moveTo(next._view.x, point._view.y);
|
||||
}
|
||||
// End Skip Stright line from the base line
|
||||
else if (previous._view.skip && vm.skipNull && !this._loop) {
|
||||
ctx.moveTo(point._view.x, previous._view.y);
|
||||
ctx.lineTo(point._view.x, point._view.y);
|
||||
}
|
||||
|
||||
if (previous._view.skip && vm.skipNull) {
|
||||
ctx.moveTo(point._view.x, point._view.y);
|
||||
}
|
||||
// Normal Bezier Curve
|
||||
else {
|
||||
if (vm.tension > 0) {
|
||||
ctx.bezierCurveTo(
|
||||
previous._vm.controlPointNextX,
|
||||
previous._vm.controlPointNextY,
|
||||
point._vm.controlPointPreviousX,
|
||||
point._vm.controlPointPreviousY,
|
||||
point._vm.x,
|
||||
point._vm.y
|
||||
previous._view.controlPointNextX,
|
||||
previous._view.controlPointNextY,
|
||||
point._view.controlPointPreviousX,
|
||||
point._view.controlPointPreviousY,
|
||||
point._view.x,
|
||||
point._view.y
|
||||
);
|
||||
} else {
|
||||
ctx.lineTo(point._vm.x, point._vm.y);
|
||||
ctx.lineTo(point._view.x, point._view.y);
|
||||
}
|
||||
}
|
||||
}, this);
|
||||
|
||||
if (vm.loop) {
|
||||
|
||||
if (vm._tension > 0 || 1) {
|
||||
// For radial scales, loop back around to the first point
|
||||
if (this._loop) {
|
||||
if (vm.tension > 0 && !first._view.skip) {
|
||||
|
||||
ctx.bezierCurveTo(
|
||||
last._vm.controlPointNextX,
|
||||
last._vm.controlPointNextY,
|
||||
first._vm.controlPointPreviousX,
|
||||
first._vm.controlPointPreviousY,
|
||||
first._vm.x,
|
||||
first._vm.y
|
||||
last._view.controlPointNextX,
|
||||
last._view.controlPointNextY,
|
||||
first._view.controlPointPreviousX,
|
||||
first._view.controlPointPreviousY,
|
||||
first._view.x,
|
||||
first._view.y
|
||||
);
|
||||
} else {
|
||||
ctx.lineTo(first._vm.x, first._vm.y);
|
||||
ctx.lineTo(first._view.x, first._view.y);
|
||||
}
|
||||
}
|
||||
|
||||
if (vm._points.length > 0) {
|
||||
// If we had points and want to fill this line, do so.
|
||||
if (this._children.length > 0 && vm.fill) {
|
||||
//Round off the line by going to the base of the chart, back to the start, then fill.
|
||||
ctx.lineTo(vm._points[vm._points.length - 1].x, vm.scaleZero);
|
||||
ctx.lineTo(vm._points[0].x, vm.scaleZero);
|
||||
ctx.lineTo(this._children[this._children.length - 1]._view.x, vm.scaleZero);
|
||||
ctx.lineTo(this._children[0]._view.x, vm.scaleZero);
|
||||
ctx.fillStyle = vm.backgroundColor || Chart.defaults.global.defaultColor;
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
@ -1330,39 +1348,61 @@
|
||||
ctx.strokeStyle = vm.borderColor || Chart.defaults.global.defaultColor;
|
||||
ctx.beginPath();
|
||||
|
||||
helpers.each(vm._points, function(point, index) {
|
||||
helpers.each(this._children, function(point, index) {
|
||||
var previous = this.previousPoint(point, this._children, index);
|
||||
var next = this.nextPoint(point, this._children, index);
|
||||
|
||||
// First point only
|
||||
if (index === 0) {
|
||||
ctx.moveTo(point._vm.x, point._vm.y);
|
||||
} else {
|
||||
if (vm._tension > 0 || 1) {
|
||||
var previous = this.previousPoint(point, vm._points, index);
|
||||
|
||||
ctx.bezierCurveTo(
|
||||
previous._vm.controlPointNextX,
|
||||
previous._vm.controlPointNextY,
|
||||
point._vm.controlPointPreviousX,
|
||||
point._vm.controlPointPreviousY,
|
||||
point._vm.x,
|
||||
point._vm.y
|
||||
);
|
||||
} else {
|
||||
ctx.lineTo(point._vm.x, point._vm.y);
|
||||
}
|
||||
ctx.moveTo(point._view.x, point._view.y);
|
||||
return;
|
||||
}
|
||||
}, this);
|
||||
if (vm.loop) {
|
||||
if (vm._tension > 0 || 1) {
|
||||
|
||||
// Start Skip and drag along scale baseline
|
||||
if (point._view.skip && vm.skipNull && !this._loop) {
|
||||
ctx.moveTo(previous._view.x, point._view.y);
|
||||
ctx.moveTo(next._view.x, point._view.y);
|
||||
return;
|
||||
}
|
||||
// End Skip Stright line from the base line
|
||||
if (previous._view.skip && vm.skipNull && !this._loop) {
|
||||
ctx.moveTo(point._view.x, previous._view.y);
|
||||
ctx.moveTo(point._view.x, point._view.y);
|
||||
return;
|
||||
}
|
||||
|
||||
if (previous._view.skip && vm.skipNull) {
|
||||
ctx.moveTo(point._view.x, point._view.y);
|
||||
return;
|
||||
}
|
||||
// Normal Bezier Curve
|
||||
if (vm.tension > 0) {
|
||||
ctx.bezierCurveTo(
|
||||
last._vm.controlPointNextX,
|
||||
last._vm.controlPointNextY,
|
||||
first._vm.controlPointPreviousX,
|
||||
first._vm.controlPointPreviousY,
|
||||
first._vm.x,
|
||||
first._vm.y
|
||||
previous._view.controlPointNextX,
|
||||
previous._view.controlPointNextY,
|
||||
point._view.controlPointPreviousX,
|
||||
point._view.controlPointPreviousY,
|
||||
point._view.x,
|
||||
point._view.y
|
||||
);
|
||||
} else {
|
||||
ctx.lineTo(first._vm.x, first._vm.y);
|
||||
ctx.lineTo(point._view.x, point._view.y);
|
||||
}
|
||||
}, this);
|
||||
|
||||
if (this._loop && !first._view.skip) {
|
||||
if (vm.tension > 0) {
|
||||
|
||||
ctx.bezierCurveTo(
|
||||
last._view.controlPointNextX,
|
||||
last._view.controlPointNextY,
|
||||
first._view.controlPointPreviousX,
|
||||
first._view.controlPointPreviousY,
|
||||
first._view.x,
|
||||
first._view.y
|
||||
);
|
||||
} else {
|
||||
ctx.lineTo(first._view.x, first._view.y);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1370,17 +1410,33 @@
|
||||
ctx.stroke();
|
||||
|
||||
},
|
||||
nextPoint: function(point, collection, index) {
|
||||
if (this.loop) {
|
||||
return collection[index + 1] || collection[0];
|
||||
}
|
||||
return collection[index + 1] || collection[collection.length - 1];
|
||||
},
|
||||
previousPoint: function(point, collection, index) {
|
||||
return helpers.findPreviousWhere(collection, function() {
|
||||
return true;
|
||||
}, index) || point;
|
||||
if (this.loop) {
|
||||
return collection[index - 1] || collection[collection.length - 1];
|
||||
}
|
||||
return collection[index - 1] || collection[0];
|
||||
},
|
||||
});
|
||||
|
||||
Chart.Arc = Chart.Element.extend({
|
||||
inGroupRange: function(mouseX) {
|
||||
var vm = this._view;
|
||||
|
||||
if (vm) {
|
||||
return (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hoverRadius, 2));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
inRange: function(chartX, chartY) {
|
||||
|
||||
var vm = this._vm;
|
||||
var vm = this._view;
|
||||
|
||||
var pointRelativePosition = helpers.getAngleFromPoint(vm, {
|
||||
x: chartX,
|
||||
@ -1395,7 +1451,7 @@
|
||||
//Ensure within the outside of the arc centre, but inside arc outer
|
||||
},
|
||||
tooltipPosition: function() {
|
||||
var vm = this._vm;
|
||||
var vm = this._view;
|
||||
|
||||
var centreAngle = vm.startAngle + ((vm.endAngle - vm.startAngle) / 2),
|
||||
rangeFromCentre = (vm.outerRadius - vm.innerRadius) / 2 + vm.innerRadius;
|
||||
@ -1407,7 +1463,7 @@
|
||||
draw: function() {
|
||||
|
||||
var ctx = this._chart.ctx;
|
||||
var vm = this._vm;
|
||||
var vm = this._view;
|
||||
|
||||
ctx.beginPath();
|
||||
|
||||
@ -1433,10 +1489,10 @@
|
||||
Chart.Rectangle = Chart.Element.extend({
|
||||
draw: function() {
|
||||
|
||||
var vm = this._vm;
|
||||
var ctx = this._chart.ctx;
|
||||
var vm = this._view;
|
||||
|
||||
var ctx = this.ctx,
|
||||
halfWidth = vm.width / 2,
|
||||
var halfWidth = vm.width / 2,
|
||||
leftX = vm.x - halfWidth,
|
||||
rightX = vm.x + halfWidth,
|
||||
top = vm.base - (vm.base - vm.y),
|
||||
@ -1468,11 +1524,11 @@
|
||||
}
|
||||
},
|
||||
height: function() {
|
||||
var vm = this._vm;
|
||||
var vm = this._view;
|
||||
return vm.base - vm.y;
|
||||
},
|
||||
inRange: function(mouseX, mouseY) {
|
||||
var vm = this._vm;
|
||||
var vm = this._view;
|
||||
if (vm.y < vm.base) {
|
||||
return (mouseX >= vm.x - vm.width / 2 && mouseX <= vm.x + vm.width / 2) && (mouseY >= vm.y && mouseY <= vm.base);
|
||||
} else {
|
||||
@ -1480,11 +1536,11 @@
|
||||
}
|
||||
},
|
||||
inGroupRange: function(mouseX) {
|
||||
var vm = this._vm;
|
||||
var vm = this._view;
|
||||
return (mouseX >= vm.x - vm.width / 2 && mouseX <= vm.x + vm.width / 2);
|
||||
},
|
||||
tooltipPosition: function() {
|
||||
var vm = this._vm;
|
||||
var vm = this._view;
|
||||
if (vm.y < vm.base) {
|
||||
return {
|
||||
x: vm.x,
|
||||
@ -1513,24 +1569,31 @@
|
||||
initialize: function() {
|
||||
var options = this._options;
|
||||
extend(this, {
|
||||
opacity: 0,
|
||||
xPadding: options.tooltips.xPadding,
|
||||
yPadding: options.tooltips.yPadding,
|
||||
xOffset: options.tooltips.xOffset,
|
||||
backgroundColor: options.tooltips.backgroundColor,
|
||||
textColor: options.tooltips.fontColor,
|
||||
_fontFamily: options.tooltips.fontFamily,
|
||||
_fontStyle: options.tooltips.fontStyle,
|
||||
fontSize: options.tooltips.fontSize,
|
||||
titleTextColor: options.tooltips.titleFontColor,
|
||||
_titleFontFamily: options.tooltips.titleFontFamily,
|
||||
_titleFontStyle: options.tooltips.titleFontStyle,
|
||||
titleFontSize: options.tooltips.titleFontSize,
|
||||
caretHeight: options.tooltips.caretSize,
|
||||
cornerRadius: options.tooltips.cornerRadius,
|
||||
legendColorBackground: options.tooltips.multiKeyBackground,
|
||||
labels: [],
|
||||
colors: [],
|
||||
_model: {
|
||||
// Positioning
|
||||
xPadding: options.tooltips.xPadding,
|
||||
yPadding: options.tooltips.yPadding,
|
||||
xOffset: options.tooltips.xOffset,
|
||||
|
||||
// Labels
|
||||
textColor: options.tooltips.fontColor,
|
||||
_fontFamily: options.tooltips.fontFamily,
|
||||
_fontStyle: options.tooltips.fontStyle,
|
||||
fontSize: options.tooltips.fontSize,
|
||||
|
||||
// Title
|
||||
titleTextColor: options.tooltips.titleFontColor,
|
||||
_titleFontFamily: options.tooltips.titleFontFamily,
|
||||
_titleFontStyle: options.tooltips.titleFontStyle,
|
||||
titleFontSize: options.tooltips.titleFontSize,
|
||||
|
||||
// Appearance
|
||||
caretHeight: options.tooltips.caretSize,
|
||||
cornerRadius: options.tooltips.cornerRadius,
|
||||
backgroundColor: options.tooltips.backgroundColor,
|
||||
opacity: 0,
|
||||
legendColorBackground: options.tooltips.multiKeyBackground,
|
||||
},
|
||||
});
|
||||
},
|
||||
update: function() {
|
||||
@ -1539,15 +1602,22 @@
|
||||
|
||||
switch (this._options.hover.mode) {
|
||||
case 'single':
|
||||
helpers.extend(this, {
|
||||
text: template(this._options.tooltips.template, this._active[0]),
|
||||
helpers.extend(this._model, {
|
||||
text: template(this._options.tooltips.template, {
|
||||
// These variables are available in the template function. Add others here
|
||||
element: this._active[0],
|
||||
value: this._data.datasets[this._active[0]._datasetIndex].data[this._active[0]._index],
|
||||
label: this._data.labels ? this._data.labels[this._active[0]._index] : '',
|
||||
}),
|
||||
});
|
||||
|
||||
var tooltipPosition = this._active[0].tooltipPosition();
|
||||
helpers.extend(this, {
|
||||
helpers.extend(this._model, {
|
||||
x: Math.round(tooltipPosition.x),
|
||||
y: Math.round(tooltipPosition.y),
|
||||
caretPadding: tooltipPosition.padding
|
||||
});
|
||||
|
||||
break;
|
||||
|
||||
case 'label':
|
||||
@ -1583,17 +1653,22 @@
|
||||
if (dataCollection[dataIndex] && dataCollection[dataIndex].hasValue()) {
|
||||
elements.push(dataCollection[dataIndex]);
|
||||
}
|
||||
});
|
||||
}, this);
|
||||
|
||||
helpers.each(elements, function(element) {
|
||||
xPositions.push(element._vm.x);
|
||||
yPositions.push(element._vm.y);
|
||||
xPositions.push(element._view.x);
|
||||
yPositions.push(element._view.y);
|
||||
|
||||
//Include any colour information about the element
|
||||
labels.push(helpers.template(this._options.tooltips.multiTemplate, element));
|
||||
labels.push(helpers.template(this._options.tooltips.multiTemplate, {
|
||||
// These variables are available in the template function. Add others here
|
||||
element: element,
|
||||
datasetLabel: this._data.datasets[element._datasetIndex].label,
|
||||
value: this._data.datasets[element._datasetIndex].data[element._index],
|
||||
}));
|
||||
colors.push({
|
||||
fill: element._vm.backgroundColor,
|
||||
stroke: element._vm.borderColor
|
||||
fill: element._view.backgroundColor,
|
||||
stroke: element._view.borderColor
|
||||
});
|
||||
|
||||
}, this);
|
||||
@ -1611,11 +1686,11 @@
|
||||
}).call(this, dataIndex);
|
||||
|
||||
// Apply for now
|
||||
helpers.extend(this, {
|
||||
helpers.extend(this._model, {
|
||||
x: medianPosition.x,
|
||||
y: medianPosition.y,
|
||||
labels: labels,
|
||||
title: this._active.length ? this._active[0].label : '',
|
||||
title: this._data.labels && this._data.labels.length ? this._data.labels[this._active[0]._index] : '',
|
||||
legendColors: colors,
|
||||
legendBackgroundColor: this._options.tooltips.multiKeyBackground,
|
||||
});
|
||||
@ -1623,30 +1698,30 @@
|
||||
|
||||
// Calculate Appearance Tweaks
|
||||
|
||||
this.height = (labels.length * this.fontSize) + ((labels.length - 1) * (this.fontSize / 2)) + (this.yPadding * 2) + this.titleFontSize * 1.5;
|
||||
this._model.height = (labels.length * this._model.fontSize) + ((labels.length - 1) * (this._model.fontSize / 2)) + (this._model.yPadding * 2) + this._model.titleFontSize * 1.5;
|
||||
|
||||
var titleWidth = ctx.measureText(this.title).width,
|
||||
//Label has a legend square as well so account for this.
|
||||
labelWidth = longestText(ctx, this.font, labels) + this.fontSize + 3,
|
||||
labelWidth = longestText(ctx, this.font, labels) + this._model.fontSize + 3,
|
||||
longestTextWidth = max([labelWidth, titleWidth]);
|
||||
|
||||
this.width = longestTextWidth + (this.xPadding * 2);
|
||||
this._model.width = longestTextWidth + (this._model.xPadding * 2);
|
||||
|
||||
|
||||
var halfHeight = this.height / 2;
|
||||
var halfHeight = this._model.height / 2;
|
||||
|
||||
//Check to ensure the height will fit on the canvas
|
||||
if (this.y - halfHeight < 0) {
|
||||
this.y = halfHeight;
|
||||
} else if (this.y + halfHeight > this._chart.height) {
|
||||
this.y = this._chart.height - halfHeight;
|
||||
if (this._model.y - halfHeight < 0) {
|
||||
this._model.y = halfHeight;
|
||||
} else if (this._model.y + halfHeight > this._chart.height) {
|
||||
this._model.y = this._chart.height - halfHeight;
|
||||
}
|
||||
|
||||
//Decide whether to align left or right based on position on canvas
|
||||
if (this.x > this._chart.width / 2) {
|
||||
this.x -= this.xOffset + this.width;
|
||||
if (this._model.x > this._chart.width / 2) {
|
||||
this._model.x -= this._model.xOffset + this._model.width;
|
||||
} else {
|
||||
this.x += this.xOffset;
|
||||
this._model.x += this._model.xOffset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -1656,7 +1731,7 @@
|
||||
draw: function() {
|
||||
|
||||
var ctx = this._chart.ctx;
|
||||
var vm = this._vm;
|
||||
var vm = this._view;
|
||||
|
||||
switch (this._options.hover.mode) {
|
||||
case 'single':
|
||||
@ -1690,7 +1765,7 @@
|
||||
|
||||
// Custom Tooltips
|
||||
if (this._custom) {
|
||||
this._custom(this._vm);
|
||||
this._custom(this._view);
|
||||
} else {
|
||||
switch (vm.yAlign) {
|
||||
case "above":
|
||||
@ -1756,8 +1831,8 @@
|
||||
//ctx.clearRect(vm.x + vm.xPadding, this.getLineHeight(index + 1) - vm.fontSize/2, vm.fontSize, vm.fontSize);
|
||||
//Instead we'll make a white filled block to put the legendColour palette over.
|
||||
|
||||
ctx.fillStyle = helpers.color(vm.legendBackgroundColor).alpha(vm.opacity).rgbString();
|
||||
ctx.fillRect(vm.x + vm.xPadding, this.getLineHeight(index + 1) - vm.fontSize / 2, vm.fontSize, vm.fontSize);
|
||||
ctx.fillStyle = helpers.color(vm.legendColors[index].stroke).alpha(vm.opacity).rgbString();
|
||||
ctx.fillRect(vm.x + vm.xPadding - 1, this.getLineHeight(index + 1) - vm.fontSize / 2 - 1, vm.fontSize + 2, vm.fontSize + 2);
|
||||
|
||||
ctx.fillStyle = helpers.color(vm.legendColors[index].fill).alpha(vm.opacity).rgbString();
|
||||
ctx.fillRect(vm.x + vm.xPadding, this.getLineHeight(index + 1) - vm.fontSize / 2, vm.fontSize, vm.fontSize);
|
||||
@ -1768,14 +1843,14 @@
|
||||
}
|
||||
},
|
||||
getLineHeight: function(index) {
|
||||
var baseLineHeight = this._vm.y - (this._vm.height / 2) + this._vm.yPadding,
|
||||
var baseLineHeight = this._view.y - (this._view.height / 2) + this._view.yPadding,
|
||||
afterTitleIndex = index - 1;
|
||||
|
||||
//If the index is zero, we're getting the title
|
||||
if (index === 0) {
|
||||
return baseLineHeight + this._vm.titleFontSize / 2;
|
||||
return baseLineHeight + this._view.titleFontSize / 2;
|
||||
} else {
|
||||
return baseLineHeight + ((this._vm.fontSize * 1.5 * afterTitleIndex) + this._vm.fontSize / 2) + this._vm.titleFontSize * 1.5;
|
||||
return baseLineHeight + ((this._view.fontSize * 1.5 * afterTitleIndex) + this._view.fontSize / 2) + this._view.titleFontSize * 1.5;
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
@ -7,21 +7,6 @@
|
||||
helpers = Chart.helpers;
|
||||
|
||||
var defaultConfig = {
|
||||
segments: {
|
||||
//Boolean - Whether we should show a stroke on each segment
|
||||
showStroke: true,
|
||||
|
||||
//String - The colour of each segment stroke
|
||||
strokeColor: "#fff",
|
||||
|
||||
//Number - The width of each segment stroke
|
||||
borderWidth: 2,
|
||||
},
|
||||
|
||||
hover: {
|
||||
// The duration of animations triggered by hover events
|
||||
animationDuration: 400,
|
||||
},
|
||||
|
||||
animation: {
|
||||
//Boolean - Whether we animate the rotation of the Doughnut
|
||||
@ -32,10 +17,8 @@
|
||||
},
|
||||
|
||||
//The percentage of the chart that we cut out of the middle.
|
||||
|
||||
cutoutPercentage: 50,
|
||||
|
||||
//String - A legend template
|
||||
legendTemplate: "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<segments.length; i++){%><li><span style=\"background-color:<%=segments[i].backgroundColor%>\"></span><%if(segments[i].label){%><%=segments[i].label%><%}%></li><%}%></ul>"
|
||||
|
||||
};
|
||||
|
||||
@ -47,40 +30,21 @@
|
||||
//Initialize is fired when the chart is initialized - Data is passed in as a parameter
|
||||
//Config is automatically merged by the core of Chart.js, and is available at this.options
|
||||
initialize: function() {
|
||||
// Slice Type and defaults
|
||||
this.Slice = Chart.Arc.extend({
|
||||
_chart: this.chart,
|
||||
x: this.chart.width / 2,
|
||||
y: this.chart.height / 2
|
||||
});
|
||||
|
||||
//Set up tooltip events on the chart
|
||||
if (this.options.tooltips.enabled) {
|
||||
helpers.bindEvents(this, this.options.events, this.onHover);
|
||||
}
|
||||
helpers.bindEvents(this, this.options.events, this.events);
|
||||
|
||||
// Create new slice for each piece of data
|
||||
this.data.metaData = [];
|
||||
helpers.each(this.data.data, function(slice, index) {
|
||||
var metaSlice = new this.Slice();
|
||||
if (typeof slice == 'number') {
|
||||
helpers.extend(metaSlice, {
|
||||
value: slice
|
||||
});
|
||||
} else {
|
||||
helpers.extend(metaSlice, slice);
|
||||
}
|
||||
helpers.extend(metaSlice, {
|
||||
startAngle: Math.PI * 1.5,
|
||||
circumference: (this.options.animation.animateRotate) ? 0 : this.calculateCircumference(metaSlice.value),
|
||||
outerRadius: (this.options.animation.animateScale) ? 0 : this.outerRadius,
|
||||
innerRadius: (this.options.animation.animateScale) ? 0 : (this.outerRadius / 100) * this.options.percentageInnerCutout,
|
||||
});
|
||||
if (!metaSlice.backgroundColor) {
|
||||
slice.backgroundColor = 'hsl(' + (360 * index / this.data.data.length) + ', 100%, 50%)';
|
||||
}
|
||||
metaSlice.save();
|
||||
this.data.metaData.push(metaSlice);
|
||||
//Create a new bar for each piece of data
|
||||
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
|
||||
dataset.metaData = [];
|
||||
helpers.each(dataset.data, function(dataPoint, index) {
|
||||
dataset.metaData.push(new Chart.Arc({
|
||||
_chart: this.chart,
|
||||
_datasetIndex: datasetIndex,
|
||||
_index: index,
|
||||
_model: {}
|
||||
}));
|
||||
}, this);
|
||||
}, this);
|
||||
|
||||
// Create tooltip instance exclusively for this chart with some defaults.
|
||||
@ -90,9 +54,137 @@
|
||||
_options: this.options,
|
||||
}, this);
|
||||
|
||||
this.resetElements();
|
||||
|
||||
// Update the chart with the latest data.
|
||||
this.update();
|
||||
|
||||
},
|
||||
onHover: function(e) {
|
||||
|
||||
calculateCircumference: function(dataset, value) {
|
||||
if (dataset.total > 0) {
|
||||
return (Math.PI * 2) * (value / dataset.total);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
},
|
||||
resetElements: function() {
|
||||
this.outerRadius = (helpers.min([this.chart.width, this.chart.height]) - this.options.elements.slice.borderWidth / 2) / 2;
|
||||
this.innerRadius = this.options.cutoutPercentage ? (this.outerRadius / 100) * (this.options.cutoutPercentage) : 1;
|
||||
this.radiusLength = (this.outerRadius - this.innerRadius) / this.data.datasets.length;
|
||||
|
||||
// Update the points
|
||||
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
|
||||
// So that calculateCircumference works
|
||||
dataset.total = 0;
|
||||
helpers.each(dataset.data, function(value) {
|
||||
dataset.total += Math.abs(value);
|
||||
}, this);
|
||||
|
||||
dataset.outerRadius = this.outerRadius - (this.radiusLength * datasetIndex);
|
||||
dataset.innerRadius = dataset.outerRadius - this.radiusLength;
|
||||
|
||||
helpers.each(dataset.metaData, function(slice, index) {
|
||||
helpers.extend(slice, {
|
||||
_model: {
|
||||
x: this.chart.width / 2,
|
||||
y: this.chart.height / 2,
|
||||
startAngle: Math.PI * -0.5, // use - PI / 2 instead of 3PI / 2 to make animations better. It means that we never deal with overflow during the transition function
|
||||
circumference: (this.options.animation.animateRotate) ? 0 : this.calculateCircumference(metaSlice.value),
|
||||
outerRadius: (this.options.animation.animateScale) ? 0 : dataset.outerRadius,
|
||||
innerRadius: (this.options.animation.animateScale) ? 0 : dataset.innerRadius,
|
||||
|
||||
backgroundColor: slice.custom && slice.custom.backgroundColor ? slice.custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, this.options.elements.slice.backgroundColor),
|
||||
hoverBackgroundColor: slice.custom && slice.custom.hoverBackgroundColor ? slice.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, this.options.elements.slice.hoverBackgroundColor),
|
||||
borderWidth: slice.custom && slice.custom.borderWidth ? slice.custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, this.options.elements.slice.borderWidth),
|
||||
borderColor: slice.custom && slice.custom.borderColor ? slice.custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, this.options.elements.slice.borderColor),
|
||||
|
||||
label: helpers.getValueAtIndexOrDefault(dataset.label, index, this.data.labels[index])
|
||||
},
|
||||
});
|
||||
|
||||
slice.pivot();
|
||||
}, this);
|
||||
|
||||
}, this);
|
||||
},
|
||||
update: function() {
|
||||
|
||||
this.outerRadius = (helpers.min([this.chart.width, this.chart.height]) - this.options.elements.slice.borderWidth / 2) / 2;
|
||||
this.innerRadius = this.options.cutoutPercentage ? (this.outerRadius / 100) * (this.options.cutoutPercentage) : 1;
|
||||
this.radiusLength = (this.outerRadius - this.innerRadius) / this.data.datasets.length;
|
||||
|
||||
|
||||
// Update the points
|
||||
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
|
||||
|
||||
dataset.total = 0;
|
||||
helpers.each(dataset.data, function(value) {
|
||||
dataset.total += Math.abs(value);
|
||||
}, this);
|
||||
|
||||
|
||||
dataset.outerRadius = this.outerRadius - (this.radiusLength * datasetIndex);
|
||||
|
||||
dataset.innerRadius = dataset.outerRadius - this.radiusLength;
|
||||
|
||||
helpers.each(dataset.metaData, function(slice, index) {
|
||||
|
||||
helpers.extend(slice, {
|
||||
// Utility
|
||||
_chart: this.chart,
|
||||
_datasetIndex: datasetIndex,
|
||||
_index: index,
|
||||
|
||||
// Desired view properties
|
||||
_model: {
|
||||
x: this.chart.width / 2,
|
||||
y: this.chart.height / 2,
|
||||
circumference: this.calculateCircumference(dataset, dataset.data[index]),
|
||||
outerRadius: dataset.outerRadius,
|
||||
innerRadius: dataset.innerRadius,
|
||||
|
||||
backgroundColor: slice.custom && slice.custom.backgroundColor ? slice.custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, this.options.elements.slice.backgroundColor),
|
||||
hoverBackgroundColor: slice.custom && slice.custom.hoverBackgroundColor ? slice.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, this.options.elements.slice.hoverBackgroundColor),
|
||||
borderWidth: slice.custom && slice.custom.borderWidth ? slice.custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, this.options.elements.slice.borderWidth),
|
||||
borderColor: slice.custom && slice.custom.borderColor ? slice.custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, this.options.elements.slice.borderColor),
|
||||
|
||||
label: helpers.getValueAtIndexOrDefault(dataset.label, index, this.data.labels[index])
|
||||
},
|
||||
});
|
||||
|
||||
if (index === 0) {
|
||||
slice._model.startAngle = Math.PI * -0.5; // use - PI / 2 instead of 3PI / 2 to make animations better. It means that we never deal with overflow during the transition function
|
||||
} else {
|
||||
slice._model.startAngle = dataset.metaData[index - 1]._model.endAngle;
|
||||
}
|
||||
|
||||
slice._model.endAngle = slice._model.startAngle + slice._model.circumference;
|
||||
|
||||
|
||||
//Check to see if it's the last slice, if not get the next and update its start angle
|
||||
if (index < dataset.data.length - 1) {
|
||||
dataset.metaData[index + 1]._model.startAngle = slice._model.endAngle;
|
||||
}
|
||||
|
||||
slice.pivot();
|
||||
}, this);
|
||||
|
||||
}, this);
|
||||
|
||||
this.render();
|
||||
},
|
||||
draw: function(easeDecimal) {
|
||||
easeDecimal = easeDecimal || 1;
|
||||
this.clear();
|
||||
|
||||
this.eachElement(function(slice) {
|
||||
slice.transition(easeDecimal).draw();
|
||||
}, this);
|
||||
|
||||
this.tooltip.transition(easeDecimal).draw();
|
||||
},
|
||||
events: function(e) {
|
||||
|
||||
// If exiting chart
|
||||
if (e.type == 'mouseout') {
|
||||
@ -102,23 +194,91 @@
|
||||
this.lastActive = this.lastActive || [];
|
||||
|
||||
// Find Active Elements
|
||||
this.active = this.getSliceAtEvent(e);
|
||||
this.active = function() {
|
||||
switch (this.options.hover.mode) {
|
||||
case 'single':
|
||||
return this.getSliceAtEvent(e);
|
||||
case 'label':
|
||||
return this.getSlicesAtEvent(e);
|
||||
case 'dataset':
|
||||
return this.getDatasetAtEvent(e);
|
||||
default:
|
||||
return e;
|
||||
}
|
||||
}.call(this);
|
||||
|
||||
// On Hover hook
|
||||
if (this.options.onHover) {
|
||||
this.options.onHover.call(this, this.active);
|
||||
if (this.options.hover.onHover) {
|
||||
this.options.hover.onHover.call(this, this.active);
|
||||
}
|
||||
|
||||
if (e.type == 'mouseup' || e.type == 'click') {
|
||||
if (this.options.onClick) {
|
||||
this.options.onClick.call(this, e, this.active);
|
||||
}
|
||||
}
|
||||
|
||||
var dataset;
|
||||
var index;
|
||||
// Remove styling for last active (even if it may still be active)
|
||||
if (this.lastActive.length) {
|
||||
this.lastActive[0].backgroundColor = this.data.data[this.lastActive[0]._index].backgroundColor;
|
||||
switch (this.options.hover.mode) {
|
||||
case 'single':
|
||||
dataset = this.data.datasets[this.lastActive[0]._datasetIndex];
|
||||
index = this.lastActive[0]._index;
|
||||
|
||||
this.lastActive[0]._model.backgroundColor = this.lastActive[0].custom && this.lastActive[0].custom.backgroundColor ? this.lastActive[0].custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, this.options.elements.slice.backgroundColor);
|
||||
this.lastActive[0]._model.borderColor = this.lastActive[0].custom && this.lastActive[0].custom.borderColor ? this.lastActive[0].custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, this.options.elements.slice.borderColor);
|
||||
this.lastActive[0]._model.borderWidth = this.lastActive[0].custom && this.lastActive[0].custom.borderWidth ? this.lastActive[0].custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, this.options.elements.slice.borderWidth);
|
||||
break;
|
||||
case 'label':
|
||||
for (var i = 0; i < this.lastActive.length; i++) {
|
||||
dataset = this.data.datasets[this.lastActive[i]._datasetIndex];
|
||||
index = this.lastActive[i]._index;
|
||||
|
||||
this.lastActive[i]._model.backgroundColor = this.lastActive[i].custom && this.lastActive[i].custom.backgroundColor ? this.lastActive[i].custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, this.options.elements.slice.backgroundColor);
|
||||
this.lastActive[i]._model.borderColor = this.lastActive[i].custom && this.lastActive[i].custom.borderColor ? this.lastActive[i].custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, this.options.elements.slice.borderColor);
|
||||
this.lastActive[i]._model.borderWidth = this.lastActive[i].custom && this.lastActive[i].custom.borderWidth ? this.lastActive[i].custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, this.options.elements.slice.borderWidth);
|
||||
}
|
||||
break;
|
||||
case 'dataset':
|
||||
break;
|
||||
default:
|
||||
// Don't change anything
|
||||
}
|
||||
}
|
||||
|
||||
// Built in hover styling
|
||||
if (this.active.length && this.options.hover.mode) {
|
||||
this.active[0].backgroundColor = this.data.data[this.active[0]._index].hoverBackgroundColor || helpers.color(this.data.data[this.active[0]._index].backgroundColor).saturate(0.5).darken(0.35).rgbString();
|
||||
switch (this.options.hover.mode) {
|
||||
case 'single':
|
||||
dataset = this.data.datasets[this.active[0]._datasetIndex];
|
||||
index = this.active[0]._index;
|
||||
|
||||
this.active[0]._model.radius = this.active[0].custom && this.active[0].custom.hoverRadius ? this.active[0].custom.hoverRadius : helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.active[0]._model.radius + 2);
|
||||
this.active[0]._model.backgroundColor = this.active[0].custom && this.active[0].custom.hoverBackgroundColor ? this.active[0].custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.color(this.active[0]._model.backgroundColor).saturate(0.5).darken(0.35).rgbString());
|
||||
this.active[0]._model.borderColor = this.active[0].custom && this.active[0].custom.hoverBorderColor ? this.active[0].custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.color(this.active[0]._model.borderColor).saturate(0.5).darken(0.35).rgbString());
|
||||
this.active[0]._model.borderWidth = this.active[0].custom && this.active[0].custom.hoverBorderWidth ? this.active[0].custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, this.active[0]._model.borderWidth + 2);
|
||||
break;
|
||||
case 'label':
|
||||
for (var i = 0; i < this.active.length; i++) {
|
||||
dataset = this.data.datasets[this.active[i]._datasetIndex];
|
||||
index = this.active[i]._index;
|
||||
|
||||
this.active[i]._model.radius = this.active[i].custom && this.active[i].custom.hoverRadius ? this.active[i].custom.hoverRadius : helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.active[i]._model.radius + 2);
|
||||
this.active[i]._model.backgroundColor = this.active[i].custom && this.active[i].custom.hoverBackgroundColor ? this.active[i].custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.color(this.active[i]._model.backgroundColor).saturate(0.5).darken(0.35).rgbString());
|
||||
this.active[i]._model.borderColor = this.active[i].custom && this.active[i].custom.hoverBorderColor ? this.active[i].custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.color(this.active[i]._model.borderColor).saturate(0.5).darken(0.35).rgbString());
|
||||
this.active[i]._model.borderWidth = this.active[i].custom && this.active[i].custom.hoverBorderWidth ? this.active[i].custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, this.active[i]._model.borderWidth + 2);
|
||||
}
|
||||
break;
|
||||
case 'dataset':
|
||||
break;
|
||||
default:
|
||||
// Don't change anything
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Built in Tooltips
|
||||
if (this.options.tooltips.enabled) {
|
||||
|
||||
@ -127,17 +287,16 @@
|
||||
|
||||
// Active
|
||||
if (this.active.length) {
|
||||
this.tooltip._model.opacity = 1;
|
||||
|
||||
helpers.extend(this.tooltip, {
|
||||
opacity: 1,
|
||||
_active: this.active,
|
||||
});
|
||||
|
||||
this.tooltip.update();
|
||||
} else {
|
||||
// Inactive
|
||||
helpers.extend(this.tooltip, {
|
||||
opacity: 0,
|
||||
});
|
||||
this.tooltip._model.opacity = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -167,85 +326,31 @@
|
||||
// Remember Last Active
|
||||
this.lastActive = this.active;
|
||||
return this;
|
||||
|
||||
},
|
||||
getSliceAtEvent: function(e) {
|
||||
var elements = [];
|
||||
|
||||
var location = helpers.getRelativePosition(e);
|
||||
|
||||
helpers.each(this.data.metaData, function(slice, index) {
|
||||
if (slice.inRange(location.x, location.y)) elements.push(slice);
|
||||
this.eachElement(function(slice, index) {
|
||||
if (slice.inRange(location.x, location.y)) {
|
||||
elements.push(slice);
|
||||
}
|
||||
}, this);
|
||||
return elements;
|
||||
},
|
||||
calculateCircumference: function(value) {
|
||||
if (this.total > 0) {
|
||||
return (Math.PI * 2) * (value / this.total);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
},
|
||||
update: function() {
|
||||
/*getSlicesAtEvent: function(e) {
|
||||
var elements = [];
|
||||
|
||||
// Calc Total
|
||||
this.total = 0;
|
||||
helpers.each(this.data.data, function(slice) {
|
||||
this.total += Math.abs(slice.value);
|
||||
}, this);
|
||||
var location = helpers.getRelativePosition(e);
|
||||
|
||||
this.outerRadius = (helpers.min([this.chart.width, this.chart.height]) - this.options.segments.borderWidth / 2) / 2;
|
||||
|
||||
// Map new data to data points
|
||||
helpers.each(this.data.metaData, function(slice, index) {
|
||||
|
||||
var datapoint = this.data.data[index];
|
||||
|
||||
helpers.extend(slice, {
|
||||
_index: index,
|
||||
x: this.chart.width / 2,
|
||||
y: this.chart.height / 2,
|
||||
value: datapoint.value,
|
||||
label: datapoint.label,
|
||||
circumference: this.calculateCircumference(datapoint.value),
|
||||
outerRadius: this.outerRadius,
|
||||
innerRadius: (this.outerRadius / 100) * this.options.cutoutPercentage,
|
||||
|
||||
backgroundColor: datapoint.backgroundColor,
|
||||
hoverBackgroundColor: datapoint.hoverBackgroundColor || datapoint.backgroundColor,
|
||||
borderWidth: this.options.segments.borderWidth,
|
||||
borderColor: this.options.segments.strokeColor,
|
||||
});
|
||||
|
||||
helpers.extend(slice, {
|
||||
endAngle: slice.startAngle + slice.circumference,
|
||||
});
|
||||
|
||||
if (index === 0) {
|
||||
slice.startAngle = Math.PI * 1.5;
|
||||
this.eachElement(function(slice, index) {
|
||||
if (slice.inGroupRange(location.x, location.y)) {
|
||||
elements.push(slice);
|
||||
}
|
||||
|
||||
//Check to see if it's the last slice, if not get the next and update its start angle
|
||||
if (index < this.data.data.length - 1) {
|
||||
this.data.metaData[index + 1].startAngle = slice.endAngle;
|
||||
}
|
||||
|
||||
slice.pivot();
|
||||
|
||||
}, this);
|
||||
|
||||
this.render();
|
||||
},
|
||||
draw: function(easeDecimal) {
|
||||
easeDecimal = easeDecimal || 1;
|
||||
this.clear();
|
||||
|
||||
helpers.each(this.data.metaData, function(slice, index) {
|
||||
slice.transition(easeDecimal).draw();
|
||||
}, this);
|
||||
|
||||
this.tooltip.transition(easeDecimal).draw();
|
||||
}
|
||||
return elements;
|
||||
},*/
|
||||
});
|
||||
|
||||
Chart.types.Doughnut.extend({
|
||||
|
||||
@ -7,6 +7,12 @@
|
||||
|
||||
var defaultConfig = {
|
||||
|
||||
stacked: false,
|
||||
|
||||
hover: {
|
||||
mode: "label"
|
||||
},
|
||||
|
||||
scales: {
|
||||
xAxes: [{
|
||||
scaleType: "dataset", // scatter should not use a dataset axis
|
||||
@ -26,11 +32,6 @@
|
||||
offsetGridLines: false,
|
||||
},
|
||||
|
||||
// scale numbers
|
||||
beginAtZero: false,
|
||||
integersOnly: false,
|
||||
override: null,
|
||||
|
||||
// label settings
|
||||
labels: {
|
||||
show: true,
|
||||
@ -60,7 +61,6 @@
|
||||
|
||||
// scale numbers
|
||||
beginAtZero: false,
|
||||
integersOnly: false,
|
||||
override: null,
|
||||
|
||||
// label settings
|
||||
@ -74,45 +74,6 @@
|
||||
}
|
||||
}],
|
||||
},
|
||||
|
||||
//Boolean - Whether to stack the lines essentially creating a stacked area chart.
|
||||
stacked: false,
|
||||
|
||||
point: {
|
||||
// Number - Radius of each point dot in pixels
|
||||
radius: 3,
|
||||
|
||||
// Number - Pixel width of point dot border
|
||||
borderWidth: 1,
|
||||
|
||||
// Number - Pixel width of point on hover
|
||||
hoverRadius: 5,
|
||||
|
||||
// Number - Pixel width of point dot border on hover
|
||||
hoverBorderWidth: 2,
|
||||
|
||||
// Color
|
||||
backgroundColor: Chart.defaults.global.defaultColor,
|
||||
|
||||
// Color
|
||||
borderColor: Chart.defaults.global.defaultColor,
|
||||
|
||||
//Number - amount extra to add to the radius to cater for hit detection outside the drawn point
|
||||
hitRadius: 6,
|
||||
},
|
||||
|
||||
line: {
|
||||
//Number - Tension of the bezier curve between points. Use 0 to turn off bezier tension
|
||||
tension: 0.4,
|
||||
},
|
||||
|
||||
//Number - Pixel width of dataset border
|
||||
borderWidth: 2,
|
||||
//Number - Pixel width of dataset border on hover
|
||||
hoverBorderWidth: 2,
|
||||
|
||||
//String - A legend template
|
||||
legendTemplate: "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<datasets.length; i++){%><li><span style=\"background-color:<%=datasets[i].borderColor%>\"></span><%if(datasets[i].label){%><%=datasets[i].label%><%}%></li><%}%></ul>",
|
||||
};
|
||||
|
||||
|
||||
@ -121,66 +82,46 @@
|
||||
defaults: defaultConfig,
|
||||
initialize: function() {
|
||||
|
||||
var _this = this;
|
||||
|
||||
// Events
|
||||
helpers.bindEvents(this, this.options.events, this.events);
|
||||
|
||||
var _this = this;
|
||||
|
||||
//Create a new line and its points for each dataset and piece of data
|
||||
// Create a new line and its points for each dataset and piece of data
|
||||
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
|
||||
dataset.metaDataset = new Chart.Line();
|
||||
|
||||
dataset.metaDataset = new Chart.Line({
|
||||
_chart: this.chart,
|
||||
_datasetIndex: datasetIndex,
|
||||
_points: dataset.metaData,
|
||||
});
|
||||
|
||||
dataset.metaData = [];
|
||||
|
||||
helpers.each(dataset.data, function(dataPoint, index) {
|
||||
dataset.metaData.push(new Chart.Point());
|
||||
dataset.metaData.push(new Chart.Point({
|
||||
_datasetIndex: datasetIndex,
|
||||
_index: index,
|
||||
_chart: this.chart,
|
||||
_model: {
|
||||
x: 0, //xScale.getPixelForValue(null, index, true),
|
||||
y: 0, //this.chartArea.bottom,
|
||||
},
|
||||
}));
|
||||
|
||||
}, this);
|
||||
|
||||
// The line chart only supports a single x axis because the x axis is always a dataset axis
|
||||
// The line chart onlty supports a single x axis because the x axis is always a dataset axis
|
||||
dataset.xAxisID = this.options.scales.xAxes[0].id;
|
||||
|
||||
if (!dataset.yAxisID) {
|
||||
dataset.yAxisID = this.options.scales.yAxes[0].id;
|
||||
}
|
||||
|
||||
}, this);
|
||||
|
||||
// Build and fit the scale. Needs to happen after the axis IDs have been set
|
||||
this.buildScale();
|
||||
Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height);
|
||||
|
||||
// Set defaults for lines
|
||||
this.eachDataset(function(dataset, datasetIndex) {
|
||||
helpers.extend(dataset.metaDataset, {
|
||||
_points: dataset.metaData,
|
||||
_datasetIndex: datasetIndex,
|
||||
_chart: this.chart,
|
||||
});
|
||||
// Fill in dataset defaults from options
|
||||
helpers.extend(dataset, helpers.merge(this.options, dataset));
|
||||
// Copy to view modele
|
||||
dataset.metaDataset.save();
|
||||
}, this);
|
||||
|
||||
// Set defaults for points
|
||||
this.eachElement(function(point, index, dataset, datasetIndex) {
|
||||
var xScale = this.scales[this.data.datasets[datasetIndex].xAxisID];
|
||||
|
||||
helpers.extend(point, {
|
||||
x: xScale.getPixelForValue(null, index, true),
|
||||
y: this.chartArea.bottom,
|
||||
_datasetIndex: datasetIndex,
|
||||
_index: index,
|
||||
_chart: this.chart
|
||||
});
|
||||
|
||||
// Default bezier control points
|
||||
helpers.extend(point, {
|
||||
controlPointPreviousX: this.previousPoint(dataset, index).x,
|
||||
controlPointPreviousY: this.nextPoint(dataset, index).y,
|
||||
controlPointNextX: this.previousPoint(dataset, index).x,
|
||||
controlPointNextY: this.nextPoint(dataset, index).y,
|
||||
});
|
||||
// Copy to view model
|
||||
point.save();
|
||||
}, this);
|
||||
|
||||
// Create tooltip instance exclusively for this chart with some defaults.
|
||||
this.tooltip = new Chart.Tooltip({
|
||||
@ -189,15 +130,103 @@
|
||||
_options: this.options,
|
||||
}, this);
|
||||
|
||||
// Need to fit scales before we reset elements.
|
||||
Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height);
|
||||
|
||||
// Reset so that we animation from the baseline
|
||||
this.resetElements();
|
||||
|
||||
// Update that shiz
|
||||
this.update();
|
||||
},
|
||||
nextPoint: function(collection, index) {
|
||||
return collection[index - 1] || collection[index];
|
||||
},
|
||||
previousPoint: function(collection, index) {
|
||||
return collection[index + 1] || collection[index];
|
||||
},
|
||||
previousPoint: function(collection, index) {
|
||||
return collection[index - 1] || collection[index];
|
||||
},
|
||||
resetElements: function() {
|
||||
// Update the points
|
||||
this.eachElement(function(point, index, dataset, datasetIndex) {
|
||||
var xScale = this.scales[this.data.datasets[datasetIndex].xAxisID];
|
||||
var yScale = this.scales[this.data.datasets[datasetIndex].yAxisID];
|
||||
|
||||
var yScalePoint;
|
||||
|
||||
if (yScale.min < 0 && yScale.max <0) {
|
||||
// all less than 0. use the top
|
||||
yScalePoint = yScale.getPixelForValue(yScale.max);
|
||||
} else if (yScale.min > 0 && yScale.max > 0) {
|
||||
yScalePoint = yScale.getPixelForValue(yScale.min);
|
||||
} else {
|
||||
yScalePoint = yScale.getPixelForValue(0);
|
||||
}
|
||||
|
||||
helpers.extend(point, {
|
||||
// Utility
|
||||
_chart: this.chart,
|
||||
_xScale: xScale,
|
||||
_yScale: yScale,
|
||||
_datasetIndex: datasetIndex,
|
||||
_index: index,
|
||||
|
||||
// Desired view properties
|
||||
_model: {
|
||||
x: xScale.getPixelForValue(null, index, true), // value not used in dataset scale, but we want a consistent API between scales
|
||||
y: yScalePoint,
|
||||
|
||||
// Appearance
|
||||
tension: point.custom && point.custom.tension ? point.custom.tension : this.options.elements.line.tension,
|
||||
radius: point.custom && point.custom.radius ? point.custom.pointRadius : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointRadius, index, this.options.elements.point.radius),
|
||||
backgroundColor: point.custom && point.custom.backgroundColor ? point.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointBackgroundColor, index, this.options.elements.point.backgroundColor),
|
||||
borderColor: point.custom && point.custom.borderColor ? point.custom.borderColor : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointBorderColor, index, this.options.elements.point.borderColor),
|
||||
borderWidth: point.custom && point.custom.borderWidth ? point.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointBorderWidth, index, this.options.elements.point.borderWidth),
|
||||
skip: typeof this.data.datasets[datasetIndex].data[index] != 'number',
|
||||
|
||||
// Tooltip
|
||||
hoverRadius: point.custom && point.custom.hoverRadius ? point.custom.hoverRadius : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointHitRadius, index, this.options.elements.point.hitRadius),
|
||||
},
|
||||
});
|
||||
}, this);
|
||||
|
||||
// Update control points for the bezier curve
|
||||
this.eachElement(function(point, index, dataset, datasetIndex) {
|
||||
var controlPoints = helpers.splineCurve(
|
||||
this.previousPoint(dataset, index)._model,
|
||||
point._model,
|
||||
this.nextPoint(dataset, index)._model,
|
||||
point._model.tension
|
||||
);
|
||||
|
||||
point._model.controlPointPreviousX = controlPoints.previous.x;
|
||||
point._model.controlPointNextX = controlPoints.next.x;
|
||||
|
||||
// Prevent the bezier going outside of the bounds of the graph
|
||||
|
||||
// Cap puter bezier handles to the upper/lower scale bounds
|
||||
if (controlPoints.next.y > this.chartArea.bottom) {
|
||||
point._model.controlPointNextY = this.chartArea.bottom;
|
||||
} else if (controlPoints.next.y < this.chartArea.top) {
|
||||
point._model.controlPointNextY = this.chartArea.top;
|
||||
} else {
|
||||
point._model.controlPointNextY = controlPoints.next.y;
|
||||
}
|
||||
|
||||
// Cap inner bezier handles to the upper/lower scale bounds
|
||||
if (controlPoints.previous.y > this.chartArea.bottom) {
|
||||
point._model.controlPointPreviousY = this.chartArea.bottom;
|
||||
} else if (controlPoints.previous.y < this.chartArea.top) {
|
||||
point._model.controlPointPreviousY = this.chartArea.top;
|
||||
} else {
|
||||
point._model.controlPointPreviousY = controlPoints.previous.y;
|
||||
}
|
||||
|
||||
// Now pivot the point for animation
|
||||
point.pivot();
|
||||
}, this);
|
||||
},
|
||||
update: function() {
|
||||
|
||||
Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height);
|
||||
|
||||
// Update the lines
|
||||
@ -206,22 +235,27 @@
|
||||
|
||||
helpers.extend(dataset.metaDataset, {
|
||||
// Utility
|
||||
_scale: yScale,
|
||||
_datasetIndex: datasetIndex,
|
||||
|
||||
// Data
|
||||
_points: dataset.metaData,
|
||||
|
||||
// Geometry
|
||||
scaleTop: yScale.top,
|
||||
scaleBottom: yScale.bottom,
|
||||
scaleZero: yScale.getPixelForValue(0),
|
||||
|
||||
// Appearance
|
||||
tension: dataset.tension || this.options.line.tension,
|
||||
backgroundColor: dataset.backgroundColor || this.options.backgroundColor,
|
||||
borderWidth: dataset.borderWidth || this.options.borderWidth,
|
||||
borderColor: dataset.borderColor || this.options.borderColor,
|
||||
_children: dataset.metaData,
|
||||
// Model
|
||||
_model: {
|
||||
// Appearance
|
||||
tension: dataset.tension || this.options.elements.line.tension,
|
||||
backgroundColor: dataset.backgroundColor || this.options.elements.line.backgroundColor,
|
||||
borderWidth: dataset.borderWidth || this.options.elements.line.borderWidth,
|
||||
borderColor: dataset.borderColor || this.options.elements.line.borderColor,
|
||||
fill: dataset.fill !== undefined ? dataset.fill : this.options.elements.line.fill, // use the value from the dataset if it was provided. else fall back to the default
|
||||
skipNull: dataset.skipNull !== undefined ? dataset.skipNull : this.options.elements.line.skipNull,
|
||||
drawNull: dataset.drawNull !== undefined ? dataset.drawNull : this.options.elements.line.drawNull,
|
||||
// Scale
|
||||
scaleTop: yScale.top,
|
||||
scaleBottom: yScale.bottom,
|
||||
scaleZero: yScale.getPixelForValue(0),
|
||||
},
|
||||
});
|
||||
|
||||
dataset.metaDataset.pivot();
|
||||
});
|
||||
|
||||
@ -233,61 +267,63 @@
|
||||
helpers.extend(point, {
|
||||
// Utility
|
||||
_chart: this.chart,
|
||||
_xScale: xScale,
|
||||
_yScale: yScale,
|
||||
_datasetIndex: datasetIndex,
|
||||
_index: index,
|
||||
|
||||
// Data
|
||||
label: this.data.labels[index],
|
||||
value: this.data.datasets[datasetIndex].data[index],
|
||||
datasetLabel: this.data.datasets[datasetIndex].label,
|
||||
// Desired view properties
|
||||
_model: {
|
||||
x: xScale.getPixelForValue(null, index, true), // value not used in dataset scale, but we want a consistent API between scales
|
||||
y: yScale.getPointPixelForValue(this.data.datasets[datasetIndex].data[index], index, datasetIndex),
|
||||
|
||||
// Geometry
|
||||
offsetGridLines: this.options.offsetGridLines,
|
||||
x: xScale.getPixelForValue(null, index, true), // value not used in dataset scale, but we want a consistent API between scales
|
||||
y: yScale.getPointPixelForValue(this.data.datasets[datasetIndex].data[index], index, datasetIndex),
|
||||
tension: this.data.datasets[datasetIndex].metaDataset.tension,
|
||||
// Appearance
|
||||
tension: point.custom && point.custom.tension ? point.custom.tension : this.options.elements.line.tension,
|
||||
radius: point.custom && point.custom.radius ? point.custom.pointRadius : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointRadius, index, this.options.elements.point.radius),
|
||||
backgroundColor: point.custom && point.custom.backgroundColor ? point.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointBackgroundColor, index, this.options.elements.point.backgroundColor),
|
||||
borderColor: point.custom && point.custom.borderColor ? point.custom.borderColor : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointBorderColor, index, this.options.elements.point.borderColor),
|
||||
borderWidth: point.custom && point.custom.borderWidth ? point.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointBorderWidth, index, this.options.elements.point.borderWidth),
|
||||
skip: typeof this.data.datasets[datasetIndex].data[index] != 'number',
|
||||
|
||||
// Appearnce
|
||||
radius: this.data.datasets[datasetIndex].pointRadius || this.options.point.radius,
|
||||
backgroundColor: this.data.datasets[datasetIndex].pointBackgroundColor || this.options.point.backgroundColor,
|
||||
borderWidth: this.data.datasets[datasetIndex].pointBorderWidth || this.options.point.borderWidth,
|
||||
|
||||
// Tooltip
|
||||
hoverRadius: this.data.datasets[datasetIndex].pointHitRadius || this.options.point.hitRadius,
|
||||
// Tooltip
|
||||
hoverRadius: point.custom && point.custom.hoverRadius ? point.custom.hoverRadius : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointHitRadius, index, this.options.elements.point.hitRadius),
|
||||
},
|
||||
});
|
||||
}, this);
|
||||
|
||||
|
||||
// Update control points for the bezier curve
|
||||
this.eachElement(function(point, index, dataset, datasetIndex) {
|
||||
var controlPoints = helpers.splineCurve(
|
||||
this.previousPoint(dataset, index),
|
||||
point,
|
||||
this.nextPoint(dataset, index),
|
||||
point.tension
|
||||
this.previousPoint(dataset, index)._model,
|
||||
point._model,
|
||||
this.nextPoint(dataset, index)._model,
|
||||
point._model.tension
|
||||
);
|
||||
|
||||
point.controlPointPreviousX = controlPoints.previous.x;
|
||||
point.controlPointNextX = controlPoints.next.x;
|
||||
point._model.controlPointPreviousX = controlPoints.previous.x;
|
||||
point._model.controlPointNextX = controlPoints.next.x;
|
||||
|
||||
// Prevent the bezier going outside of the bounds of the graph
|
||||
|
||||
// Cap puter bezier handles to the upper/lower scale bounds
|
||||
if (controlPoints.next.y > this.chartArea.bottom) {
|
||||
point.controlPointNextY = this.chartArea.bottom;
|
||||
point._model.controlPointNextY = this.chartArea.bottom;
|
||||
} else if (controlPoints.next.y < this.chartArea.top) {
|
||||
point.controlPointNextY = this.chartArea.top;
|
||||
point._model.controlPointNextY = this.chartArea.top;
|
||||
} else {
|
||||
point.controlPointNextY = controlPoints.next.y;
|
||||
point._model.controlPointNextY = controlPoints.next.y;
|
||||
}
|
||||
|
||||
// Cap inner bezier handles to the upper/lower scale bounds
|
||||
if (controlPoints.previous.y > this.chartArea.bottom) {
|
||||
point.controlPointPreviousY = this.chartArea.bottom;
|
||||
point._model.controlPointPreviousY = this.chartArea.bottom;
|
||||
} else if (controlPoints.previous.y < this.chartArea.top) {
|
||||
point.controlPointPreviousY = this.chartArea.top;
|
||||
point._model.controlPointPreviousY = this.chartArea.top;
|
||||
} else {
|
||||
point.controlPointPreviousY = controlPoints.previous.y;
|
||||
point._model.controlPointPreviousY = controlPoints.previous.y;
|
||||
}
|
||||
|
||||
// Now pivot the point for animation
|
||||
point.pivot();
|
||||
}, this);
|
||||
@ -400,9 +436,6 @@
|
||||
|
||||
this.scales[scale.id] = scale;
|
||||
}, this);
|
||||
},
|
||||
redraw: function() {
|
||||
|
||||
},
|
||||
draw: function(ease) {
|
||||
|
||||
@ -418,7 +451,6 @@
|
||||
for (var i = this.data.datasets.length - 1; i >= 0; i--) {
|
||||
|
||||
var dataset = this.data.datasets[i];
|
||||
var datasetIndex = i;
|
||||
|
||||
// Transition Point Locations
|
||||
helpers.each(dataset.metaData, function(point, index) {
|
||||
@ -461,8 +493,8 @@
|
||||
}.call(this);
|
||||
|
||||
// On Hover hook
|
||||
if (this.options.onHover) {
|
||||
this.options.onHover.call(this, this.active);
|
||||
if (this.options.hover.onHover) {
|
||||
this.options.hover.onHover.call(this, this.active);
|
||||
}
|
||||
|
||||
if (e.type == 'mouseup' || e.type == 'click') {
|
||||
@ -472,25 +504,28 @@
|
||||
}
|
||||
|
||||
var dataset;
|
||||
var index;
|
||||
// Remove styling for last active (even if it may still be active)
|
||||
if (this.lastActive.length) {
|
||||
switch (this.options.hover.mode) {
|
||||
case 'single':
|
||||
dataset = this.data.datasets[this.lastActive[0]._datasetIndex];
|
||||
index = this.lastActive[0]._index;
|
||||
|
||||
this.lastActive[0].radius = dataset.pointRadius;
|
||||
this.lastActive[0].backgroundColor = dataset.pointBackgroundColor;
|
||||
this.lastActive[0].borderColor = dataset.pointBorderColor;
|
||||
this.lastActive[0].borderWidth = dataset.pointBorderWidth;
|
||||
this.lastActive[0]._model.radius = this.lastActive[0].custom && this.lastActive[0].custom.radius ? this.lastActive[0].custom.pointRadius : helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, this.options.elements.point.radius);
|
||||
this.lastActive[0]._model.backgroundColor = this.lastActive[0].custom && this.lastActive[0].custom.backgroundColor ? this.lastActive[0].custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointBackgroundColor, index, this.options.elements.point.backgroundColor);
|
||||
this.lastActive[0]._model.borderColor = this.lastActive[0].custom && this.lastActive[0].custom.borderColor ? this.lastActive[0].custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.pointBorderColor, index, this.options.elements.point.borderColor);
|
||||
this.lastActive[0]._model.borderWidth = this.lastActive[0].custom && this.lastActive[0].custom.borderWidth ? this.lastActive[0].custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, this.options.elements.point.borderWidth);
|
||||
break;
|
||||
case 'label':
|
||||
for (var i = 0; i < this.lastActive.length; i++) {
|
||||
dataset = this.data.datasets[this.lastActive[i]._datasetIndex];
|
||||
index = this.lastActive[i]._index;
|
||||
|
||||
this.lastActive[i].radius = dataset.pointRadius;
|
||||
this.lastActive[i].backgroundColor = dataset.pointBackgroundColor;
|
||||
this.lastActive[i].borderColor = dataset.pointBorderColor;
|
||||
this.lastActive[i].borderWidth = dataset.pointBorderWidth;
|
||||
this.lastActive[i]._model.radius = this.lastActive[i].custom && this.lastActive[i].custom.radius ? this.lastActive[i].custom.pointRadius : helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, this.options.elements.point.radius);
|
||||
this.lastActive[i]._model.backgroundColor = this.lastActive[i].custom && this.lastActive[i].custom.backgroundColor ? this.lastActive[i].custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointBackgroundColor, index, this.options.elements.point.backgroundColor);
|
||||
this.lastActive[i]._model.borderColor = this.lastActive[i].custom && this.lastActive[i].custom.borderColor ? this.lastActive[i].custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.pointBorderColor, index, this.options.elements.point.borderColor);
|
||||
this.lastActive[i]._model.borderWidth = this.lastActive[i].custom && this.lastActive[i].custom.borderWidth ? this.lastActive[i].custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, this.options.elements.point.borderWidth);
|
||||
}
|
||||
break;
|
||||
case 'dataset':
|
||||
@ -505,20 +540,22 @@
|
||||
switch (this.options.hover.mode) {
|
||||
case 'single':
|
||||
dataset = this.data.datasets[this.active[0]._datasetIndex];
|
||||
index = this.active[0]._index;
|
||||
|
||||
this.active[0].radius = dataset.pointHoverRadius || dataset.pointRadius + 2;
|
||||
this.active[0].backgroundColor = dataset.pointHoverBackgroundColor || helpers.color(dataset.pointBackgroundColor).saturate(0.5).darken(0.35).rgbString();
|
||||
this.active[0].borderColor = dataset.pointHoverBorderColor || helpers.color(dataset.pointBorderColor).saturate(0.5).darken(0.35).rgbString();
|
||||
this.active[0].borderWidth = dataset.pointHoverBorderWidth || dataset.pointBorderWidth + 2;
|
||||
this.active[0]._model.radius = this.active[0].custom && this.active[0].custom.hoverRadius ? this.active[0].custom.hoverRadius : helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.active[0]._model.radius + 2);
|
||||
this.active[0]._model.backgroundColor = this.active[0].custom && this.active[0].custom.hoverBackgroundColor ? this.active[0].custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.color(this.active[0]._model.backgroundColor).saturate(0.5).darken(0.35).rgbString());
|
||||
this.active[0]._model.borderColor = this.active[0].custom && this.active[0].custom.hoverBorderColor ? this.active[0].custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.color(this.active[0]._model.borderColor).saturate(0.5).darken(0.35).rgbString());
|
||||
this.active[0]._model.borderWidth = this.active[0].custom && this.active[0].custom.hoverBorderWidth ? this.active[0].custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, this.active[0]._model.borderWidth + 2);
|
||||
break;
|
||||
case 'label':
|
||||
for (var i = 0; i < this.active.length; i++) {
|
||||
dataset = this.data.datasets[this.active[i]._datasetIndex];
|
||||
index = this.active[i]._index;
|
||||
|
||||
this.active[i].radius = dataset.pointHoverRadius || dataset.pointRadius + 2;
|
||||
this.active[i].backgroundColor = dataset.pointHoverBackgroundColor || helpers.color(dataset.pointBackgroundColor).saturate(0.5).darken(0.35).rgbString();
|
||||
this.active[i].borderColor = dataset.pointHoverBorderColor || helpers.color(dataset.pointBorderColor).saturate(0.5).darken(0.35).rgbString();
|
||||
this.active[i].borderWidth = dataset.pointHoverBorderWidth || dataset.pointBorderWidth + 2;
|
||||
this.active[i]._model.radius = this.active[i].custom && this.active[i].custom.hoverRadius ? this.active[i].custom.hoverRadius : helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.active[i]._model.radius + 2);
|
||||
this.active[i]._model.backgroundColor = this.active[i].custom && this.active[i].custom.hoverBackgroundColor ? this.active[i].custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.color(this.active[i]._model.backgroundColor).saturate(0.5).darken(0.35).rgbString());
|
||||
this.active[i]._model.borderColor = this.active[i].custom && this.active[i].custom.hoverBorderColor ? this.active[i].custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.color(this.active[i]._model.borderColor).saturate(0.5).darken(0.35).rgbString());
|
||||
this.active[i]._model.borderWidth = this.active[i].custom && this.active[i].custom.hoverBorderWidth ? this.active[i].custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, this.active[i]._model.borderWidth + 2);
|
||||
}
|
||||
break;
|
||||
case 'dataset':
|
||||
@ -537,17 +574,16 @@
|
||||
|
||||
// Active
|
||||
if (this.active.length) {
|
||||
this.tooltip._model.opacity = 1;
|
||||
|
||||
helpers.extend(this.tooltip, {
|
||||
opacity: 1,
|
||||
_active: this.active,
|
||||
});
|
||||
|
||||
this.tooltip.update();
|
||||
} else {
|
||||
// Inactive
|
||||
helpers.extend(this.tooltip, {
|
||||
opacity: 0,
|
||||
});
|
||||
this.tooltip._model.opacity = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -570,7 +606,7 @@
|
||||
(this.lastActive.length && this.active.length && changed)) {
|
||||
|
||||
this.stop();
|
||||
this.render(this.options.hoverAnimationDuration);
|
||||
this.render(this.options.hover.animationDuration);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -8,14 +8,6 @@
|
||||
|
||||
var defaultConfig = {
|
||||
|
||||
segment: {
|
||||
//String - The colour of the border on each segment.
|
||||
borderColor: "#fff",
|
||||
|
||||
//Number - The width of the border value in pixels
|
||||
borderWidth: 2,
|
||||
},
|
||||
|
||||
scale: {
|
||||
scaleType: "radialLinear",
|
||||
display: true,
|
||||
@ -60,9 +52,6 @@
|
||||
|
||||
//Boolean - Whether to animate the rotation of the chart
|
||||
animateRotate: true,
|
||||
|
||||
//String - A legend template
|
||||
legendTemplate: "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<segments.length; i++){%><li><span style=\"background-color:<%=segments[i].fillColor%>\"></span><%if(segments[i].label){%><%=segments[i].label%><%}%></li><%}%></ul>"
|
||||
};
|
||||
|
||||
|
||||
@ -91,56 +80,40 @@
|
||||
this.min = null;
|
||||
this.max = null;
|
||||
|
||||
helpers.each(self.data.data, function(data) {
|
||||
helpers.each(self.data.datasets[0].data, function(value) {
|
||||
if (this.min === null) {
|
||||
this.min = data.value;
|
||||
} else if (data.value < this.min) {
|
||||
this.min = data.value;
|
||||
this.min = value;
|
||||
} else if (value < this.min) {
|
||||
this.min = value;
|
||||
}
|
||||
|
||||
if (this.max === null) {
|
||||
this.max = data.value;
|
||||
} else if (data.value > this.max) {
|
||||
this.max = data.value;
|
||||
this.max = value;
|
||||
} else if (value > this.max) {
|
||||
this.max = value;
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
});
|
||||
|
||||
//Declare segment class as a chart instance specific class, so it can share props for this instance
|
||||
this.Slice = Chart.Arc.extend();
|
||||
helpers.bindEvents(this, this.options.events, this.events);
|
||||
|
||||
//Set up tooltip events on the chart
|
||||
if (this.options.showTooltips) {
|
||||
helpers.bindEvents(this, this.options.events, this.onHover);
|
||||
}
|
||||
helpers.bindEvents(this, this.options.events, this.events);
|
||||
|
||||
// Create new slice for each piece of data
|
||||
this.data.metaData = [];
|
||||
helpers.each(this.data.data, function(slice, index) {
|
||||
var metaSlice = new this.Slice({
|
||||
_chart: this.chart,
|
||||
innerRadius: 0,
|
||||
startAngle: Math.PI * 1.5,
|
||||
endAngle: Math.PI * 1.5,
|
||||
x: this.chart.width / 2,
|
||||
y: this.chart.height / 2
|
||||
});
|
||||
if (typeof slice == 'number') {
|
||||
helpers.extend(metaSlice, {
|
||||
value: slice
|
||||
});
|
||||
} else {
|
||||
helpers.extend(metaSlice, slice);
|
||||
}
|
||||
if (!metaSlice.backgroundColor) {
|
||||
slice.backgroundColor = 'hsl(' + (360 * index / this.data.data.length) + ', 100%, 50%)';
|
||||
}
|
||||
metaSlice.save();
|
||||
this.data.metaData.push(metaSlice);
|
||||
//Create a new bar for each piece of data
|
||||
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
|
||||
dataset.metaData = [];
|
||||
helpers.each(dataset.data, function(dataPoint, index) {
|
||||
dataset.metaData.push(new Chart.Arc({
|
||||
_chart: this.chart,
|
||||
_datasetIndex: datasetIndex,
|
||||
_index: index,
|
||||
_model: {}
|
||||
}));
|
||||
}, this);
|
||||
}, this);
|
||||
|
||||
|
||||
// Create tooltip instance exclusively for this chart with some defaults.
|
||||
this.tooltip = new Chart.Tooltip({
|
||||
_chart: this.chart,
|
||||
@ -148,6 +121,15 @@
|
||||
_options: this.options,
|
||||
}, this);
|
||||
|
||||
// Fit the scale before we animate
|
||||
this.updateScaleRange();
|
||||
this.scale.calculateRange();
|
||||
Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height);
|
||||
|
||||
// so that we animate nicely
|
||||
this.resetElements();
|
||||
|
||||
// Update the chart with the latest data.
|
||||
this.update();
|
||||
|
||||
},
|
||||
@ -158,6 +140,39 @@
|
||||
yCenter: this.chart.height / 2
|
||||
});
|
||||
},
|
||||
resetElements: function() {
|
||||
var circumference = 1 / this.data.datasets[0].data.length * 2;
|
||||
|
||||
// Map new data to data points
|
||||
helpers.each(this.data.datasets[0].metaData, function(slice, index) {
|
||||
|
||||
var value = this.data.datasets[0].data[index];
|
||||
|
||||
var startAngle = Math.PI * 1.5 + (Math.PI * circumference) * index;
|
||||
var endAngle = startAngle + (circumference * Math.PI);
|
||||
|
||||
helpers.extend(slice, {
|
||||
_index: index,
|
||||
_model: {
|
||||
x: this.chart.width / 2,
|
||||
y: this.chart.height / 2,
|
||||
innerRadius: 0,
|
||||
outerRadius: 0,
|
||||
startAngle: Math.PI * 1.5,
|
||||
endAngle: Math.PI * 1.5,
|
||||
|
||||
backgroundColor: slice.custom && slice.custom.backgroundColor ? slice.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.data.datasets[0].backgroundColor, index, this.options.elements.slice.backgroundColor),
|
||||
hoverBackgroundColor: slice.custom && slice.custom.hoverBackgroundColor ? slice.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(this.data.datasets[0].hoverBackgroundColor, index, this.options.elements.slice.hoverBackgroundColor),
|
||||
borderWidth: slice.custom && slice.custom.borderWidth ? slice.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.data.datasets[0].borderWidth, index, this.options.elements.slice.borderWidth),
|
||||
borderColor: slice.custom && slice.custom.borderColor ? slice.custom.borderColor : helpers.getValueAtIndexOrDefault(this.data.datasets[0].borderColor, index, this.options.elements.slice.borderColor),
|
||||
|
||||
label: helpers.getValueAtIndexOrDefault(this.data.datasets[0].labels, index, this.data.datasets[0].labels[index])
|
||||
},
|
||||
});
|
||||
|
||||
slice.pivot();
|
||||
}, this);
|
||||
},
|
||||
update: function() {
|
||||
|
||||
this.updateScaleRange();
|
||||
@ -165,36 +180,40 @@
|
||||
this.scale.generateTicks();
|
||||
this.scale.buildYLabels();
|
||||
|
||||
this.outerRadius = (helpers.min([this.chart.width, this.chart.height]) - this.options.segment.borderWidth / 2) / 2;
|
||||
Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height);
|
||||
|
||||
var circumference = 1 / this.data.data.length * 2;
|
||||
var circumference = 1 / this.data.datasets[0].data.length * 2;
|
||||
|
||||
// Map new data to data points
|
||||
helpers.each(this.data.metaData, function(slice, index) {
|
||||
helpers.each(this.data.datasets[0].metaData, function(slice, index) {
|
||||
|
||||
var datapoint = this.data.data[index];
|
||||
var value = this.data.datasets[0].data[index];
|
||||
|
||||
var startAngle = Math.PI * 1.5 + (Math.PI * circumference) * index;
|
||||
var endAngle = startAngle + (circumference * Math.PI);
|
||||
|
||||
helpers.extend(slice, {
|
||||
_index: index,
|
||||
x: this.chart.width / 2,
|
||||
y: this.chart.height / 2,
|
||||
value: datapoint.value,
|
||||
label: datapoint.label,
|
||||
innerRadius: 0,
|
||||
outerRadius: this.scale.calculateCenterOffset(slice.value),
|
||||
startAngle: startAngle,
|
||||
endAngle: endAngle,
|
||||
_model: {
|
||||
x: this.chart.width / 2,
|
||||
y: this.chart.height / 2,
|
||||
innerRadius: 0,
|
||||
outerRadius: this.scale.calculateCenterOffset(value),
|
||||
startAngle: startAngle,
|
||||
endAngle: endAngle,
|
||||
|
||||
backgroundColor: datapoint.backgroundColor,
|
||||
hoverBackgroundColor: datapoint.hoverBackgroundColor || datapoint.backgroundColor,
|
||||
borderWidth: this.options.borderWidth,
|
||||
borderColor: this.options.segmentStrokeColor,
|
||||
backgroundColor: slice.custom && slice.custom.backgroundColor ? slice.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.data.datasets[0].backgroundColor, index, this.options.elements.slice.backgroundColor),
|
||||
hoverBackgroundColor: slice.custom && slice.custom.hoverBackgroundColor ? slice.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(this.data.datasets[0].hoverBackgroundColor, index, this.options.elements.slice.hoverBackgroundColor),
|
||||
borderWidth: slice.custom && slice.custom.borderWidth ? slice.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.data.datasets[0].borderWidth, index, this.options.elements.slice.borderWidth),
|
||||
borderColor: slice.custom && slice.custom.borderColor ? slice.custom.borderColor : helpers.getValueAtIndexOrDefault(this.data.datasets[0].borderColor, index, this.options.elements.slice.borderColor),
|
||||
|
||||
label: helpers.getValueAtIndexOrDefault(this.data.datasets[0].labels, index, this.data.datasets[0].labels[index])
|
||||
},
|
||||
});
|
||||
slice.pivot();
|
||||
|
||||
console.log(slice);
|
||||
|
||||
}, this);
|
||||
|
||||
this.render();
|
||||
@ -204,12 +223,181 @@
|
||||
|
||||
this.clear();
|
||||
|
||||
helpers.each(this.data.metaData, function(segment, index) {
|
||||
segment.transition(easingDecimal).draw();
|
||||
helpers.each(this.data.datasets[0].metaData, function(slice, index) {
|
||||
slice.transition(easingDecimal).draw();
|
||||
}, this);
|
||||
|
||||
this.scale.draw();
|
||||
}
|
||||
|
||||
this.tooltip.transition(easingDecimal).draw();
|
||||
},
|
||||
events: function(e) {
|
||||
|
||||
// If exiting chart
|
||||
if (e.type == 'mouseout') {
|
||||
return this;
|
||||
}
|
||||
|
||||
this.lastActive = this.lastActive || [];
|
||||
|
||||
// Find Active Elements
|
||||
this.active = function() {
|
||||
switch (this.options.hover.mode) {
|
||||
case 'single':
|
||||
return this.getSliceAtEvent(e);
|
||||
case 'label':
|
||||
return this.getSlicesAtEvent(e);
|
||||
case 'dataset':
|
||||
return this.getDatasetAtEvent(e);
|
||||
default:
|
||||
return e;
|
||||
}
|
||||
}.call(this);
|
||||
|
||||
// On Hover hook
|
||||
if (this.options.hover.onHover) {
|
||||
this.options.hover.onHover.call(this, this.active);
|
||||
}
|
||||
|
||||
if (e.type == 'mouseup' || e.type == 'click') {
|
||||
if (this.options.onClick) {
|
||||
this.options.onClick.call(this, e, this.active);
|
||||
}
|
||||
}
|
||||
|
||||
var dataset;
|
||||
var index;
|
||||
// Remove styling for last active (even if it may still be active)
|
||||
if (this.lastActive.length) {
|
||||
switch (this.options.hover.mode) {
|
||||
case 'single':
|
||||
dataset = this.data.datasets[this.lastActive[0]._datasetIndex];
|
||||
index = this.lastActive[0]._index;
|
||||
|
||||
this.lastActive[0]._model.backgroundColor = this.lastActive[0].custom && this.lastActive[0].custom.backgroundColor ? this.lastActive[0].custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, this.options.elements.slice.backgroundColor);
|
||||
this.lastActive[0]._model.borderColor = this.lastActive[0].custom && this.lastActive[0].custom.borderColor ? this.lastActive[0].custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, this.options.elements.slice.borderColor);
|
||||
this.lastActive[0]._model.borderWidth = this.lastActive[0].custom && this.lastActive[0].custom.borderWidth ? this.lastActive[0].custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, this.options.elements.slice.borderWidth);
|
||||
break;
|
||||
case 'label':
|
||||
for (var i = 0; i < this.lastActive.length; i++) {
|
||||
dataset = this.data.datasets[this.lastActive[i]._datasetIndex];
|
||||
index = this.lastActive[i]._index;
|
||||
|
||||
this.lastActive[i]._model.backgroundColor = this.lastActive[i].custom && this.lastActive[i].custom.backgroundColor ? this.lastActive[i].custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, this.options.elements.slice.backgroundColor);
|
||||
this.lastActive[i]._model.borderColor = this.lastActive[i].custom && this.lastActive[i].custom.borderColor ? this.lastActive[i].custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, this.options.elements.slice.borderColor);
|
||||
this.lastActive[i]._model.borderWidth = this.lastActive[i].custom && this.lastActive[i].custom.borderWidth ? this.lastActive[i].custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, this.options.elements.slice.borderWidth);
|
||||
}
|
||||
break;
|
||||
case 'dataset':
|
||||
break;
|
||||
default:
|
||||
// Don't change anything
|
||||
}
|
||||
}
|
||||
|
||||
// Built in hover styling
|
||||
if (this.active.length && this.options.hover.mode) {
|
||||
switch (this.options.hover.mode) {
|
||||
case 'single':
|
||||
dataset = this.data.datasets[this.active[0]._datasetIndex];
|
||||
index = this.active[0]._index;
|
||||
|
||||
this.active[0]._model.radius = this.active[0].custom && this.active[0].custom.hoverRadius ? this.active[0].custom.hoverRadius : helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.active[0]._model.radius + 2);
|
||||
this.active[0]._model.backgroundColor = this.active[0].custom && this.active[0].custom.hoverBackgroundColor ? this.active[0].custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.color(this.active[0]._model.backgroundColor).saturate(0.5).darken(0.35).rgbString());
|
||||
this.active[0]._model.borderColor = this.active[0].custom && this.active[0].custom.hoverBorderColor ? this.active[0].custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.color(this.active[0]._model.borderColor).saturate(0.5).darken(0.35).rgbString());
|
||||
this.active[0]._model.borderWidth = this.active[0].custom && this.active[0].custom.hoverBorderWidth ? this.active[0].custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, this.active[0]._model.borderWidth + 2);
|
||||
break;
|
||||
case 'label':
|
||||
for (var i = 0; i < this.active.length; i++) {
|
||||
dataset = this.data.datasets[this.active[i]._datasetIndex];
|
||||
index = this.active[i]._index;
|
||||
|
||||
this.active[i]._model.radius = this.active[i].custom && this.active[i].custom.hoverRadius ? this.active[i].custom.hoverRadius : helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.active[i]._model.radius + 2);
|
||||
this.active[i]._model.backgroundColor = this.active[i].custom && this.active[i].custom.hoverBackgroundColor ? this.active[i].custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.color(this.active[i]._model.backgroundColor).saturate(0.5).darken(0.35).rgbString());
|
||||
this.active[i]._model.borderColor = this.active[i].custom && this.active[i].custom.hoverBorderColor ? this.active[i].custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.color(this.active[i]._model.borderColor).saturate(0.5).darken(0.35).rgbString());
|
||||
this.active[i]._model.borderWidth = this.active[i].custom && this.active[i].custom.hoverBorderWidth ? this.active[i].custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, this.active[i]._model.borderWidth + 2);
|
||||
}
|
||||
break;
|
||||
case 'dataset':
|
||||
break;
|
||||
default:
|
||||
// Don't change anything
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Built in Tooltips
|
||||
if (this.options.tooltips.enabled) {
|
||||
|
||||
// The usual updates
|
||||
this.tooltip.initialize();
|
||||
|
||||
// Active
|
||||
if (this.active.length) {
|
||||
this.tooltip._model.opacity = 1;
|
||||
|
||||
helpers.extend(this.tooltip, {
|
||||
_active: this.active,
|
||||
});
|
||||
|
||||
this.tooltip.update();
|
||||
} else {
|
||||
// Inactive
|
||||
this.tooltip._model.opacity = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Hover animations
|
||||
this.tooltip.pivot();
|
||||
|
||||
if (!this.animating) {
|
||||
var changed;
|
||||
|
||||
helpers.each(this.active, function(element, index) {
|
||||
if (element !== this.lastActive[index]) {
|
||||
changed = true;
|
||||
}
|
||||
}, this);
|
||||
|
||||
// If entering, leaving, or changing elements, animate the change via pivot
|
||||
if ((!this.lastActive.length && this.active.length) ||
|
||||
(this.lastActive.length && !this.active.length) ||
|
||||
(this.lastActive.length && this.active.length && changed)) {
|
||||
|
||||
this.stop();
|
||||
this.render(this.options.hover.animationDuration);
|
||||
}
|
||||
}
|
||||
|
||||
// Remember Last Active
|
||||
this.lastActive = this.active;
|
||||
return this;
|
||||
},
|
||||
getSliceAtEvent: function(e) {
|
||||
var elements = [];
|
||||
|
||||
var location = helpers.getRelativePosition(e);
|
||||
|
||||
this.eachElement(function(slice, index) {
|
||||
if (slice.inRange(location.x, location.y)) {
|
||||
elements.push(slice);
|
||||
}
|
||||
}, this);
|
||||
return elements;
|
||||
},
|
||||
/*getSlicesAtEvent: function(e) {
|
||||
var elements = [];
|
||||
|
||||
var location = helpers.getRelativePosition(e);
|
||||
|
||||
this.eachElement(function(slice, index) {
|
||||
if (slice.inGroupRange(location.x, location.y)) {
|
||||
elements.push(slice);
|
||||
}
|
||||
}, this);
|
||||
return elements;
|
||||
},*/
|
||||
});
|
||||
|
||||
}).call(this);
|
||||
|
||||
@ -73,47 +73,10 @@
|
||||
},
|
||||
},
|
||||
|
||||
line: {
|
||||
|
||||
//Boolean - Whether to show a dot for each point
|
||||
show: true,
|
||||
|
||||
//Number - Pixel width of dot border
|
||||
borderWidth: 1,
|
||||
|
||||
backgroundColor: Chart.defaults.global.defaultColor,
|
||||
|
||||
borderColor: Chart.defaults.global.defaultColor,
|
||||
|
||||
//Number - amount extra to add to the radius to cater for hit detection outside the drawn
|
||||
hitRadius: 20,
|
||||
|
||||
//Number - Tension of the bezier curve between points. Use 0 to turn off bezier tension
|
||||
tension: 0.4,
|
||||
},
|
||||
|
||||
point: {
|
||||
//Boolean - Whether to show a dot for each point
|
||||
show: true,
|
||||
|
||||
//Number - Radius of each dot in pixels
|
||||
radius: 3,
|
||||
|
||||
//Number - Pixel width of dot border
|
||||
borderWidth: 1,
|
||||
|
||||
//Number - Pixel width of on hover
|
||||
hoverRadius: 5,
|
||||
|
||||
//Number - Pixel width of dot border on hover
|
||||
hoverBorderWidth: 2,
|
||||
|
||||
backgroundColor: Chart.defaults.global.defaultColor,
|
||||
|
||||
borderColor: Chart.defaults.global.defaultColor,
|
||||
|
||||
//Number - amount extra to add to the radius to cater for hit detection outside the drawn
|
||||
hitRadius: 20,
|
||||
elements: {
|
||||
line: {
|
||||
tension: 0, // no bezier in radar
|
||||
}
|
||||
},
|
||||
|
||||
//String - A legend template
|
||||
@ -122,73 +85,37 @@
|
||||
},
|
||||
|
||||
initialize: function() {
|
||||
|
||||
this.buildScale(this.data);
|
||||
|
||||
|
||||
|
||||
// Events
|
||||
helpers.bindEvents(this, this.options.events, this.events);
|
||||
|
||||
var _this = this;
|
||||
|
||||
//Create a new line and its points for each dataset and piece of data
|
||||
// Create a new line and its points for each dataset and piece of data
|
||||
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
|
||||
dataset.metaDataset = new Chart.Line();
|
||||
dataset.metaData = [];
|
||||
helpers.each(dataset.data, function(dataPoint, index) {
|
||||
dataset.metaData.push(new Chart.Point());
|
||||
}, this);
|
||||
|
||||
// The line chart only supports a single x axis because the x axis is always a dataset axis
|
||||
}, this);
|
||||
|
||||
// Build and fit the scale. Needs to happen after the axis IDs have been set
|
||||
this.buildScale();
|
||||
Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height);
|
||||
|
||||
// Set defaults for lines
|
||||
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
|
||||
helpers.extend(dataset.metaDataset, {
|
||||
dataset.metaDataset = new Chart.Line({
|
||||
_chart: this.chart,
|
||||
_datasetIndex: datasetIndex,
|
||||
_points: dataset.metaData,
|
||||
_datasetIndex: datasetIndex,
|
||||
_chart: this.chart,
|
||||
loop: true
|
||||
_loop: true
|
||||
});
|
||||
// Fill in dataset defaults from options
|
||||
helpers.extend(dataset, helpers.merge(this.options, dataset));
|
||||
// Copy to view modele
|
||||
dataset.metaDataset.save();
|
||||
|
||||
dataset.metaData = [];
|
||||
|
||||
helpers.each(dataset.data, function(dataPoint, index) {
|
||||
dataset.metaData.push(new Chart.Point({
|
||||
_datasetIndex: datasetIndex,
|
||||
_index: index,
|
||||
_chart: this.chart,
|
||||
_model: {
|
||||
x: 0, //xScale.getPixelForValue(null, index, true),
|
||||
y: 0, //this.chartArea.bottom,
|
||||
},
|
||||
}));
|
||||
|
||||
}, this);
|
||||
}, this);
|
||||
|
||||
// Set defaults for points
|
||||
this.eachElement(function(point, index, dataset, datasetIndex) {
|
||||
|
||||
helpers.extend(point, {
|
||||
_datasetIndex: datasetIndex,
|
||||
_index: index,
|
||||
_chart: this.chart,
|
||||
display: this.options.pointDot,
|
||||
x: this.scale.xCenter,
|
||||
y: this.scale.yCenter,
|
||||
});
|
||||
|
||||
// Default bezier control points
|
||||
helpers.extend(point, {
|
||||
controlPointPreviousX: this.scale.xCenter,
|
||||
controlPointPreviousY: this.scale.yCenter,
|
||||
controlPointNextX: this.scale.xCenter,
|
||||
controlPointNextY: this.scale.yCenter,
|
||||
});
|
||||
// Copy to view model
|
||||
point.save();
|
||||
|
||||
}, this);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Build the scale.
|
||||
this.buildScale();
|
||||
|
||||
// Create tooltip instance exclusively for this chart with some defaults.
|
||||
this.tooltip = new Chart.Tooltip({
|
||||
@ -197,42 +124,169 @@
|
||||
_options: this.options,
|
||||
}, this);
|
||||
|
||||
// Need to fit scales before we reset elements.
|
||||
Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height);
|
||||
|
||||
// Reset so that we animation from the baseline
|
||||
this.resetElements();
|
||||
|
||||
|
||||
|
||||
// Update that shiz
|
||||
this.update();
|
||||
},
|
||||
|
||||
/*getPointsAtEvent: function(evt) {
|
||||
var mousePosition = helpers.getRelativePosition(evt),
|
||||
fromCenter = helpers.getAngleFromPoint({
|
||||
x: this.scale.xCenter,
|
||||
y: this.scale.yCenter
|
||||
}, mousePosition);
|
||||
|
||||
var anglePerIndex = (Math.PI * 2) / this.scale.valuesCount,
|
||||
pointIndex = Math.round((fromCenter.angle - Math.PI * 1.5) / anglePerIndex),
|
||||
activePointsCollection = [];
|
||||
|
||||
// If we're at the top, make the pointIndex 0 to get the first of the array.
|
||||
if (pointIndex >= this.scale.valuesCount || pointIndex < 0) {
|
||||
pointIndex = 0;
|
||||
}
|
||||
|
||||
if (fromCenter.distance <= this.scale.drawingArea) {
|
||||
helpers.each(this.data.datasets, function(dataset) {
|
||||
activePointsCollection.push(dataset.points[pointIndex]);
|
||||
});
|
||||
}
|
||||
|
||||
return activePointsCollection;
|
||||
},*/
|
||||
nextPoint: function(collection, index) {
|
||||
return collection[index - 1] || collection[collection.length - 1];
|
||||
return collection[index + 1] || collection[0];
|
||||
},
|
||||
previousPoint: function(collection, index) {
|
||||
return collection[index + 1] || collection[0];
|
||||
return collection[index - 1] || collection[collection.length - 1];
|
||||
},
|
||||
resetElements: function() {
|
||||
|
||||
// Update the points
|
||||
this.eachElement(function(point, index, dataset, datasetIndex) {
|
||||
helpers.extend(point, {
|
||||
// Utility
|
||||
_chart: this.chart,
|
||||
_datasetIndex: datasetIndex,
|
||||
_index: index,
|
||||
_scale: this.scale,
|
||||
|
||||
// Desired view properties
|
||||
_model: {
|
||||
x: this.scale.xCenter,
|
||||
y: this.scale.yCenter,
|
||||
|
||||
// Appearance
|
||||
tension: point.custom && point.custom.tension ? point.custom.tension : this.options.elements.line.tension,
|
||||
radius: point.custom && point.custom.radius ? point.custom.pointRadius : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointRadius, index, this.options.elements.point.radius),
|
||||
backgroundColor: point.custom && point.custom.backgroundColor ? point.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointBackgroundColor, index, this.options.elements.point.backgroundColor),
|
||||
borderColor: point.custom && point.custom.borderColor ? point.custom.borderColor : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointBorderColor, index, this.options.elements.point.borderColor),
|
||||
borderWidth: point.custom && point.custom.borderWidth ? point.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointBorderWidth, index, this.options.elements.point.borderWidth),
|
||||
skip: typeof this.data.datasets[datasetIndex].data[index] != 'number',
|
||||
|
||||
// Tooltip
|
||||
hoverRadius: point.custom && point.custom.hoverRadius ? point.custom.hoverRadius : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointHitRadius, index, this.options.elements.point.hitRadius),
|
||||
},
|
||||
});
|
||||
}, this);
|
||||
|
||||
// Update control points for the bezier curve
|
||||
this.eachElement(function(point, index, dataset, datasetIndex) {
|
||||
var controlPoints = helpers.splineCurve(
|
||||
this.previousPoint(dataset, index)._model,
|
||||
point._model,
|
||||
this.nextPoint(dataset, index)._model,
|
||||
point._model.tension
|
||||
);
|
||||
|
||||
point._model.controlPointPreviousX = this.scale.xCenter;
|
||||
point._model.controlPointPreviousY = this.scale.yCenter;
|
||||
point._model.controlPointNextX = this.scale.xCenter;
|
||||
point._model.controlPointNextY = this.scale.yCenter;
|
||||
|
||||
// Now pivot the point for animation
|
||||
point.pivot();
|
||||
}, this);
|
||||
},
|
||||
update: function() {
|
||||
Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height);
|
||||
|
||||
// Update the lines
|
||||
this.eachDataset(function(dataset, datasetIndex) {
|
||||
helpers.extend(dataset.metaDataset, {
|
||||
// Utility
|
||||
_datasetIndex: datasetIndex,
|
||||
|
||||
// Data
|
||||
_children: dataset.metaData,
|
||||
|
||||
// Model
|
||||
_model: {
|
||||
// Appearance
|
||||
tension: dataset.tension || this.options.elements.line.tension,
|
||||
backgroundColor: dataset.backgroundColor || this.options.elements.line.backgroundColor,
|
||||
borderWidth: dataset.borderWidth || this.options.elements.line.borderWidth,
|
||||
borderColor: dataset.borderColor || this.options.elements.line.borderColor,
|
||||
fill: dataset.fill !== undefined ? dataset.fill : this.options.elements.line.fill, // use the value from the dataset if it was provided. else fall back to the default
|
||||
skipNull: dataset.skipNull !== undefined ? dataset.skipNull : this.options.elements.line.skipNull,
|
||||
drawNull: dataset.drawNull !== undefined ? dataset.drawNull : this.options.elements.line.drawNull,
|
||||
|
||||
// Scale
|
||||
scaleTop: this.scale.top,
|
||||
scaleBottom: this.scale.bottom,
|
||||
scaleZero: this.scale.getPointPosition(0),
|
||||
},
|
||||
});
|
||||
|
||||
dataset.metaDataset.pivot();
|
||||
});
|
||||
|
||||
// Update the points
|
||||
this.eachElement(function(point, index, dataset, datasetIndex) {
|
||||
var pointPosition = this.scale.getPointPosition(index, this.scale.calculateCenterOffset(this.data.datasets[datasetIndex].data[index]));
|
||||
|
||||
helpers.extend(point, {
|
||||
// Utility
|
||||
_chart: this.chart,
|
||||
_datasetIndex: datasetIndex,
|
||||
_index: index,
|
||||
|
||||
// Desired view properties
|
||||
_model: {
|
||||
x: pointPosition.x, // value not used in dataset scale, but we want a consistent API between scales
|
||||
y: pointPosition.y,
|
||||
|
||||
// Appearance
|
||||
tension: point.custom && point.custom.tension ? point.custom.tension : this.options.elements.line.tension,
|
||||
radius: point.custom && point.custom.radius ? point.custom.pointRadius : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointRadius, index, this.options.elements.point.radius),
|
||||
backgroundColor: point.custom && point.custom.backgroundColor ? point.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointBackgroundColor, index, this.options.elements.point.backgroundColor),
|
||||
borderColor: point.custom && point.custom.borderColor ? point.custom.borderColor : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointBorderColor, index, this.options.elements.point.borderColor),
|
||||
borderWidth: point.custom && point.custom.borderWidth ? point.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointBorderWidth, index, this.options.elements.point.borderWidth),
|
||||
skip: typeof this.data.datasets[datasetIndex].data[index] != 'number',
|
||||
|
||||
// Tooltip
|
||||
hoverRadius: point.custom && point.custom.hoverRadius ? point.custom.hoverRadius : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointHitRadius, index, this.options.elements.point.hitRadius),
|
||||
},
|
||||
});
|
||||
}, this);
|
||||
|
||||
|
||||
// Update control points for the bezier curve
|
||||
this.eachElement(function(point, index, dataset, datasetIndex) {
|
||||
var controlPoints = helpers.splineCurve(
|
||||
this.previousPoint(dataset, index)._model,
|
||||
point._model,
|
||||
this.nextPoint(dataset, index)._model,
|
||||
point._model.tension
|
||||
);
|
||||
|
||||
point._model.controlPointPreviousX = controlPoints.previous.x;
|
||||
point._model.controlPointNextX = controlPoints.next.x;
|
||||
|
||||
// Prevent the bezier going outside of the bounds of the graph
|
||||
|
||||
// Cap puter bezier handles to the upper/lower scale bounds
|
||||
if (controlPoints.next.y > this.chartArea.bottom) {
|
||||
point._model.controlPointNextY = this.chartArea.bottom;
|
||||
} else if (controlPoints.next.y < this.chartArea.top) {
|
||||
point._model.controlPointNextY = this.chartArea.top;
|
||||
} else {
|
||||
point._model.controlPointNextY = controlPoints.next.y;
|
||||
}
|
||||
|
||||
// Cap inner bezier handles to the upper/lower scale bounds
|
||||
if (controlPoints.previous.y > this.chartArea.bottom) {
|
||||
point._model.controlPointPreviousY = this.chartArea.bottom;
|
||||
} else if (controlPoints.previous.y < this.chartArea.top) {
|
||||
point._model.controlPointPreviousY = this.chartArea.top;
|
||||
} else {
|
||||
point._model.controlPointPreviousY = controlPoints.previous.y;
|
||||
}
|
||||
|
||||
// Now pivot the point for animation
|
||||
point.pivot();
|
||||
}, this);
|
||||
|
||||
this.render();
|
||||
},
|
||||
buildScale: function() {
|
||||
var self = this;
|
||||
@ -276,113 +330,17 @@
|
||||
this.scale.generateTicks();
|
||||
this.scale.buildYLabels();
|
||||
},
|
||||
update: function() {
|
||||
|
||||
// Update the lines
|
||||
this.eachDataset(function(dataset, datasetIndex) {
|
||||
|
||||
helpers.extend(dataset.metaDataset, {
|
||||
// Utility
|
||||
_datasetIndex: datasetIndex,
|
||||
|
||||
// Data
|
||||
_points: dataset.metaData,
|
||||
|
||||
// Geometry
|
||||
scaleTop: this.scale.top,
|
||||
scaleBottom: this.scale.bottom,
|
||||
scaleZero: this.scale.getPointPosition(0),
|
||||
|
||||
// Appearance
|
||||
tension: dataset.tension || this.options.line.tension,
|
||||
backgroundColor: dataset.backgroundColor || this.options.backgroundColor,
|
||||
borderWidth: dataset.borderWidth || this.options.borderWidth,
|
||||
borderColor: dataset.borderColor || this.options.borderColor,
|
||||
});
|
||||
dataset.metaDataset.pivot();
|
||||
}, this);
|
||||
|
||||
// Update the points
|
||||
this.eachElement(function(point, index, dataset, datasetIndex) {
|
||||
|
||||
var pointPosition = this.scale.getPointPosition(index, this.scale.calculateCenterOffset(this.data.datasets[datasetIndex].data[index]));
|
||||
|
||||
helpers.extend(point, {
|
||||
// Utility
|
||||
_chart: this.chart,
|
||||
_datasetIndex: datasetIndex,
|
||||
_index: index,
|
||||
|
||||
// Data
|
||||
label: this.data.labels[index],
|
||||
value: this.data.datasets[datasetIndex].data[index],
|
||||
datasetLabel: this.data.datasets[datasetIndex].label,
|
||||
|
||||
// Geometry
|
||||
offsetGridLines: this.options.offsetGridLines,
|
||||
x: pointPosition.x,
|
||||
y: pointPosition.y,
|
||||
tension: this.data.datasets[datasetIndex].metaDataset.tension,
|
||||
|
||||
// Appearnce
|
||||
radius: this.data.datasets[datasetIndex].pointRadius || this.options.point.radius,
|
||||
backgroundColor: this.data.datasets[datasetIndex].pointBackgroundColor || this.options.point.backgroundColor,
|
||||
borderWidth: this.data.datasets[datasetIndex].pointBorderWidth || this.options.pointsborderWidth,
|
||||
|
||||
// Tooltip
|
||||
hoverRadius: this.data.datasets[datasetIndex].pointHitRadius || this.options.point.hitRadius,
|
||||
});
|
||||
|
||||
}, this);
|
||||
|
||||
// Update control points for the bezier curve
|
||||
this.eachElement(function(point, index, dataset, datasetIndex) {
|
||||
var controlPoints = helpers.splineCurve(
|
||||
this.previousPoint(dataset, index),
|
||||
point,
|
||||
this.nextPoint(dataset, index),
|
||||
point.tension
|
||||
);
|
||||
|
||||
point.controlPointPreviousX = controlPoints.previous.x;
|
||||
point.controlPointNextX = controlPoints.next.x;
|
||||
|
||||
// Prevent the bezier going outside of the bounds of the graph
|
||||
|
||||
// Cap puter bezier handles to the upper/lower scale bounds
|
||||
if (controlPoints.next.y > this.chartArea.bottom) {
|
||||
point.controlPointNextY = this.chartArea.bottom;
|
||||
} else if (controlPoints.next.y < this.chartArea.top) {
|
||||
point.controlPointNextY = this.chartArea.top;
|
||||
} else {
|
||||
point.controlPointNextY = controlPoints.next.y;
|
||||
}
|
||||
|
||||
// Cap inner bezier handles to the upper/lower scale bounds
|
||||
if (controlPoints.previous.y > this.chartArea.bottom) {
|
||||
point.controlPointPreviousY = this.chartArea.bottom;
|
||||
} else if (controlPoints.previous.y < this.chartArea.top) {
|
||||
point.controlPointPreviousY = this.chartArea.top;
|
||||
} else {
|
||||
point.controlPointPreviousY = controlPoints.previous.y;
|
||||
}
|
||||
// Now pivot the point for animation
|
||||
point.pivot();
|
||||
}, this);
|
||||
|
||||
this.render();
|
||||
},
|
||||
draw: function(ease) {
|
||||
|
||||
var easingDecimal = ease || 1;
|
||||
this.clear();
|
||||
|
||||
// Draw all the scales
|
||||
this.scale.draw(this.chartArea);
|
||||
|
||||
// reverse for-loop for proper stacking
|
||||
for (var i = this.data.datasets.length - 1; i >= 0; i--) {
|
||||
|
||||
var dataset = this.data.datasets[i];
|
||||
var datasetIndex = i;
|
||||
|
||||
// Transition Point Locations
|
||||
helpers.each(dataset.metaData, function(point, index) {
|
||||
@ -398,12 +356,8 @@
|
||||
});
|
||||
}
|
||||
|
||||
// Draw all the scales
|
||||
this.scale.draw(this.chartArea);
|
||||
|
||||
// Finally draw the tooltip
|
||||
this.tooltip.transition(easingDecimal).draw();
|
||||
|
||||
},
|
||||
events: function(e) {
|
||||
|
||||
@ -429,8 +383,8 @@
|
||||
}.call(this);
|
||||
|
||||
// On Hover hook
|
||||
if (this.options.onHover) {
|
||||
this.options.onHover.call(this, this.active);
|
||||
if (this.options.hover.onHover) {
|
||||
this.options.hover.onHover.call(this, this.active);
|
||||
}
|
||||
|
||||
if (e.type == 'mouseup' || e.type == 'click') {
|
||||
@ -440,25 +394,28 @@
|
||||
}
|
||||
|
||||
var dataset;
|
||||
var index;
|
||||
// Remove styling for last active (even if it may still be active)
|
||||
if (this.lastActive.length) {
|
||||
switch (this.options.hover.mode) {
|
||||
case 'single':
|
||||
dataset = this.data.datasets[this.lastActive[0]._datasetIndex];
|
||||
index = this.lastActive[0]._index;
|
||||
|
||||
this.lastActive[0].radius = dataset.pointRadius;
|
||||
this.lastActive[0].backgroundColor = dataset.pointBackgroundColor;
|
||||
this.lastActive[0].borderColor = dataset.pointBorderColor;
|
||||
this.lastActive[0].borderWidth = dataset.pointBorderWidth;
|
||||
this.lastActive[0]._model.radius = this.lastActive[0].custom && this.lastActive[0].custom.radius ? this.lastActive[0].custom.pointRadius : helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, this.options.elements.point.radius);
|
||||
this.lastActive[0]._model.backgroundColor = this.lastActive[0].custom && this.lastActive[0].custom.backgroundColor ? this.lastActive[0].custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointBackgroundColor, index, this.options.elements.point.backgroundColor);
|
||||
this.lastActive[0]._model.borderColor = this.lastActive[0].custom && this.lastActive[0].custom.borderColor ? this.lastActive[0].custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.pointBorderColor, index, this.options.elements.point.borderColor);
|
||||
this.lastActive[0]._model.borderWidth = this.lastActive[0].custom && this.lastActive[0].custom.borderWidth ? this.lastActive[0].custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, this.options.elements.point.borderWidth);
|
||||
break;
|
||||
case 'label':
|
||||
for (var i = 0; i < this.lastActive.length; i++) {
|
||||
dataset = this.data.datasets[this.lastActive[i]._datasetIndex];
|
||||
index = this.lastActive[i]._index;
|
||||
|
||||
this.lastActive[i].radius = dataset.pointRadius;
|
||||
this.lastActive[i].backgroundColor = dataset.pointBackgroundColor;
|
||||
this.lastActive[i].borderColor = dataset.pointBorderColor;
|
||||
this.lastActive[i].borderWidth = dataset.pointBorderWidth;
|
||||
this.lastActive[i]._model.radius = this.lastActive[i].custom && this.lastActive[i].custom.radius ? this.lastActive[i].custom.pointRadius : helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, this.options.elements.point.radius);
|
||||
this.lastActive[i]._model.backgroundColor = this.lastActive[i].custom && this.lastActive[i].custom.backgroundColor ? this.lastActive[i].custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointBackgroundColor, index, this.options.elements.point.backgroundColor);
|
||||
this.lastActive[i]._model.borderColor = this.lastActive[i].custom && this.lastActive[i].custom.borderColor ? this.lastActive[i].custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.pointBorderColor, index, this.options.elements.point.borderColor);
|
||||
this.lastActive[i]._model.borderWidth = this.lastActive[i].custom && this.lastActive[i].custom.borderWidth ? this.lastActive[i].custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, this.options.elements.point.borderWidth);
|
||||
}
|
||||
break;
|
||||
case 'dataset':
|
||||
@ -473,20 +430,22 @@
|
||||
switch (this.options.hover.mode) {
|
||||
case 'single':
|
||||
dataset = this.data.datasets[this.active[0]._datasetIndex];
|
||||
index = this.active[0]._index;
|
||||
|
||||
this.active[0].radius = dataset.pointHoverRadius || dataset.pointRadius + 2;
|
||||
this.active[0].backgroundColor = dataset.pointHoverBackgroundColor || helpers.color(dataset.pointBackgroundColor).saturate(0.5).darken(0.35).rgbString();
|
||||
this.active[0].borderColor = dataset.pointHoverBorderColor || helpers.color(dataset.pointBorderColor).saturate(0.5).darken(0.35).rgbString();
|
||||
this.active[0].borderWidth = dataset.pointHoverBorderWidth || dataset.pointBorderWidth + 2;
|
||||
this.active[0]._model.radius = this.active[0].custom && this.active[0].custom.hoverRadius ? this.active[0].custom.hoverRadius : helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.active[0]._model.radius + 2);
|
||||
this.active[0]._model.backgroundColor = this.active[0].custom && this.active[0].custom.hoverBackgroundColor ? this.active[0].custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.color(this.active[0]._model.backgroundColor).saturate(0.5).darken(0.35).rgbString());
|
||||
this.active[0]._model.borderColor = this.active[0].custom && this.active[0].custom.hoverBorderColor ? this.active[0].custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.color(this.active[0]._model.borderColor).saturate(0.5).darken(0.35).rgbString());
|
||||
this.active[0]._model.borderWidth = this.active[0].custom && this.active[0].custom.hoverBorderWidth ? this.active[0].custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, this.active[0]._model.borderWidth + 2);
|
||||
break;
|
||||
case 'label':
|
||||
for (var i = 0; i < this.active.length; i++) {
|
||||
dataset = this.data.datasets[this.active[i]._datasetIndex];
|
||||
index = this.active[i]._index;
|
||||
|
||||
this.active[i].radius = dataset.pointHoverRadius || dataset.pointRadius + 2;
|
||||
this.active[i].backgroundColor = dataset.pointHoverBackgroundColor || helpers.color(dataset.pointBackgroundColor).saturate(0.5).darken(0.35).rgbString();
|
||||
this.active[i].borderColor = dataset.pointHoverBorderColor || helpers.color(dataset.pointBorderColor).saturate(0.5).darken(0.35).rgbString();
|
||||
this.active[i].borderWidth = dataset.pointHoverBorderWidth || dataset.pointBorderWidth + 2;
|
||||
this.active[i]._model.radius = this.active[i].custom && this.active[i].custom.hoverRadius ? this.active[i].custom.hoverRadius : helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.active[i]._model.radius + 2);
|
||||
this.active[i]._model.backgroundColor = this.active[i].custom && this.active[i].custom.hoverBackgroundColor ? this.active[i].custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.color(this.active[i]._model.backgroundColor).saturate(0.5).darken(0.35).rgbString());
|
||||
this.active[i]._model.borderColor = this.active[i].custom && this.active[i].custom.hoverBorderColor ? this.active[i].custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.color(this.active[i]._model.borderColor).saturate(0.5).darken(0.35).rgbString());
|
||||
this.active[i]._model.borderWidth = this.active[i].custom && this.active[i].custom.hoverBorderWidth ? this.active[i].custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, this.active[i]._model.borderWidth + 2);
|
||||
}
|
||||
break;
|
||||
case 'dataset':
|
||||
@ -496,7 +455,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Built in Tooltips
|
||||
if (this.options.tooltips.enabled) {
|
||||
|
||||
@ -505,21 +463,19 @@
|
||||
|
||||
// Active
|
||||
if (this.active.length) {
|
||||
this.tooltip._model.opacity = 1;
|
||||
|
||||
helpers.extend(this.tooltip, {
|
||||
opacity: 1,
|
||||
_active: this.active,
|
||||
});
|
||||
|
||||
this.tooltip.update();
|
||||
} else {
|
||||
// Inactive
|
||||
helpers.extend(this.tooltip, {
|
||||
opacity: 0,
|
||||
});
|
||||
this.tooltip._model.opacity = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Hover animations
|
||||
this.tooltip.pivot();
|
||||
|
||||
@ -538,7 +494,7 @@
|
||||
(this.lastActive.length && this.active.length && changed)) {
|
||||
|
||||
this.stop();
|
||||
this.render(this.options.hoverAnimationDuration);
|
||||
this.render(this.options.hover.animationDuration);
|
||||
}
|
||||
}
|
||||
|
||||
@ -546,11 +502,5 @@
|
||||
this.lastActive = this.active;
|
||||
return this;
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}).call(this);
|
||||
|
||||
@ -77,27 +77,12 @@
|
||||
}],
|
||||
},
|
||||
|
||||
//Number - Tension of the bezier curve between points
|
||||
tension: 0.4,
|
||||
|
||||
//Number - Radius of each point dot in pixels
|
||||
pointRadius: 4,
|
||||
|
||||
//Number - Pixel width of point dot border
|
||||
pointBorderWidth: 1,
|
||||
|
||||
//Number - amount extra to add to the radius to cater for hit detection outside the drawn point
|
||||
pointHoverRadius: 20,
|
||||
|
||||
//Number - Pixel width of dataset border
|
||||
borderWidth: 2,
|
||||
|
||||
//String - A legend template
|
||||
legendTemplate: "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<datasets.length; i++){%><li><span style=\"background-color:<%=datasets[i].borderColor%>\"></span><%if(datasets[i].label){%><%=datasets[i].label%><%}%></li><%}%></ul>",
|
||||
|
||||
tooltips: {
|
||||
template: "(<%= dataX %>, <%= dataY %>)",
|
||||
multiTemplate: "<%if (datasetLabel){%><%=datasetLabel%>: <%}%>(<%= dataX %>, <%= dataY %>)",
|
||||
template: "(<%= value.x %>, <%= value.y %>)",
|
||||
multiTemplate: "<%if (datasetLabel){%><%=datasetLabel%>: <%}%>(<%= value.x %>, <%= value.y %>)",
|
||||
},
|
||||
|
||||
};
|
||||
@ -107,31 +92,34 @@
|
||||
name: "Scatter",
|
||||
defaults: defaultConfig,
|
||||
initialize: function() {
|
||||
//Custom Point Defaults
|
||||
this.PointClass = Chart.Point.extend({
|
||||
_chart: this.chart,
|
||||
offsetGridLines: this.options.offsetGridLines,
|
||||
borderWidth: this.options.pointBorderWidth,
|
||||
radius: this.options.pointRadius,
|
||||
hoverRadius: this.options.pointHoverRadius,
|
||||
});
|
||||
|
||||
// Events
|
||||
helpers.bindEvents(this, this.options.events, this.events);
|
||||
|
||||
// Build Scale
|
||||
this.buildScale();
|
||||
Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height);
|
||||
|
||||
//Create a new line and its points for each dataset and piece of data
|
||||
//Custom Point Defaults
|
||||
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
|
||||
dataset.metaDataset = new Chart.Line();
|
||||
dataset.metaDataset = new Chart.Line({
|
||||
_chart: this.chart,
|
||||
_datasetIndex: datasetIndex,
|
||||
_points: dataset.metaData,
|
||||
});
|
||||
|
||||
dataset.metaData = [];
|
||||
|
||||
helpers.each(dataset.data, function(dataPoint, index) {
|
||||
dataset.metaData.push(new this.PointClass());
|
||||
dataset.metaData.push(new Chart.Point({
|
||||
_datasetIndex: datasetIndex,
|
||||
_index: index,
|
||||
_chart: this.chart,
|
||||
_model: {
|
||||
x: 0, //xScale.getPixelForValue(null, index, true),
|
||||
y: 0, //this.chartArea.bottom,
|
||||
},
|
||||
}));
|
||||
|
||||
}, this);
|
||||
|
||||
// Make sure each dataset is bound to an x and a y axis
|
||||
// The line chart onlty supports a single x axis because the x axis is always a dataset axis
|
||||
if (!dataset.xAxisID) {
|
||||
dataset.xAxisID = this.options.scales.xAxes[0].id;
|
||||
}
|
||||
@ -139,42 +127,11 @@
|
||||
if (!dataset.yAxisID) {
|
||||
dataset.yAxisID = this.options.scales.yAxes[0].id;
|
||||
}
|
||||
|
||||
}, this);
|
||||
|
||||
// Set defaults for lines
|
||||
this.eachDataset(function(dataset, datasetIndex) {
|
||||
dataset = helpers.merge(this.options, dataset);
|
||||
helpers.extend(dataset.metaDataset, {
|
||||
_points: dataset.metaData,
|
||||
_datasetIndex: datasetIndex,
|
||||
_chart: this.chart,
|
||||
});
|
||||
// Copy to view model
|
||||
dataset.metaDataset.save();
|
||||
}, this);
|
||||
|
||||
// Set defaults for points
|
||||
this.eachElement(function(point, index, dataset, datasetIndex) {
|
||||
var xScale = this.scales[this.data.datasets[datasetIndex].xAxisID];
|
||||
|
||||
helpers.extend(point, {
|
||||
x: xScale.getPixelForValue(index),
|
||||
y: this.chartArea.bottom,
|
||||
_datasetIndex: datasetIndex,
|
||||
_index: index,
|
||||
_chart: this.chart
|
||||
});
|
||||
|
||||
// Default bezier control points
|
||||
helpers.extend(point, {
|
||||
controlPointPreviousX: this.previousPoint(dataset, index).x,
|
||||
controlPointPreviousY: this.nextPoint(dataset, index).y,
|
||||
controlPointNextX: this.previousPoint(dataset, index).x,
|
||||
controlPointNextY: this.nextPoint(dataset, index).y,
|
||||
});
|
||||
// Copy to view model
|
||||
point.save();
|
||||
}, this);
|
||||
// Build and fit the scale. Needs to happen after the axis IDs have been set
|
||||
this.buildScale();
|
||||
|
||||
// Create tooltip instance exclusively for this chart with some defaults.
|
||||
this.tooltip = new Chart.Tooltip({
|
||||
@ -183,149 +140,131 @@
|
||||
_options: this.options,
|
||||
}, this);
|
||||
|
||||
// Need to fit scales before we reset elements.
|
||||
Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height);
|
||||
|
||||
// Reset so that we animation from the baseline
|
||||
this.resetElements();
|
||||
|
||||
// Update that shiz
|
||||
this.update();
|
||||
},
|
||||
nextPoint: function(collection, index) {
|
||||
return collection[index - 1] || collection[index];
|
||||
},
|
||||
previousPoint: function(collection, index) {
|
||||
return collection[index + 1] || collection[index];
|
||||
},
|
||||
events: function(e) {
|
||||
// If exiting chart
|
||||
if (e.type == 'mouseout') {
|
||||
return this;
|
||||
}
|
||||
previousPoint: function(collection, index) {
|
||||
return collection[index - 1] || collection[index];
|
||||
},
|
||||
resetElements: function() {
|
||||
// Update the points
|
||||
this.eachElement(function(point, index, dataset, datasetIndex) {
|
||||
var xScale = this.scales[this.data.datasets[datasetIndex].xAxisID];
|
||||
var yScale = this.scales[this.data.datasets[datasetIndex].yAxisID];
|
||||
|
||||
this.lastActive = this.lastActive || [];
|
||||
var yScalePoint;
|
||||
|
||||
// Find Active Elements
|
||||
this.active = function() {
|
||||
switch (this.options.hover.mode) {
|
||||
case 'single':
|
||||
return this.getElementAtEvent(e);
|
||||
case 'label':
|
||||
return this.getElementsAtEvent(e);
|
||||
case 'dataset':
|
||||
return this.getDatasetAtEvent(e);
|
||||
default:
|
||||
return e;
|
||||
}
|
||||
}.call(this);
|
||||
|
||||
// On Hover hook
|
||||
if (this.options.onHover) {
|
||||
this.options.onHover.call(this, this.active);
|
||||
}
|
||||
|
||||
// Remove styling for last active (even if it may still be active)
|
||||
if (this.lastActive.length) {
|
||||
switch (this.options.hover.mode) {
|
||||
case 'single':
|
||||
this.lastActive[0].backgroundColor = this.data.datasets[this.lastActive[0]._datasetIndex].pointBackgroundColor;
|
||||
this.lastActive[0].borderColor = this.data.datasets[this.lastActive[0]._datasetIndex].pointBorderColor;
|
||||
this.lastActive[0].borderWidth = this.data.datasets[this.lastActive[0]._datasetIndex].pointBorderWidth;
|
||||
break;
|
||||
case 'label':
|
||||
for (var i = 0; i < this.lastActive.length; i++) {
|
||||
this.lastActive[i].backgroundColor = this.data.datasets[this.lastActive[i]._datasetIndex].pointBackgroundColor;
|
||||
this.lastActive[i].borderColor = this.data.datasets[this.lastActive[i]._datasetIndex].pointBorderColor;
|
||||
this.lastActive[i].borderWidth = this.data.datasets[this.lastActive[0]._datasetIndex].pointBorderWidth;
|
||||
}
|
||||
break;
|
||||
case 'dataset':
|
||||
break;
|
||||
default:
|
||||
// Don't change anything
|
||||
}
|
||||
}
|
||||
|
||||
// Built in hover styling
|
||||
if (this.active.length && this.options.hover.mode) {
|
||||
switch (this.options.hover.mode) {
|
||||
case 'single':
|
||||
this.active[0].backgroundColor = this.data.datasets[this.active[0]._datasetIndex].hoverBackgroundColor || helpers.color(this.active[0].backgroundColor).saturate(0.5).darken(0.35).rgbString();
|
||||
this.active[0].borderColor = this.data.datasets[this.active[0]._datasetIndex].hoverBorderColor || helpers.color(this.active[0].borderColor).saturate(0.5).darken(0.35).rgbString();
|
||||
this.active[0].borderWidth = this.data.datasets[this.active[0]._datasetIndex].borderWidth + 10;
|
||||
break;
|
||||
case 'label':
|
||||
for (var i = 0; i < this.active.length; i++) {
|
||||
this.active[i].backgroundColor = this.data.datasets[this.active[i]._datasetIndex].hoverBackgroundColor || helpers.color(this.active[i].backgroundColor).saturate(0.5).darken(0.35).rgbString();
|
||||
this.active[i].borderColor = this.data.datasets[this.active[i]._datasetIndex].hoverBorderColor || helpers.color(this.active[i].borderColor).saturate(0.5).darken(0.35).rgbString();
|
||||
this.active[i].borderWidth = this.data.datasets[this.active[i]._datasetIndex].borderWidth + 2;
|
||||
}
|
||||
break;
|
||||
case 'dataset':
|
||||
break;
|
||||
default:
|
||||
// Don't change anything
|
||||
}
|
||||
}
|
||||
|
||||
// Built in Tooltips
|
||||
if (this.options.tooltips.enabled) {
|
||||
|
||||
// The usual updates
|
||||
this.tooltip.initialize();
|
||||
|
||||
// Active
|
||||
if (this.active.length) {
|
||||
helpers.extend(this.tooltip, {
|
||||
opacity: 1,
|
||||
_active: this.active,
|
||||
});
|
||||
|
||||
this.tooltip.update();
|
||||
if (yScale.min < 0 && yScale.max < 0) {
|
||||
// all less than 0. use the top
|
||||
yScalePoint = yScale.getPixelForValue(yScale.max);
|
||||
} else if (yScale.min > 0 && yScale.max > 0) {
|
||||
yScalePoint = yScale.getPixelForValue(yScale.min);
|
||||
} else {
|
||||
// Inactive
|
||||
helpers.extend(this.tooltip, {
|
||||
opacity: 0,
|
||||
});
|
||||
yScalePoint = yScale.getPixelForValue(0);
|
||||
}
|
||||
}
|
||||
|
||||
// Hover animations
|
||||
this.tooltip.pivot();
|
||||
helpers.extend(point, {
|
||||
// Utility
|
||||
_chart: this.chart,
|
||||
_xScale: xScale,
|
||||
_yScale: yScale,
|
||||
_datasetIndex: datasetIndex,
|
||||
_index: index,
|
||||
|
||||
if (!this.animating) {
|
||||
var changed;
|
||||
// Desired view properties
|
||||
_model: {
|
||||
x: xScale.getPixelForValue(this.data.datasets[datasetIndex].data[index].x), // value not used in dataset scale, but we want a consistent API between scales
|
||||
y: yScalePoint,
|
||||
|
||||
helpers.each(this.active, function(element, index) {
|
||||
if (element !== this.lastActive[index]) {
|
||||
changed = true;
|
||||
}
|
||||
}, this);
|
||||
// Appearance
|
||||
tension: point.custom && point.custom.tension ? point.custom.tension : this.options.elements.line.tension,
|
||||
radius: point.custom && point.custom.radius ? point.custom.pointRadius : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointRadius, index, this.options.elements.point.radius),
|
||||
backgroundColor: point.custom && point.custom.backgroundColor ? point.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointBackgroundColor, index, this.options.elements.point.backgroundColor),
|
||||
borderColor: point.custom && point.custom.borderColor ? point.custom.borderColor : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointBorderColor, index, this.options.elements.point.borderColor),
|
||||
borderWidth: point.custom && point.custom.borderWidth ? point.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointBorderWidth, index, this.options.elements.point.borderWidth),
|
||||
skip: (typeof this.data.datasets[datasetIndex].data[index].x != 'number') || (typeof this.data.datasets[datasetIndex].data[index].y != 'number'),
|
||||
|
||||
// If entering, leaving, or changing elements, animate the change via pivot
|
||||
if ((!this.lastActive.length && this.active.length) ||
|
||||
(this.lastActive.length && !this.active.length) ||
|
||||
(this.lastActive.length && this.active.length && changed)) {
|
||||
// Tooltip
|
||||
hoverRadius: point.custom && point.custom.hoverRadius ? point.custom.hoverRadius : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointHitRadius, index, this.options.elements.point.hitRadius),
|
||||
},
|
||||
});
|
||||
}, this);
|
||||
|
||||
this.stop();
|
||||
this.render(this.options.hoverAnimationDuration);
|
||||
// Update control points for the bezier curve
|
||||
this.eachElement(function(point, index, dataset, datasetIndex) {
|
||||
var controlPoints = helpers.splineCurve(
|
||||
this.previousPoint(dataset, index)._model,
|
||||
point._model,
|
||||
this.nextPoint(dataset, index)._model,
|
||||
point._model.tension
|
||||
);
|
||||
|
||||
point._model.controlPointPreviousX = controlPoints.previous.x;
|
||||
point._model.controlPointNextX = controlPoints.next.x;
|
||||
|
||||
// Prevent the bezier going outside of the bounds of the graph
|
||||
|
||||
// Cap puter bezier handles to the upper/lower scale bounds
|
||||
if (controlPoints.next.y > this.chartArea.bottom) {
|
||||
point._model.controlPointNextY = this.chartArea.bottom;
|
||||
} else if (controlPoints.next.y < this.chartArea.top) {
|
||||
point._model.controlPointNextY = this.chartArea.top;
|
||||
} else {
|
||||
point._model.controlPointNextY = controlPoints.next.y;
|
||||
}
|
||||
}
|
||||
|
||||
// Remember Last Active
|
||||
this.lastActive = this.active;
|
||||
return this;
|
||||
// Cap inner bezier handles to the upper/lower scale bounds
|
||||
if (controlPoints.previous.y > this.chartArea.bottom) {
|
||||
point._model.controlPointPreviousY = this.chartArea.bottom;
|
||||
} else if (controlPoints.previous.y < this.chartArea.top) {
|
||||
point._model.controlPointPreviousY = this.chartArea.top;
|
||||
} else {
|
||||
point._model.controlPointPreviousY = controlPoints.previous.y;
|
||||
}
|
||||
|
||||
// Now pivot the point for animation
|
||||
point.pivot();
|
||||
}, this);
|
||||
},
|
||||
update: function() {
|
||||
Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height);
|
||||
Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height);
|
||||
|
||||
// Update the lines
|
||||
this.eachDataset(function(dataset, datasetIndex) {
|
||||
var yScale = this.scales[dataset.yAxisID];
|
||||
|
||||
helpers.extend(dataset.metaDataset, {
|
||||
backgroundColor: dataset.backgroundColor || this.options.backgroundColor,
|
||||
borderWidth: dataset.borderWidth || this.options.borderWidth,
|
||||
borderColor: dataset.borderColor || this.options.borderColor,
|
||||
tension: dataset.tension || this.options.tension,
|
||||
scaleTop: this.chartArea.top,
|
||||
scaleBottom: this.chartArea.bottom,
|
||||
_points: dataset.metaData,
|
||||
// Utility
|
||||
_scale: yScale,
|
||||
_datasetIndex: datasetIndex,
|
||||
// Data
|
||||
_children: dataset.metaData,
|
||||
// Model
|
||||
_model: {
|
||||
// Appearance
|
||||
tension: dataset.tension || this.options.elements.line.tension,
|
||||
backgroundColor: dataset.backgroundColor || this.options.elements.line.backgroundColor,
|
||||
borderWidth: dataset.borderWidth || this.options.elements.line.borderWidth,
|
||||
borderColor: dataset.borderColor || this.options.elements.line.borderColor,
|
||||
fill: dataset.fill !== undefined ? dataset.fill : this.options.elements.line.fill, // use the value from the dataset if it was provided. else fall back to the default
|
||||
skipNull: dataset.skipNull !== undefined ? dataset.skipNull : this.options.elements.line.skipNull,
|
||||
drawNull: dataset.drawNull !== undefined ? dataset.drawNull : this.options.elements.line.drawNull,
|
||||
// Scale
|
||||
scaleTop: yScale.top,
|
||||
scaleBottom: yScale.bottom,
|
||||
scaleZero: yScale.getPixelForValue(0),
|
||||
},
|
||||
});
|
||||
|
||||
dataset.metaDataset.pivot();
|
||||
});
|
||||
|
||||
@ -335,58 +274,65 @@
|
||||
var yScale = this.scales[this.data.datasets[datasetIndex].yAxisID];
|
||||
|
||||
helpers.extend(point, {
|
||||
x: xScale.getPixelForValue(this.data.datasets[datasetIndex].data[index].x),
|
||||
y: yScale.getPixelForValue(this.data.datasets[datasetIndex].data[index].y),
|
||||
dataX: this.data.datasets[datasetIndex].data[index].x,
|
||||
dataY: this.data.datasets[datasetIndex].data[index].y,
|
||||
label: '', // so that the multitooltip looks ok
|
||||
value: this.data.datasets[datasetIndex].data[index].y, // for legacy reasons
|
||||
datasetLabel: this.data.datasets[datasetIndex].label,
|
||||
// Appearance
|
||||
hoverBackgroundColor: this.data.datasets[datasetIndex].pointHoverBackgroundColor || this.options.pointHoverBackgroundColor,
|
||||
hoverBorderColor: this.data.datasets[datasetIndex].pointHoverBorderColor || this.options.pointHoverBorderColor,
|
||||
hoverRadius: this.data.datasets[datasetIndex].pointHoverRadius || this.options.pointHoverRadius,
|
||||
radius: this.data.datasets[datasetIndex].pointRadius || this.options.pointRadius,
|
||||
borderWidth: this.data.datasets[datasetIndex].pointBorderWidth || this.options.pointBorderWidth,
|
||||
borderColor: this.data.datasets[datasetIndex].pointBorderColor || this.options.pointBorderColor,
|
||||
backgroundColor: this.data.datasets[datasetIndex].pointBackgroundColor || this.options.pointBackgroundColor,
|
||||
tension: this.data.datasets[datasetIndex].metaDataset.tension,
|
||||
// Utility
|
||||
_chart: this.chart,
|
||||
_xScale: xScale,
|
||||
_yScale: yScale,
|
||||
_datasetIndex: datasetIndex,
|
||||
_index: index,
|
||||
|
||||
// Desired view properties
|
||||
_model: {
|
||||
x: xScale.getPixelForValue(this.data.datasets[datasetIndex].data[index].x),
|
||||
y: yScale.getPixelForValue(this.data.datasets[datasetIndex].data[index].y),
|
||||
|
||||
// Appearance
|
||||
tension: point.custom && point.custom.tension ? point.custom.tension : this.options.elements.line.tension,
|
||||
radius: point.custom && point.custom.radius ? point.custom.pointRadius : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointRadius, index, this.options.elements.point.radius),
|
||||
backgroundColor: point.custom && point.custom.backgroundColor ? point.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointBackgroundColor, index, this.options.elements.point.backgroundColor),
|
||||
borderColor: point.custom && point.custom.borderColor ? point.custom.borderColor : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointBorderColor, index, this.options.elements.point.borderColor),
|
||||
borderWidth: point.custom && point.custom.borderWidth ? point.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointBorderWidth, index, this.options.elements.point.borderWidth),
|
||||
skip: (typeof this.data.datasets[datasetIndex].data[index].x != 'number') || (typeof this.data.datasets[datasetIndex].data[index].y != 'number'),
|
||||
|
||||
// Tooltip
|
||||
hoverRadius: point.custom && point.custom.hoverRadius ? point.custom.hoverRadius : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointHitRadius, index, this.options.elements.point.hitRadius),
|
||||
},
|
||||
});
|
||||
}, this);
|
||||
|
||||
|
||||
// Update control points for the bezier curve
|
||||
this.eachElement(function(point, index, dataset, datasetIndex) {
|
||||
var controlPoints = helpers.splineCurve(
|
||||
this.previousPoint(dataset, index),
|
||||
point,
|
||||
this.nextPoint(dataset, index),
|
||||
point.tension
|
||||
this.previousPoint(dataset, index)._model,
|
||||
point._model,
|
||||
this.nextPoint(dataset, index)._model,
|
||||
point._model.tension
|
||||
);
|
||||
|
||||
point.controlPointPreviousX = controlPoints.previous.x;
|
||||
point.controlPointNextX = controlPoints.next.x;
|
||||
point._model.controlPointPreviousX = controlPoints.previous.x;
|
||||
point._model.controlPointNextX = controlPoints.next.x;
|
||||
|
||||
// Prevent the bezier going outside of the bounds of the graph
|
||||
|
||||
// Cap puter bezier handles to the upper/lower scale bounds
|
||||
if (controlPoints.next.y > this.chartArea.bottom) {
|
||||
point.controlPointNextY = this.chartArea.bottom;
|
||||
point._model.controlPointNextY = this.chartArea.bottom;
|
||||
} else if (controlPoints.next.y < this.chartArea.top) {
|
||||
point.controlPointNextY = this.chartArea.top;
|
||||
point._model.controlPointNextY = this.chartArea.top;
|
||||
} else {
|
||||
point.controlPointNextY = controlPoints.next.y;
|
||||
point._model.controlPointNextY = controlPoints.next.y;
|
||||
}
|
||||
|
||||
// Cap inner bezier handles to the upper/lower scale bounds
|
||||
if (controlPoints.previous.y > this.chartArea.bottom) {
|
||||
point.controlPointPreviousY = this.chartArea.bottom;
|
||||
point._model.controlPointPreviousY = this.chartArea.bottom;
|
||||
} else if (controlPoints.previous.y < this.chartArea.top) {
|
||||
point.controlPointPreviousY = this.chartArea.top;
|
||||
point._model.controlPointPreviousY = this.chartArea.top;
|
||||
} else {
|
||||
point.controlPointPreviousY = controlPoints.previous.y;
|
||||
point._model.controlPointPreviousY = controlPoints.previous.y;
|
||||
}
|
||||
|
||||
// Now pivot the point for animation
|
||||
point.pivot();
|
||||
}, this);
|
||||
@ -465,25 +411,28 @@
|
||||
options: yAxisOptions,
|
||||
calculateRange: calculateYRange,
|
||||
id: yAxisOptions.id,
|
||||
getPointPixelForValue: function(value, index, datasetIndex) {
|
||||
return this.getPixelForValue(value);
|
||||
}
|
||||
});
|
||||
|
||||
this.scales[scale.id] = scale;
|
||||
}, this);
|
||||
},
|
||||
redraw: function() {
|
||||
|
||||
},
|
||||
draw: function(ease) {
|
||||
|
||||
var easingDecimal = ease || 1;
|
||||
this.clear();
|
||||
|
||||
// Draw all the scales
|
||||
helpers.each(this.scales, function(scale) {
|
||||
scale.draw(this.chartArea);
|
||||
}, this);
|
||||
|
||||
this.eachDataset(function(dataset, datasetIndex) {
|
||||
// Draw all the scales
|
||||
helpers.each(this.scales, function(scale) {
|
||||
scale.draw(this.chartArea);
|
||||
}, this);
|
||||
|
||||
// reverse for-loop for proper stacking
|
||||
for (var i = this.data.datasets.length - 1; i >= 0; i--) {
|
||||
|
||||
var dataset = this.data.datasets[i];
|
||||
|
||||
// Transition Point Locations
|
||||
helpers.each(dataset.metaData, function(point, index) {
|
||||
point.transition(easingDecimal);
|
||||
@ -496,11 +445,148 @@
|
||||
helpers.each(dataset.metaData, function(point) {
|
||||
point.draw();
|
||||
});
|
||||
}, this);
|
||||
}
|
||||
|
||||
// Finally draw the tooltip
|
||||
this.tooltip.transition(easingDecimal).draw();
|
||||
}
|
||||
},
|
||||
events: function(e) {
|
||||
// If exiting chart
|
||||
if (e.type == 'mouseout') {
|
||||
return this;
|
||||
}
|
||||
|
||||
this.lastActive = this.lastActive || [];
|
||||
|
||||
// Find Active Elements
|
||||
this.active = function() {
|
||||
switch (this.options.hover.mode) {
|
||||
case 'single':
|
||||
return this.getElementAtEvent(e);
|
||||
case 'label':
|
||||
return this.getElementsAtEvent(e);
|
||||
case 'dataset':
|
||||
return this.getDatasetAtEvent(e);
|
||||
default:
|
||||
return e;
|
||||
}
|
||||
}.call(this);
|
||||
|
||||
// On Hover hook
|
||||
if (this.options.hover.onHover) {
|
||||
this.options.hover.onHover.call(this, this.active);
|
||||
}
|
||||
|
||||
var dataset;
|
||||
var index;
|
||||
// Remove styling for last active (even if it may still be active)
|
||||
if (this.lastActive.length) {
|
||||
switch (this.options.hover.mode) {
|
||||
case 'single':
|
||||
dataset = this.data.datasets[this.lastActive[0]._datasetIndex];
|
||||
index = this.lastActive[0]._index;
|
||||
|
||||
this.lastActive[0]._model.radius = this.lastActive[0].custom && this.lastActive[0].custom.radius ? this.lastActive[0].custom.pointRadius : helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, this.options.elements.point.radius);
|
||||
this.lastActive[0]._model.backgroundColor = this.lastActive[0].custom && this.lastActive[0].custom.backgroundColor ? this.lastActive[0].custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointBackgroundColor, index, this.options.elements.point.backgroundColor);
|
||||
this.lastActive[0]._model.borderColor = this.lastActive[0].custom && this.lastActive[0].custom.borderColor ? this.lastActive[0].custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.pointBorderColor, index, this.options.elements.point.borderColor);
|
||||
this.lastActive[0]._model.borderWidth = this.lastActive[0].custom && this.lastActive[0].custom.borderWidth ? this.lastActive[0].custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, this.options.elements.point.borderWidth);
|
||||
break;
|
||||
case 'label':
|
||||
for (var i = 0; i < this.lastActive.length; i++) {
|
||||
dataset = this.data.datasets[this.lastActive[i]._datasetIndex];
|
||||
index = this.lastActive[i]._index;
|
||||
|
||||
this.lastActive[i]._model.radius = this.lastActive[i].custom && this.lastActive[i].custom.radius ? this.lastActive[i].custom.pointRadius : helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, this.options.elements.point.radius);
|
||||
this.lastActive[i]._model.backgroundColor = this.lastActive[i].custom && this.lastActive[i].custom.backgroundColor ? this.lastActive[i].custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointBackgroundColor, index, this.options.elements.point.backgroundColor);
|
||||
this.lastActive[i]._model.borderColor = this.lastActive[i].custom && this.lastActive[i].custom.borderColor ? this.lastActive[i].custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.pointBorderColor, index, this.options.elements.point.borderColor);
|
||||
this.lastActive[i]._model.borderWidth = this.lastActive[i].custom && this.lastActive[i].custom.borderWidth ? this.lastActive[i].custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, this.options.elements.point.borderWidth);
|
||||
}
|
||||
break;
|
||||
case 'dataset':
|
||||
break;
|
||||
default:
|
||||
// Don't change anything
|
||||
}
|
||||
}
|
||||
|
||||
// Built in hover styling
|
||||
if (this.active.length && this.options.hover.mode) {
|
||||
switch (this.options.hover.mode) {
|
||||
case 'single':
|
||||
dataset = this.data.datasets[this.active[0]._datasetIndex];
|
||||
index = this.active[0]._index;
|
||||
|
||||
this.active[0]._model.radius = this.active[0].custom && this.active[0].custom.hoverRadius ? this.active[0].custom.hoverRadius : helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.active[0]._model.radius + 2);
|
||||
this.active[0]._model.backgroundColor = this.active[0].custom && this.active[0].custom.hoverBackgroundColor ? this.active[0].custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.color(this.active[0]._model.backgroundColor).saturate(0.5).darken(0.35).rgbString());
|
||||
this.active[0]._model.borderColor = this.active[0].custom && this.active[0].custom.hoverBorderColor ? this.active[0].custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.color(this.active[0]._model.borderColor).saturate(0.5).darken(0.35).rgbString());
|
||||
this.active[0]._model.borderWidth = this.active[0].custom && this.active[0].custom.hoverBorderWidth ? this.active[0].custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, this.active[0]._model.borderWidth + 2);
|
||||
break;
|
||||
case 'label':
|
||||
for (var i = 0; i < this.active.length; i++) {
|
||||
dataset = this.data.datasets[this.active[i]._datasetIndex];
|
||||
index = this.active[i]._index;
|
||||
|
||||
this.active[i]._model.radius = this.active[i].custom && this.active[i].custom.hoverRadius ? this.active[i].custom.hoverRadius : helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.active[i]._model.radius + 2);
|
||||
this.active[i]._model.backgroundColor = this.active[i].custom && this.active[i].custom.hoverBackgroundColor ? this.active[i].custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.color(this.active[i]._model.backgroundColor).saturate(0.5).darken(0.35).rgbString());
|
||||
this.active[i]._model.borderColor = this.active[i].custom && this.active[i].custom.hoverBorderColor ? this.active[i].custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.color(this.active[i]._model.borderColor).saturate(0.5).darken(0.35).rgbString());
|
||||
this.active[i]._model.borderWidth = this.active[i].custom && this.active[i].custom.hoverBorderWidth ? this.active[i].custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, this.active[i]._model.borderWidth + 2);
|
||||
}
|
||||
break;
|
||||
case 'dataset':
|
||||
break;
|
||||
default:
|
||||
// Don't change anything
|
||||
}
|
||||
}
|
||||
|
||||
// Built in Tooltips
|
||||
if (this.options.tooltips.enabled) {
|
||||
|
||||
// The usual updates
|
||||
this.tooltip.initialize();
|
||||
|
||||
// Active
|
||||
if (this.active.length) {
|
||||
this.tooltip._model.opacity = 1;
|
||||
|
||||
helpers.extend(this.tooltip, {
|
||||
_active: this.active,
|
||||
});
|
||||
|
||||
this.tooltip.update();
|
||||
} else {
|
||||
// Inactive
|
||||
this.tooltip._model.opacity = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Hover animations
|
||||
this.tooltip.pivot();
|
||||
|
||||
if (!this.animating) {
|
||||
var changed;
|
||||
|
||||
helpers.each(this.active, function(element, index) {
|
||||
if (element !== this.lastActive[index]) {
|
||||
changed = true;
|
||||
}
|
||||
}, this);
|
||||
|
||||
// If entering, leaving, or changing elements, animate the change via pivot
|
||||
if ((!this.lastActive.length && this.active.length) ||
|
||||
(this.lastActive.length && !this.active.length) ||
|
||||
(this.lastActive.length && this.active.length && changed)) {
|
||||
|
||||
this.stop();
|
||||
this.render(this.options.hoverAnimationDuration);
|
||||
}
|
||||
}
|
||||
|
||||
// Remember Last Active
|
||||
this.lastActive = this.active;
|
||||
return this;
|
||||
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user