Skip to content

Commit c7360b1

Browse files
authored
Fix category panning (#476)
* Update category panning * Restore hammer-simulator for one test
1 parent b746091 commit c7360b1

19 files changed

+304
-33
lines changed

docs/guide/options.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ const chart = new Chart('id', {
3636
| `mode` | `'x'`\|`'y'`\|`'xy'` | `'xy'` | Allowed panning directions
3737
| `modifierKey` | `'ctrl'`\|`'alt'`\|`'shift'`\|`'meta'` | `null` | Modifier key required for panning with mouse
3838
| `overScaleMode` | `'x'`\|`'y'`\|`'xy'` | `undefined` | Which of the enabled panning directions should only be available when the mouse cursor is over a scale for that axis
39-
| `speed` | `number` | `20` | Factor for pan velocity on **category scale**
4039
| `threshold` | `number` | `10` | Mimimal pan distance required before actually applying pan
4140

4241
### Pan Events

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
"eslint-plugin-es": "^4.1.0",
4848
"eslint-plugin-html": "^6.1.2",
4949
"eslint-plugin-markdown": "^2.0.1",
50-
"hammer-simulator": "0.0.1",
50+
"hammer-simulator": "^0.0.1",
5151
"jasmine": "^3.7.0",
5252
"karma": "^6.3.2",
5353
"karma-chrome-launcher": "^3.1.0",

src/core.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,12 +76,12 @@ export function resetZoom(chart) {
7676
chart.update();
7777
}
7878

79-
function panScale(scale, delta, panOptions, limits) {
79+
function panScale(scale, delta, limits) {
8080
const {panDelta} = getState(scale.chart);
8181
// Add possible cumulative delta from previous pan attempts where scale did not change
8282
delta += panDelta[scale.id] || 0;
8383
const fn = panFunctions[scale.type] || panFunctions.default;
84-
if (call(fn, [scale, delta, panOptions, limits])) {
84+
if (call(fn, [scale, delta, limits])) {
8585
// The scale changed, reset cumulative delta
8686
panDelta[scale.id] = 0;
8787
} else {
@@ -102,9 +102,9 @@ export function doPan(chart, pan, enabledScales) {
102102

103103
each(enabledScales || chart.scales, function(scale) {
104104
if (scale.isHorizontal() && xEnabled) {
105-
panScale(scale, x, panOptions, limits);
105+
panScale(scale, x, limits);
106106
} else if (!scale.isHorizontal() && yEnabled) {
107-
panScale(scale, y, panOptions, limits);
107+
panScale(scale, y, limits);
108108
}
109109
});
110110

src/plugin.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ export default {
1515
pan: {
1616
enabled: false,
1717
mode: 'xy',
18-
speed: 20,
1918
threshold: 10,
2019
modifierKey: null,
2120
},

src/scale.types.js

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -66,23 +66,31 @@ function zoomCategoryScale(scale, zoom, center, limits) {
6666
return updateRange(scale, newRange, limits, true);
6767
}
6868

69-
const categoryDelta = new WeakMap();
70-
function panCategoryScale(scale, delta, panOptions, limits) {
69+
function scaleLength(scale) {
70+
return scale.isHorizontal() ? scale.width : scale.height;
71+
}
72+
73+
function panCategoryScale(scale, delta, limits) {
7174
const labels = scale.getLabels();
7275
const lastLabelIndex = labels.length - 1;
73-
const offsetAmt = Math.max(scale.ticks.length, 1);
74-
const panSpeed = panOptions.speed;
75-
const step = Math.round(scale.width / (offsetAmt * panSpeed));
76-
const cumDelta = (categoryDelta.get(scale) || 0) + delta;
77-
const scaleMin = scale.min;
78-
const minIndex = cumDelta > step ? Math.max(0, scaleMin - 1)
79-
: cumDelta < -step ? Math.min(lastLabelIndex - offsetAmt + 1, scaleMin + 1)
80-
: scaleMin;
81-
const maxIndex = Math.min(lastLabelIndex, minIndex + offsetAmt - 1);
82-
83-
categoryDelta.set(scale, minIndex !== scaleMin ? 0 : cumDelta);
76+
let {min, max} = scale;
77+
// The visible range. Ticks can be skipped, and thus not reliable.
78+
const range = Math.max(max - min, 1);
79+
// How many pixels of delta is required before making a step. stepSize, but limited to max 1/10 of the scale length.
80+
const stepDelta = Math.round(scaleLength(scale) / Math.max(range, 10));
81+
const stepSize = Math.ceil(Math.abs(delta / stepDelta));
82+
let applied;
83+
if (delta < -stepDelta) {
84+
max = Math.min(max + stepSize, lastLabelIndex);
85+
min = range === 1 ? max : max - range;
86+
applied = max === lastLabelIndex;
87+
} else if (delta > stepDelta) {
88+
min = Math.max(0, min - stepSize);
89+
max = range === 1 ? min : min + range;
90+
applied = min === 0;
91+
}
8492

85-
return updateRange(scale, {min: minIndex, max: maxIndex}, limits);
93+
return updateRange(scale, {min, max}, limits) || applied;
8694
}
8795

8896
const OFFSETS = {
@@ -96,7 +104,7 @@ const OFFSETS = {
96104
year: 182 * 24 * 60 * 60 * 1000 // 182 d
97105
};
98106

99-
function panNumericalScale(scale, delta, panOptions, limits) {
107+
function panNumericalScale(scale, delta, limits) {
100108
const {min: prevStart, max: prevEnd, options} = scale;
101109
const round = options.time && options.time.round;
102110
const offset = OFFSETS[round] || 0;

test/fixtures/pan/category-x-1.js

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
const labels = [];
2+
const data = [];
3+
for (let i = 1; i <= 100; i++) {
4+
labels.push('Label ' + i);
5+
data.push(Math.sin(i / 100 * Math.PI) * 10);
6+
}
7+
8+
const canvas = document.createElement('canvas');
9+
canvas.width = 512;
10+
canvas.height = 512;
11+
const ctx = canvas.getContext('2d');
12+
13+
module.exports = {
14+
tolerance: 0.02,
15+
config: {
16+
type: 'bar',
17+
data: {
18+
labels,
19+
datasets: [{
20+
data,
21+
barPercentage: 1,
22+
categoryPercentage: 1,
23+
backgroundColor: c => `rgba(${255 - c.index * 4}, 0, 0, 1)`
24+
}]
25+
},
26+
options: {
27+
events: [],
28+
scales: {
29+
x: {
30+
display: false,
31+
min: 'Label 1',
32+
max: 'Label 1'
33+
},
34+
y: {display: false, max: 10}
35+
},
36+
plugins: {
37+
legend: false,
38+
tooltip: false,
39+
zoom: {
40+
pan: {
41+
enabled: true,
42+
mode: 'x',
43+
}
44+
}
45+
},
46+
layout: {
47+
padding: 2
48+
}
49+
}
50+
},
51+
options: {
52+
spriteText: true,
53+
run(chart) {
54+
const steps = 16;
55+
const n = Math.sqrt(steps);
56+
const side = 512 / n;
57+
for (let i = 0; i < steps; i++) {
58+
const col = i % n;
59+
const row = Math.floor(i / n);
60+
chart.pan({x: -100});
61+
chart.update();
62+
ctx.drawImage(chart.canvas, col * side, row * side, side, side);
63+
}
64+
Chart.helpers.clearCanvas(chart.canvas);
65+
chart.ctx.drawImage(canvas, 0, 0);
66+
}
67+
}
68+
};

test/fixtures/pan/category-x-1.png

7.35 KB
Loading

test/fixtures/pan/category-x-10.js

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
const labels = [];
2+
const data = [];
3+
for (let i = 1; i <= 100; i++) {
4+
labels.push('Label ' + i);
5+
data.push(Math.sin(i / 100 * Math.PI) * 10);
6+
}
7+
8+
const canvas = document.createElement('canvas');
9+
canvas.width = 512;
10+
canvas.height = 512;
11+
const ctx = canvas.getContext('2d');
12+
13+
module.exports = {
14+
tolerance: 0.02,
15+
config: {
16+
type: 'bar',
17+
data: {
18+
labels,
19+
datasets: [{
20+
data,
21+
barPercentage: 1,
22+
categoryPercentage: 1,
23+
backgroundColor: c => c.index < 50 ? 'blue' : 'red'
24+
}]
25+
},
26+
options: {
27+
events: [],
28+
scales: {
29+
x: {
30+
display: false,
31+
min: 'Label 1',
32+
max: 'Label 10',
33+
},
34+
y: {display: false, max: 10}
35+
},
36+
plugins: {
37+
legend: false,
38+
tooltip: false,
39+
zoom: {
40+
pan: {
41+
enabled: true,
42+
mode: 'x',
43+
}
44+
}
45+
},
46+
layout: {
47+
padding: 2
48+
}
49+
}
50+
},
51+
options: {
52+
spriteText: true,
53+
run(chart) {
54+
const steps = 16;
55+
const n = Math.sqrt(steps);
56+
const side = 512 / n;
57+
for (let i = 0; i < steps; i++) {
58+
const col = i % n;
59+
const row = Math.floor(i / n);
60+
chart.pan({x: -200});
61+
chart.update();
62+
ctx.drawImage(chart.canvas, col * side, row * side, side, side);
63+
}
64+
Chart.helpers.clearCanvas(chart.canvas);
65+
chart.ctx.drawImage(canvas, 0, 0);
66+
}
67+
}
68+
};

test/fixtures/pan/category-x-10.png

17.2 KB
Loading

0 commit comments

Comments
 (0)