]> git.ipfire.org Git - thirdparty/Chart.js.git/commitdiff
Start on pan support
authorEvert Timberg <evert.timberg+github@gmail.com>
Thu, 21 Apr 2016 11:48:47 +0000 (07:48 -0400)
committerEvert Timberg <evert.timberg+github@gmail.com>
Thu, 21 Apr 2016 11:48:47 +0000 (07:48 -0400)
samples/zoom.html [new file with mode: 0644]
src/core/core.scale.js
src/scales/scale.linear.js
src/scales/scale.logarithmic.js
src/scales/scale.time.js
test/scale.linear.tests.js
test/scale.logarithmic.tests.js
test/scale.time.tests.js

diff --git a/samples/zoom.html b/samples/zoom.html
new file mode 100644 (file)
index 0000000..6954631
--- /dev/null
@@ -0,0 +1,232 @@
+<!doctype html>
+<html>
+
+<head>
+    <title>Scatter Chart</title>
+    <script src="../dist/Chart.bundle.js"></script>
+    <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.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>
+    </div>
+    <script src="http://hammerjs.github.io/dist/hammer.min.js"></script>
+    <script type="text/javascript">
+        var helpers = Chart.helpers;
+        function canZoomDirection(mode, dir) {
+            if (mode === undefined) {
+                return true;
+            } else if (typeof mode === 'string') {
+                return mode.indexOf(dir) !== -1;
+            }
+
+            return false;
+        }
+
+        function zoomScaleIn(scale, zoom, center) {
+            if (typeof scale.min === 'number' && typeof scale.max === 'number') {
+                var newDiff = (scale.max - scale.min) * (zoom - 1);
+                scale.options.ticks.min = scale.min + (newDiff / 2);
+                scale.options.ticks.max = scale.max - (newDiff / 2);
+            } else {
+                // Category scale
+                var indexDiff = scale.maxIndex - scale.minIndex;
+            }
+        }
+
+        function doZoom(chartInstance, zoom, center) {
+            var ca = chartInstance.chartArea;
+            if (!center) {
+                center = {
+                    x: (ca.left + ca.right) / 2,
+                    y: (ca.top + ca.bottom) / 2,
+                }
+            }
+
+            if (chartInstance.options.zoom && chartInstance.options.zoom.enabled) {
+                // Do the zoom here
+                helpers.each(chartInstance.scales, function(scale, id) {
+                    if (scale.isHorizontal() && canZoomDirection(chartInstance.options.zoom.mode, 'x')) {
+                        zoomScaleIn(scale, zoom, center);
+                    } else if (canZoomDirection(chartInstance.options.zoom.mode, 'y')) {
+                        // Do Y zoom
+                        zoomScaleIn(scale, zoom, center);
+                    }
+                });
+
+                chartInstance.update();
+            }
+        }
+
+        // Chartjs Zoom Plugin
+        var ZoomPlugin = Chart.PluginBase.extend({
+            beforeInit: function(chartInstance) {
+                var node = chartInstance.chart.ctx.canvas;
+                var options = chartInstance.options;
+                var wheelHandler = function(e) {
+                    if (e.wheelDelta > 0) {
+                        doZoom(chartInstance, 1.1);
+                    } else {
+                        doZoom(chartInstance, 0.909);
+                    }
+                };
+                chartInstance._wheelHandler = wheelHandler;
+
+                node.addEventListener('mousewheel', wheelHandler);
+
+                var mc = new window.Hammer.Manager(node);
+                mc.add(new Hammer.Pinch());
+
+                var handlePinch = function handlePinch(e) {
+                    var diff = 1 / (currentPinchScaling) * e.scale;
+                    doZoom(chartInstance, diff, e.center);
+
+                    // Keep track of overall scale
+                    currentPinchScaling = e.scale;
+                };
+
+                // Hammer reports the total scaling. We need the incremental amount
+                var currentPinchScaling;
+
+                mc.on('pinchstart', function(e) {
+                    currentPinchScaling = 1; // reset tracker
+                });
+                mc.on('pinch', handlePinch);
+                mc.on('pinchend', function(e) {
+                    handlePinch(e);
+                    currentPinchScaling = null; // reset
+                });
+            },
+
+            destroy: function(chartInstance) {
+                var node = chartInstance.chart.ctx.canvas;
+
+                // Remove wheel handler
+                node.removeEventListener('mousewheel', chartInstance._wheelHandler);
+            }
+        });
+
+        Chart.pluginService.register(new ZoomPlugin());
+    </script>
+
+    <script>
+        var randomScalingFactor = function() {
+            return (Math.random() > 0.5 ? 1.0 : -1.0) * Math.round(Math.random() * 100);
+        };
+        var randomColor = function(opacity) {
+            return 'rgba(' + Math.round(Math.random() * 255) + ',' + Math.round(Math.random() * 255) + ',' + Math.round(Math.random() * 255) + ',' + (opacity || '.3') + ')';
+        };
+
+        var scatterChartData = {
+            datasets: [{
+                label: "My First dataset",
+                data: [{
+                    x: randomScalingFactor(),
+                    y: randomScalingFactor(),
+                }, {
+                    x: randomScalingFactor(),
+                    y: randomScalingFactor(),
+                }, {
+                    x: randomScalingFactor(),
+                    y: randomScalingFactor(),
+                }, {
+                    x: randomScalingFactor(),
+                    y: randomScalingFactor(),
+                }, {
+                    x: randomScalingFactor(),
+                    y: randomScalingFactor(),
+                }, {
+                    x: randomScalingFactor(),
+                    y: randomScalingFactor(),
+                }, {
+                    x: randomScalingFactor(),
+                    y: randomScalingFactor(),
+                }]
+            }, {
+                label: "My Second dataset",
+                data: [{
+                    x: randomScalingFactor(),
+                    y: randomScalingFactor(),
+                }, {
+                    x: randomScalingFactor(),
+                    y: randomScalingFactor(),
+                }, {
+                    x: randomScalingFactor(),
+                    y: randomScalingFactor(),
+                }, {
+                    x: randomScalingFactor(),
+                    y: randomScalingFactor(),
+                }, {
+                    x: randomScalingFactor(),
+                    y: randomScalingFactor(),
+                }, {
+                    x: randomScalingFactor(),
+                    y: randomScalingFactor(),
+                }, {
+                    x: randomScalingFactor(),
+                    y: randomScalingFactor(),
+                }]
+            }]
+        };
+
+        $.each(scatterChartData.datasets, function(i, dataset) {
+            dataset.borderColor = randomColor(0.4);
+            dataset.backgroundColor = randomColor(0.1);
+            dataset.pointBorderColor = randomColor(0.7);
+            dataset.pointBackgroundColor = randomColor(0.5);
+            dataset.pointBorderWidth = 1;
+        });
+
+        window.onload = function() {
+            var ctx = document.getElementById("canvas").getContext("2d");
+            window.myScatter = Chart.Scatter(ctx, {
+                data: scatterChartData,
+                options: {
+                    title: {
+                        display: true,
+                        text: 'Chart.js Scatter Chart'
+                    },
+                    scales: {
+                        xAxes: [{
+                            gridLines: {
+                                zeroLineColor: "rgba(0,255,0,1)"
+                            },
+                            scaleLabel: {
+                                display: true,
+                                labelString: 'x axis'
+                            },
+                            ticks: {
+                                maxRotation: 0
+                            }
+                        }],
+                        yAxes: [{
+                            gridLines: {
+                                zeroLineColor: "rgba(0,255,0,1)"
+                            },
+                            scaleLabel: {
+                                display: true,
+                                labelString: 'y axis'
+                            }
+                        }]
+                    },
+                    zoom: {
+                        enabled: true,
+                        mode: 'x'
+                    }
+                }
+            });
+        };
+    </script>
+</body>
+
+</html>
index 24881e1286ee0a0ede63faa4e26d2c86a54d9bac..45b36dd91a386278ee07a49888c936db294b2fe4 100644 (file)
@@ -393,6 +393,9 @@ module.exports = function(Chart) {
                // Used to get data value locations.  Value can either be an index or a numerical value
                getPixelForValue: helpers.noop,
 
+               // Used to get the data value from a given pixel. This is the inverse of getPixelForValue
+               getValueForPixel: helpers.noop,
+
                // Used for tick location, should
                getPixelForTick: function(index, includeOffset) {
                        if (this.isHorizontal()) {
index 3cf2b020d939195870aa718a75040ab75b6df68c..868d6581cbee1f371be1d2bb69de54dc2fb52910 100644 (file)
@@ -250,6 +250,19 @@ module.exports = function(Chart) {
                                return Math.round(pixel);
                        }
                },
+               getValueForPixel: function(pixel) {
+                       var offset;
+
+                       if (this.isHorizontal()) {
+                               var innerWidth = this.width - (this.paddingLeft + this.paddingRight);
+                               offset = (pixel - this.left - this.paddingLeft) / innerWidth;
+                       } else {
+                               var innerHeight = this.height - (this.paddingTop + this.paddingBottom);
+                               offset = (this.bottom - this.paddingBottom - pixel) / innerHeight;
+                       }
+
+                       return this.min + ((this.max - this.min) * offset);
+               },
                getPixelForTick: function(index, includeOffset) {
                        return this.getPixelForValue(this.ticksAsNumbers[index], null, null, includeOffset);
                }
index 070a08d61ff0fd94836e39a98608632c1ef5db28..7476765db63f95f37da766eb0787f4dad24f8636 100644 (file)
@@ -187,6 +187,16 @@ module.exports = function(Chart) {
                        }
 
                        return pixel;
+               },
+               getValueForPixel: function(pixel) {
+                       var offset;
+
+                       if (this.isHorizontal) {
+                               var innerWidth = this.width - (this.paddingLeft + this.paddingRight);
+                       } else {
+                               var innerHeight = this.height - (this.paddingTop + this.paddingBottom);
+                       }
+
                }
 
        });
index 2dbb4ba6d4d067e2a24b5772d6517072b0737a53..47d1fa09c691ab95c4c98e812f7205ece2d99e82 100644 (file)
@@ -316,6 +316,11 @@ module.exports = function(Chart) {
                                }
                        }
                },
+               getValueForPixel: function(pixel) {
+                       var offset = pixel - (this.isHorizontal() ? this.left + this.paddingLeft : this.top + this.paddingTop);
+                       offset *= this.scaleSizeInUnits;
+                       return this.firstTick.clone().add(offset, this.tickUnit);
+               },
                parseTime: function(label) {
                        if (typeof this.options.time.parser === 'string') {
                                return moment(label, this.options.time.parser);
index 3236949b123a23e21cfb0c54f6a139ec18183b6d..59af5968f68c3d9285f1d581c7ecbfe1334e68ef 100644 (file)
@@ -778,8 +778,13 @@ describe('Linear Scale', function() {
                verticalScale.height = 110;
 
                expect(verticalScale.getPixelForValue(1, 0, 0)).toBe(5); // top + paddingTop
+               expect(verticalScale.getValueForPixel(5)).toBe(1);
+
                expect(verticalScale.getPixelForValue(-1, 0, 0)).toBe(105); // bottom - paddingBottom
+               expect(verticalScale.getValueForPixel(105)).toBe(-1);
+
                expect(verticalScale.getPixelForValue(0, 0, 0)).toBe(55); // halfway
+               expect(verticalScale.getValueForPixel(55)).toBe(0);
 
                var horizontalConfig = Chart.helpers.clone(config);
                horizontalConfig.position = 'bottom';
@@ -806,8 +811,13 @@ describe('Linear Scale', function() {
 
                // Range expands to [-2, 2] due to nicenum algorithm
                expect(horizontalScale.getPixelForValue(2, 0, 0)).toBe(105); // right - paddingRight
+               expect(horizontalScale.getValueForPixel(105)).toBe(2);
+
                expect(horizontalScale.getPixelForValue(-2, 0, 0)).toBe(5); // left + paddingLeft
+               expect(horizontalScale.getValueForPixel(5)).toBe(-2);
+
                expect(horizontalScale.getPixelForValue(0, 0, 0)).toBe(55); // halfway
+               expect(horizontalScale.getValueForPixel(55)).toBe(0);
        });
 
        it('should fit correctly', function() {
index a75f174e15decafb191acae8ddc755e844c4c380..f49865a943e4a2f61e4ee0e71b78ceb71e2d2851 100644 (file)
@@ -595,8 +595,14 @@ describe('Logarithmic Scale tests', function() {
                verticalScale.height = 110;
 
                expect(verticalScale.getPixelForValue(80, 0, 0)).toBe(5); // top + paddingTop
+               expect(verticalScale.getValueForPixel(5)).toBeCloseTo(80, 1e-4);
+
                expect(verticalScale.getPixelForValue(1, 0, 0)).toBe(105); // bottom - paddingBottom
+               expect(verticalScale.getValueForPixel(105)).toBeCloseTo(1, 1e-4);
+
                expect(verticalScale.getPixelForValue(10, 0, 0)).toBeCloseTo(52.4, 1e-4); // halfway
+               expect(verticalScale.getValueForPixel(52.4)).toBeCloseTo(10, 1e-4);
+
                expect(verticalScale.getPixelForValue(0, 0, 0)).toBe(5); // 0 is invalid. force it on top
 
                var horizontalConfig = Chart.helpers.clone(config);
@@ -623,8 +629,14 @@ describe('Logarithmic Scale tests', function() {
                horizontalScale.height = 50;
 
                expect(horizontalScale.getPixelForValue(80, 0, 0)).toBe(105); // right - paddingRight
+               expect(horizontalScale.getValueForPixel(105)).toBeCloseTo(80, 1e-4);
+
                expect(horizontalScale.getPixelForValue(1, 0, 0)).toBe(5); // left + paddingLeft
+               expect(horizontalScale.getValueForPixel(5)).toBeCloseTo(1, 1e-4);
+
                expect(horizontalScale.getPixelForValue(10, 0, 0)).toBeCloseTo(57.5, 1e-4); // halfway
+               expect(horizontalScale.getValueForPixel(57.5)).toBeCloseTo(10, 1e-4);
+
                expect(horizontalScale.getPixelForValue(0, 0, 0)).toBe(5); // 0 is invalid, put it on the left.
        });
 });
index c35b4cea32b6b6d0b3447b5c5bb390e6191aafc9..722354d59465298f70b1f9508140a8ef9f62965a 100644 (file)
@@ -337,7 +337,10 @@ describe('Time scale tests', function() {
                scale.bottom = 38;
 
                expect(scale.getPixelForValue('', 0, 0)).toBe(81);
+               expect(scale.getValueForPixel(81)).toEqual(scale.firstTick);
+
                expect(scale.getPixelForValue('', 6, 0)).toBe(323);
+               expect(scale.getValueForPixel(323)).toEqual(scale.lastTick);
 
                var verticalScaleConfig = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('time'));
                verticalScaleConfig.position = "left";
@@ -359,7 +362,10 @@ describe('Time scale tests', function() {
                verticalScale.bottom = 400;
 
                expect(verticalScale.getPixelForValue('', 0, 0)).toBe(38);
+               expect(verticalScale.getValueForPixel(38)).toEqual(verticalScale.firstTick);
+
                expect(verticalScale.getPixelForValue('', 6, 0)).toBe(375);
+               expect(verticalScale.getValueForPixel(375)).toEqual(verticalScale.lastTick);
        });
 
        it('should get the correct label for a data value', function() {