Skip to content

Commit

Permalink
Initial upload
Browse files Browse the repository at this point in the history
  • Loading branch information
Sean committed Feb 9, 2017
1 parent 05715fe commit 95a6e36
Show file tree
Hide file tree
Showing 8 changed files with 700 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,8 @@ jspm_packages

# Optional REPL history
.node_repl_history

.idea

/chartjs-plugin-downsample.js
/chartjs-plugin-downsample.min.js
60 changes: 60 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# chartjs-plugin-downsample

Plugin for downsampling data in Chart.js, based off of [sveinn-steinarsson/flot-downsample](https://github.com/sveinn-steinarsson/flot-downsample).

## Configuration

The configuration for this plugin lives in chartInstance.options.downsample. This looks like the following while setting up your chart:

```
{
options: {
downsample: {
enabled: true,
threshold: 500 // max number of points to display per dataset
}
}
}
```

### Additional Options

| Option | Default | Description |
| --------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| auto | true | If true, downsamples data automatically every update. Otherwise, chart will have to be manually downsampled with `.downsample()` |
| onInit | true | If true, downsamples data when the chart is initialized. |
| restoreOriginalData | true | If true, replaces the downsampled data with the original data after each update. This is, mainly, for compatibility with other plugins. |
| preferOriginalData | false | If true, downsamples original data instead of data. This option can clash with dynamically-added data. If false, data cannot be "un-downscaled". |

## Methods

| Method | Description |
|------------------------------------------------ |------------------------------------------------------------------------------------------------------------------------------------------- |
| chartInstance.downsample(*var threshold = null*) | Manually downsamples the data on the given chart. If a threshold is passed, updates the threshold in the chart config to the given value. |

## Optimal Performance

This plugin was created because of performance issues while loading lots of data in a chart with [pan/zoom capabilites](https://github.com/chartjs/chartjs-plugin-zoom/).

If options are not changed from their defaults, data will be downsampled every time the user pans or zooms - this is probably not what you want. For a more performant configuration, try this:

```
{
options: {
downsample: {
enabled: true,
threshold: <YOUR THRESHOLD HERE>, // change this
auto: false, // don't re-downsample the data every move
onInit: true, // but do resample it when we init the chart (this is default)
preferOriginalData: true, // use our original data when downscaling so we can downscale less, if we need to.
restoreOriginalData: false, // if auto is false and this is true, original data will be restored on pan/zoom - that isn't what we want.
}
}
}
```

## License

chartjs-plugin-downsample is released under the terms of [the MIT License](http://www.opensource.org/licenses/MIT).
31 changes: 31 additions & 0 deletions gulpfile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
var gulp = require('gulp');
var browserify = require('browserify');
var source = require('vinyl-source-stream');
var merge = require('merge-stream');
var streamify = require('gulp-streamify');
var uglify = require('gulp-uglify');
var insert = require('gulp-insert');
var concat = require('gulp-concat');
var buffer = require('vinyl-buffer');

var header = "/* chartjs-plugin-downsample | AlbinoDrought | MIT License | https://github.com/AlbinoDrought/chartjs-plugin-downsample/blob/master/LICENSE | Based on flot-downsample, https://github.com/sveinn-steinarsson/flot-downsample/ */\n";
var outDir = "./";

gulp.task('build', buildTask);

function buildTask() {
var build = browserify('./src/chartjs-plugin-downsample.js')
.ignore('chart.js')
.bundle()
.pipe(source('chartjs-plugin-downsample.js'))
.pipe(buffer())
.pipe(insert.prepend(header))
.pipe(gulp.dest(outDir))
// min build
.pipe(streamify(uglify()))
.pipe(insert.prepend(header))
.pipe(streamify(concat('chartjs-plugin-downsample.min.js')))
.pipe(gulp.dest(outDir));

return build;
}
26 changes: 26 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "chartjs-plugin-downsample",
"version": "1.0.2",
"description": "Chart.js plugin to downsample chart data",
"main": "src/chartjs-plugin-downsample.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "AlbinoDrought",
"license": "MIT",
"dependencies": {
"chart.js": "^2.5.0"
},
"devDependencies": {
"browserify": "^14.0.0",
"chartjs-plugin-zoom": "^0.4.5",
"gulp": "^3.9.1",
"gulp-concat": "^2.6.1",
"gulp-insert": "^0.5.0",
"gulp-streamify": "^1.0.2",
"gulp-uglify": "^2.0.1",
"merge-stream": "^1.0.1",
"vinyl-buffer": "^1.0.0",
"vinyl-source-stream": "^1.1.0"
}
}
115 changes: 115 additions & 0 deletions samples/data.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
<!doctype html>
<html>

<head>
<title>Scatter Chart</title>
<script src="../node_modules/chart.js/dist/Chart.bundle.js"></script>
<script src="../chartjs-plugin-downsample.js"></script>
<style>
canvas {
-moz-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
}
</style>
</head>

<body>
<div style="width:75%">
<div>
<canvas id="canvas"></canvas>
<div>
<label for="threshold">Threshold: </label>
<input type="range" id="threshold" min="1" max="0" value="0" step="1" style="width: 100%" />
<span id="thresholdCurrent">0</span>
</div>
</div>
</div>
<script>
var scalingFactor = function(value) {
return (value * 0.9) + (Math.random() > 0.5 ? 1.0 : -1.0) * Math.round(Math.random() * 10);
};
var randomColor = function (opacity) {
return 'rgba(' + Math.round(Math.random() * 255) + ',' + Math.round(Math.random() * 255) + ',' + Math.round(Math.random() * 255) + ',' + (opacity || '.3') + ')';
};

var generateData = function (count) {
var data = [];

var y = 0;
for (var i = 0; i < count; i++) {
y = scalingFactor(y);
data.push({
x: i,
y: y,
});
}

return data;
};

var generateDataset = function(name, dataCount) {
return {
label: name,
data: generateData(dataCount),
borderColor: randomColor(1),
borderWidth: 2,
fill: false,
};
};

var config = {
type: 'line',
data: {
datasets: [generateDataset("Foo", 500), generateDataset("Bar", 500)]
},
options: {
responsive: true,
title:{
display:true,
text: 'Chart.js Downsample Plugin'
},
legend: {
display: false
},
scales: {
xAxes: [{
type: 'linear',
position: 'bottom',
}]
},
downsample: {
enabled: true,
threshold: 100,
},
animation: {
duration: 0,
},
elements: {
point: {
radius: 0, // disable points
}
}
}
};

window.onload = function () {
var ctx = document.getElementById("canvas").getContext("2d");
window.myLine = new Chart(ctx, config);

var slider = document.getElementById("threshold"),
thresholdCurrent = document.getElementById("thresholdCurrent");

slider.max = config.data.datasets[0].data.length;
slider.value = thresholdCurrent.innerHTML = config.options.downsample.threshold;

slider.oninput = function() {
window.myLine.options.downsample.threshold = thresholdCurrent.innerHTML = slider.value;
window.myLine.update();
};
};

</script>
</body>

</html>
134 changes: 134 additions & 0 deletions samples/many-datasets.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
<!doctype html>
<html>

<head>
<title>Scatter Chart</title>
<script src="../node_modules/chart.js/dist/Chart.bundle.js"></script>
<script src="../chartjs-plugin-downsample.js"></script>
<style>
canvas {
-moz-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
}
</style>
</head>

<body>
<div style="width:75%">
<div>
<canvas id="canvas"></canvas>
<div>
<label for="threshold">Threshold (total): </label>
<input type="range" id="threshold" min="1" max="0" value="0" step="1" style="width: 100%" />
<span id="thresholdCurrent">0</span>
</div>
<div>
<button type="button" onclick="addDataset(window.myLine);">Add Dataset</button>
</div>
</div>
</div>
<script>
var scalingFactor = function(value) {
return (value * 0.9) + (Math.random() > 0.5 ? 1.0 : -1.0) * Math.round(Math.random() * 10);
};
var randomColor = function (opacity) {
return 'rgba(' + Math.round(Math.random() * 255) + ',' + Math.round(Math.random() * 255) + ',' + Math.round(Math.random() * 255) + ',' + (opacity || '.3') + ')';
};

var generateData = function (count) {
var data = [];

var y = 0;
for (var i = 0; i < count; i++) {
y = scalingFactor(y);
data.push({
x: i,
y: y,
});
}

return data;
};

var threshold = 0;

var generateDataset = function(name, dataCount) {
return {
label: name,
data: generateData(dataCount),
borderColor: randomColor(1),
borderWidth: 2,
fill: false,
};
};

var updateChart = function(chart) {
chart.options.downsample.threshold = Math.floor(threshold / chart.data.datasets.length);
chart.update();
};

var addDataset = function(chart) {
chart.data.datasets.push(generateDataset(
Math.floor(Math.random() * 1337), 500
));

updateChart(chart);
};

var config = {
type: 'line',
data: {
datasets: [generateDataset("Foo", 500), generateDataset("Bar", 500)]
},
options: {
responsive: true,
title:{
display:true,
text: 'Chart.js Downsample Plugin'
},
legend: {
display: false
},
scales: {
xAxes: [{
type: 'linear',
position: 'bottom',
}]
},
downsample: {
enabled: true,
threshold: 50,
preferOriginalData: false,
},
animation: {
duration: threshold,
},
elements: {
point: {
radius: 0, // disable points
}
}
}
};

window.onload = function () {
var ctx = document.getElementById("canvas").getContext("2d");
window.myLine = new Chart(ctx, config);

var slider = document.getElementById("threshold"),
thresholdCurrent = document.getElementById("thresholdCurrent");

slider.max = 500;
slider.value = thresholdCurrent.innerHTML = config.options.downsample.threshold;

slider.oninput = function() {
threshold = thresholdCurrent.innerHTML = slider.value;
updateChart(window.myLine);
};
};

</script>
</body>

</html>
Loading

0 comments on commit 95a6e36

Please sign in to comment.