-
Notifications
You must be signed in to change notification settings - Fork 193
/
zoom.js
125 lines (117 loc) · 3.8 KB
/
zoom.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
import { dispatch } from 'd3-dispatch';
import { select } from 'd3-selection';
import { zoom, zoomIdentity } from 'd3-zoom';
import { rebind } from '@d3fc/d3fc-rebind';
const domainsEqual = (a, b) => {
if (a == null && b == null) {
return true;
}
const aDomain = a.domain();
const bDomain = b.domain();
return (
aDomain.length === bDomain.length &&
aDomain.every((d, i) => d?.valueOf() === bDomain[i]?.valueOf())
);
};
const subtract = (a, b) =>
zoomIdentity.scale(a.k / b.k).translate(a.x - b.x, a.y - b.y);
const symbol = Symbol('d3fc-domain-zoom');
export default () => {
const dispatcher = dispatch('zoom');
const zoomer = zoom().on('zoom', function (e) {
const { transform } = e;
const node = this;
let updatedTransform = transform;
let {
originalXScale,
previousXScale,
xScale,
originalYScale,
previousYScale,
yScale,
previousTransform
} = node[symbol];
if (
!domainsEqual(previousXScale, xScale) ||
!domainsEqual(previousYScale, yScale)
) {
originalXScale = xScale?.copy();
originalYScale = yScale?.copy();
updatedTransform = subtract(transform, previousTransform);
}
if (xScale != null) {
previousXScale = updatedTransform.rescaleX(
originalXScale.range(xScale.range())
);
xScale.domain(previousXScale.domain());
}
if (yScale != null) {
previousYScale = updatedTransform.rescaleY(
originalYScale.range(yScale.range())
);
yScale.domain(previousYScale.domain());
}
previousTransform = updatedTransform;
node[symbol] = {
originalXScale,
previousXScale,
xScale,
originalYScale,
previousYScale,
yScale,
previousTransform
};
if (updatedTransform !== transform) {
zoomer.transform(select(node), updatedTransform);
}
dispatcher.call('zoom', this, e);
});
const instance = (selection, xScale = null, yScale = null) => {
if (xScale == null && yScale == null) {
console.warn(
`Without an xScale and/or yScale specified, this component won't do anything. Perhaps you forgot to specify them e.g. selection.call(zoom, x, y)?`
);
}
selection
.each((d, i, nodes) => {
const existingContext = nodes[i][symbol];
if (
existingContext != null &&
existingContext.xScale === xScale &&
existingContext.yScale === yScale
) {
console.warn(
`This component should only be called on a selection once. Perhaps you're missing an .enter()?`
);
}
const xScaleCopy = xScale?.copy();
const yScaleCopy = yScale?.copy();
nodes[i][symbol] = {
originalXScale: xScaleCopy,
previousXScale: xScaleCopy,
xScale,
originalYScale: yScaleCopy,
previousYScale: yScaleCopy,
yScale,
previousTransform: zoomIdentity
};
})
.call(zoomer);
};
rebind(instance, dispatcher, 'on');
rebind(
instance,
zoomer,
'extent',
'filter',
'wheelDelta',
'touchable',
'clickDistance',
'tapDistance',
'duration',
'interpolate',
'scaleExtent',
'translateExtent'
);
return instance;
};