@fc-plot/ts-graph 0.16.5 → 0.17.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/BundleReport.html +2 -2
- package/dist/index.js +1 -1
- package/examples/index.js +27 -13
- package/package.json +1 -1
- package/src/getNearestPoints.ts +36 -17
- package/src/index.ts +294 -170
- package/src/interface.ts +164 -160
- package/src/line.ts +51 -26
- package/src/tooltip.ts +125 -63
- package/src/utils.ts +35 -18
- package/src/yAxis.ts +35 -19
package/src/index.ts
CHANGED
|
@@ -1,18 +1,36 @@
|
|
|
1
|
-
import * as d3 from
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
import
|
|
13
|
-
import
|
|
14
|
-
import
|
|
15
|
-
import
|
|
1
|
+
import * as d3 from "d3";
|
|
2
|
+
import {
|
|
3
|
+
assign,
|
|
4
|
+
merge,
|
|
5
|
+
debounce,
|
|
6
|
+
throttle,
|
|
7
|
+
uniqueId,
|
|
8
|
+
isEmpty,
|
|
9
|
+
find,
|
|
10
|
+
sortBy,
|
|
11
|
+
} from "lodash";
|
|
12
|
+
import { addListener, removeListener } from "resize-detector";
|
|
13
|
+
import EventEmitter from "events";
|
|
14
|
+
import XAxis from "./xAxis";
|
|
15
|
+
import YAxis from "./yAxis";
|
|
16
|
+
import Tooltip from "./tooltip";
|
|
17
|
+
import Line from "./line";
|
|
18
|
+
import Bar from "./bar";
|
|
19
|
+
import Zoom from "./zoom";
|
|
20
|
+
import Legend from "./legend";
|
|
21
|
+
import Shapes from "./shapes";
|
|
22
|
+
import {
|
|
23
|
+
TsGraphType,
|
|
24
|
+
Options,
|
|
25
|
+
EventPosition,
|
|
26
|
+
Transform,
|
|
27
|
+
Serie,
|
|
28
|
+
SerieDataItem,
|
|
29
|
+
XScales,
|
|
30
|
+
YScales,
|
|
31
|
+
} from "./interface";
|
|
32
|
+
import { getTouchPosition, getYkeyValue } from "./utils";
|
|
33
|
+
import "../assets/style.less";
|
|
16
34
|
|
|
17
35
|
const eventEmitter = new EventEmitter();
|
|
18
36
|
export default class TsGraph {
|
|
@@ -39,11 +57,28 @@ export default class TsGraph {
|
|
|
39
57
|
ratio: window.devicePixelRatio || 1,
|
|
40
58
|
xkey: 0,
|
|
41
59
|
ykey: 1,
|
|
42
|
-
|
|
43
|
-
|
|
60
|
+
ykey2: 2,
|
|
61
|
+
oykey: "__originValue", // stack area 用于缓存原始值
|
|
62
|
+
timestamp: "x",
|
|
44
63
|
chart: {
|
|
45
|
-
id: uniqueId(
|
|
46
|
-
colors: [
|
|
64
|
+
id: uniqueId("ts-graph-"),
|
|
65
|
+
colors: [
|
|
66
|
+
"#3399CC",
|
|
67
|
+
"#CC9933",
|
|
68
|
+
"#9966CC",
|
|
69
|
+
"#66CC66",
|
|
70
|
+
"#CC3333",
|
|
71
|
+
"#99CCCC",
|
|
72
|
+
"#CCCC66",
|
|
73
|
+
"#CC99CC",
|
|
74
|
+
"#99CC99",
|
|
75
|
+
"#CC6666",
|
|
76
|
+
"#336699",
|
|
77
|
+
"#996633",
|
|
78
|
+
"#993399",
|
|
79
|
+
"#339966",
|
|
80
|
+
"#993333",
|
|
81
|
+
],
|
|
47
82
|
width: userOptions.chart.renderTo.offsetWidth,
|
|
48
83
|
height: userOptions.chart.renderTo.offsetHeight || 350,
|
|
49
84
|
marginTop: 10,
|
|
@@ -53,13 +88,13 @@ export default class TsGraph {
|
|
|
53
88
|
},
|
|
54
89
|
xAxis: {
|
|
55
90
|
visible: true,
|
|
56
|
-
lineColor:
|
|
91
|
+
lineColor: "#ccc",
|
|
57
92
|
lineWidth: 1,
|
|
58
93
|
tickLength: 5,
|
|
59
94
|
tickpadding: 5,
|
|
60
|
-
tickColor:
|
|
95
|
+
tickColor: "#ccc",
|
|
61
96
|
labels: {
|
|
62
|
-
color:
|
|
97
|
+
color: "#999",
|
|
63
98
|
fontSize: 11,
|
|
64
99
|
},
|
|
65
100
|
},
|
|
@@ -67,18 +102,18 @@ export default class TsGraph {
|
|
|
67
102
|
// min: -Infinity,
|
|
68
103
|
// max: Infinity,
|
|
69
104
|
visible: true,
|
|
70
|
-
lineColor:
|
|
105
|
+
lineColor: "#ccc",
|
|
71
106
|
lineWidth: 1,
|
|
72
107
|
tickLength: 5,
|
|
73
108
|
tickpadding: 5,
|
|
74
|
-
tickColor:
|
|
75
|
-
gridLineColor:
|
|
109
|
+
tickColor: "#ccc",
|
|
110
|
+
gridLineColor: "#efefef",
|
|
76
111
|
labels: {
|
|
77
|
-
color:
|
|
112
|
+
color: "#999",
|
|
78
113
|
fontSize: 11,
|
|
79
114
|
},
|
|
80
115
|
},
|
|
81
|
-
type:
|
|
116
|
+
type: "line",
|
|
82
117
|
line: {
|
|
83
118
|
enable: true,
|
|
84
119
|
width: 2,
|
|
@@ -99,12 +134,12 @@ export default class TsGraph {
|
|
|
99
134
|
shared: true,
|
|
100
135
|
cascade: false, // tooltip 联动
|
|
101
136
|
cascadeScope: undefined, // tooltip 联动的范围
|
|
102
|
-
timeFormat:
|
|
137
|
+
timeFormat: "%Y-%m-%d %H:%M:%S",
|
|
103
138
|
},
|
|
104
139
|
series: [],
|
|
105
140
|
legend: {
|
|
106
|
-
align:
|
|
107
|
-
verticalAlign:
|
|
141
|
+
align: "center",
|
|
142
|
+
verticalAlign: "top",
|
|
108
143
|
enabled: false,
|
|
109
144
|
},
|
|
110
145
|
fillNull: undefined,
|
|
@@ -130,17 +165,19 @@ export default class TsGraph {
|
|
|
130
165
|
this.options.notDisplayedSeries = [];
|
|
131
166
|
this.options.chart.containerWidth = chart.width;
|
|
132
167
|
this.options.chart.containerHeight = chart.height;
|
|
133
|
-
this.options.chart.width =
|
|
134
|
-
|
|
168
|
+
this.options.chart.width =
|
|
169
|
+
chart.width - chart.marginLeft - chart.marginRight;
|
|
170
|
+
this.options.chart.height =
|
|
171
|
+
chart.height - chart.marginTop - chart.marginBottom;
|
|
135
172
|
this.createContainer();
|
|
136
173
|
this.createCanvas();
|
|
137
174
|
this.initSeries();
|
|
138
175
|
this.initScales();
|
|
139
176
|
this.initEvent();
|
|
140
|
-
if (this.options.type ===
|
|
177
|
+
if (this.options.type === "line") {
|
|
141
178
|
this.initLine();
|
|
142
179
|
}
|
|
143
|
-
if (this.options.type ===
|
|
180
|
+
if (this.options.type === "bar") {
|
|
144
181
|
this.initBar();
|
|
145
182
|
}
|
|
146
183
|
this.initLegend();
|
|
@@ -151,7 +188,9 @@ export default class TsGraph {
|
|
|
151
188
|
}
|
|
152
189
|
|
|
153
190
|
clearRect(ctx: CanvasRenderingContext2D) {
|
|
154
|
-
const {
|
|
191
|
+
const {
|
|
192
|
+
chart: { containerWidth, containerHeight },
|
|
193
|
+
} = this.options;
|
|
155
194
|
|
|
156
195
|
ctx.clearRect(0, 0, containerWidth, containerHeight);
|
|
157
196
|
}
|
|
@@ -161,10 +200,10 @@ export default class TsGraph {
|
|
|
161
200
|
if (this.options.xAxis.visible) {
|
|
162
201
|
this.yAxis.drawGridLine(this.yScales);
|
|
163
202
|
}
|
|
164
|
-
if (this.options.type ===
|
|
203
|
+
if (this.options.type === "line") {
|
|
165
204
|
this.line.draw(this.xScales, this.yScales);
|
|
166
205
|
}
|
|
167
|
-
if (this.options.type ===
|
|
206
|
+
if (this.options.type === "bar") {
|
|
168
207
|
this.bar.draw(this.xScales, this.yScales, transform);
|
|
169
208
|
}
|
|
170
209
|
if (this.options.xAxis.visible) {
|
|
@@ -195,10 +234,10 @@ export default class TsGraph {
|
|
|
195
234
|
this.initSeries();
|
|
196
235
|
this.initScales();
|
|
197
236
|
this.initEvent();
|
|
198
|
-
if (this.options.type ===
|
|
237
|
+
if (this.options.type === "line") {
|
|
199
238
|
this.initLine();
|
|
200
239
|
}
|
|
201
|
-
if (this.options.type ===
|
|
240
|
+
if (this.options.type === "bar") {
|
|
202
241
|
this.initBar();
|
|
203
242
|
}
|
|
204
243
|
this.initTooltip();
|
|
@@ -213,16 +252,18 @@ export default class TsGraph {
|
|
|
213
252
|
|
|
214
253
|
showTooltip = (eventPosition: EventPosition) => {
|
|
215
254
|
this.tooltip.draw(eventPosition, this);
|
|
216
|
-
}
|
|
255
|
+
};
|
|
217
256
|
|
|
218
257
|
hideTooltip = () => {
|
|
219
258
|
this.tooltip.clear();
|
|
220
|
-
}
|
|
259
|
+
};
|
|
221
260
|
|
|
222
261
|
initEvent() {
|
|
223
262
|
const { cascade, cascadeScope } = this.options.tooltip;
|
|
224
|
-
const plotmoveType = cascadeScope ? `plotmove:${cascadeScope}` :
|
|
225
|
-
const plotleaveType = cascadeScope
|
|
263
|
+
const plotmoveType = cascadeScope ? `plotmove:${cascadeScope}` : "plotmove";
|
|
264
|
+
const plotleaveType = cascadeScope
|
|
265
|
+
? `plotleave:${cascadeScope}`
|
|
266
|
+
: "plotleave";
|
|
226
267
|
let mousedownStatus = false;
|
|
227
268
|
let mousedownPos: EventPosition = {} as EventPosition;
|
|
228
269
|
let mouseupPos: EventPosition = {} as EventPosition;
|
|
@@ -241,72 +282,107 @@ export default class TsGraph {
|
|
|
241
282
|
const handleMouseLeave = throttle(() => {
|
|
242
283
|
_this.tooltip.clear();
|
|
243
284
|
}, 5);
|
|
244
|
-
d3.select(this.eventCanvas)
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
285
|
+
d3.select(this.eventCanvas)
|
|
286
|
+
.on("mousemove", function mousemove(this: HTMLCanvasElement) {
|
|
287
|
+
isMouserover = true;
|
|
288
|
+
handleMouseover(d3.event);
|
|
289
|
+
if (cascade) eventEmitter.emit(plotmoveType, d3.event);
|
|
290
|
+
})
|
|
291
|
+
.on("mouseleave", function mouseleave(this: HTMLCanvasElement) {
|
|
292
|
+
mouseleavePos = d3.event;
|
|
293
|
+
isMouserover = false;
|
|
294
|
+
handleMouseLeave();
|
|
295
|
+
if (cascade) eventEmitter.emit(plotleaveType);
|
|
296
|
+
})
|
|
297
|
+
.on("mousedown", function mousedown(this: HTMLCanvasElement) {
|
|
298
|
+
if (d3.event.which === 1) {
|
|
299
|
+
mousedownStatus = true;
|
|
300
|
+
mousedownPos = d3.event;
|
|
301
|
+
}
|
|
302
|
+
})
|
|
303
|
+
.on("mouseup", function mouseup(this: HTMLCanvasElement) {
|
|
304
|
+
d3.event.stopPropagation();
|
|
305
|
+
const eventPosition = d3.event as EventPosition;
|
|
306
|
+
if (mousedownPos && mousedownPos.offsetX !== eventPosition.offsetX) {
|
|
307
|
+
_this.zoom.clearMarker();
|
|
308
|
+
_this.zoom.onZoom(
|
|
309
|
+
mousedownPos,
|
|
310
|
+
eventPosition,
|
|
311
|
+
(transform: Transform) => {
|
|
312
|
+
_this.handleZoom(transform, [mousedownPos, eventPosition]);
|
|
313
|
+
}
|
|
314
|
+
);
|
|
315
|
+
} else {
|
|
316
|
+
_this.options.onClick(
|
|
317
|
+
d3.event,
|
|
318
|
+
_this.xScales.invert(d3.event.offsetX),
|
|
319
|
+
_this.yScales.invert(d3.event.offsetY)
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
mousedownStatus = false;
|
|
323
|
+
mousedownPos = {} as EventPosition;
|
|
324
|
+
})
|
|
325
|
+
.on("contextmenu", function contextmenu() {
|
|
326
|
+
_this.options.onRightClick(
|
|
327
|
+
d3.event,
|
|
328
|
+
_this.xScales.invert(d3.event.offsetX),
|
|
329
|
+
_this.yScales.invert(d3.event.offsetY)
|
|
330
|
+
);
|
|
331
|
+
d3.event.preventDefault();
|
|
332
|
+
})
|
|
333
|
+
.on("touchstart", function touchmove(this: HTMLCanvasElement) {
|
|
334
|
+
// 模拟 mousedown
|
|
335
|
+
if (d3.event.touches.length > 1) {
|
|
336
|
+
mousedownStatus = true;
|
|
337
|
+
// mousedownPos = getTouchPosition(_this.eventCanvas, d3.event);
|
|
338
|
+
}
|
|
339
|
+
})
|
|
340
|
+
.on("touchmove", function touchmove(this: HTMLCanvasElement) {
|
|
341
|
+
const eventPosition = getTouchPosition(
|
|
342
|
+
_this.eventCanvas,
|
|
343
|
+
d3.event
|
|
344
|
+
) as EventPosition;
|
|
345
|
+
|
|
346
|
+
if (d3.event.touches.length === 1) {
|
|
347
|
+
_this.tooltip.draw(eventPosition, _this);
|
|
348
|
+
}
|
|
296
349
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
350
|
+
// 触发 zoom marker
|
|
351
|
+
if (mousedownStatus) {
|
|
352
|
+
mousedownPos = eventPosition;
|
|
353
|
+
mouseupPos = getTouchPosition(
|
|
354
|
+
_this.eventCanvas,
|
|
355
|
+
d3.event,
|
|
356
|
+
1
|
|
357
|
+
) as EventPosition;
|
|
358
|
+
_this.zoom.drawMarker(mousedownPos, mouseupPos);
|
|
359
|
+
}
|
|
360
|
+
})
|
|
361
|
+
.on("touchend", function mouseleave(this: HTMLCanvasElement) {
|
|
362
|
+
isMouserover = false;
|
|
363
|
+
handleMouseLeave();
|
|
364
|
+
|
|
365
|
+
// 触发 zoom
|
|
366
|
+
if (mousedownPos && mousedownPos.offsetX !== mouseupPos.offsetX) {
|
|
367
|
+
_this.zoom.clearMarker();
|
|
368
|
+
_this.zoom.onZoom(
|
|
369
|
+
mousedownPos,
|
|
370
|
+
mouseupPos,
|
|
371
|
+
(transform: Transform) => {
|
|
372
|
+
_this.handleZoom(transform, [mousedownPos, mouseupPos]);
|
|
373
|
+
}
|
|
374
|
+
);
|
|
375
|
+
} else {
|
|
376
|
+
_this.options.onClick(
|
|
377
|
+
d3.event,
|
|
378
|
+
_this.xScales.invert(d3.event.offsetX),
|
|
379
|
+
_this.yScales.invert(d3.event.offsetY)
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
mousedownStatus = false;
|
|
383
|
+
mousedownPos = {} as EventPosition;
|
|
384
|
+
mouseupPos = {} as EventPosition;
|
|
385
|
+
});
|
|
310
386
|
|
|
311
387
|
if (cascade) {
|
|
312
388
|
eventEmitter.on(plotmoveType, this.showTooltip);
|
|
@@ -314,14 +390,18 @@ export default class TsGraph {
|
|
|
314
390
|
}
|
|
315
391
|
|
|
316
392
|
// TODO: removeListener
|
|
317
|
-
window.addEventListener(
|
|
393
|
+
window.addEventListener("mouseup", () => {
|
|
318
394
|
if (!isEmpty(mousedownPos)) {
|
|
319
395
|
const eventPosition = mouseleavePos;
|
|
320
396
|
mousedownStatus = false;
|
|
321
397
|
_this.zoom.clearMarker();
|
|
322
|
-
_this.zoom.onZoom(
|
|
323
|
-
|
|
324
|
-
|
|
398
|
+
_this.zoom.onZoom(
|
|
399
|
+
mousedownPos,
|
|
400
|
+
eventPosition,
|
|
401
|
+
(transform: Transform) => {
|
|
402
|
+
_this.handleZoom(transform, [mousedownPos, eventPosition]);
|
|
403
|
+
}
|
|
404
|
+
);
|
|
325
405
|
mousedownPos = {} as EventPosition;
|
|
326
406
|
}
|
|
327
407
|
});
|
|
@@ -330,12 +410,17 @@ export default class TsGraph {
|
|
|
330
410
|
}
|
|
331
411
|
|
|
332
412
|
getZoomedSeries = () => {
|
|
333
|
-
const {
|
|
413
|
+
const {
|
|
414
|
+
chart: { renderTo },
|
|
415
|
+
series,
|
|
416
|
+
timestamp,
|
|
417
|
+
xkey,
|
|
418
|
+
} = this.options;
|
|
334
419
|
const renderToWidth = renderTo.offsetWidth;
|
|
335
420
|
return series.map((serie) => {
|
|
336
|
-
if (Object.prototype.toString.call(serie.data) ===
|
|
421
|
+
if (Object.prototype.toString.call(serie.data) === "[object Array]") {
|
|
337
422
|
const data = serie.data.filter((point) => {
|
|
338
|
-
const xVal = timestamp ===
|
|
423
|
+
const xVal = timestamp === "X" ? point[xkey] * 1000 : point[xkey];
|
|
339
424
|
const x = this.xScales(new Date(xVal));
|
|
340
425
|
return x >= 0 && x <= renderToWidth;
|
|
341
426
|
});
|
|
@@ -346,7 +431,7 @@ export default class TsGraph {
|
|
|
346
431
|
}
|
|
347
432
|
return serie;
|
|
348
433
|
});
|
|
349
|
-
}
|
|
434
|
+
};
|
|
350
435
|
|
|
351
436
|
handleResize = () => {
|
|
352
437
|
const { chart } = this.options;
|
|
@@ -359,23 +444,25 @@ export default class TsGraph {
|
|
|
359
444
|
this.options.chart.containerHeight = height;
|
|
360
445
|
this.container.style.width = `${width}px`;
|
|
361
446
|
this.container.style.height = `${height}px`;
|
|
362
|
-
this.retinaScaled(this.backCanvas,
|
|
363
|
-
this.retinaScaled(this.frontCanvas,
|
|
364
|
-
this.retinaScaled(this.eventCanvas,
|
|
447
|
+
this.retinaScaled(this.backCanvas, "back");
|
|
448
|
+
this.retinaScaled(this.frontCanvas, "front");
|
|
449
|
+
this.retinaScaled(this.eventCanvas, "event");
|
|
365
450
|
this.initScales();
|
|
366
451
|
this.initZoom();
|
|
367
452
|
this.draw(false);
|
|
368
|
-
}
|
|
453
|
+
};
|
|
369
454
|
|
|
370
455
|
handleZoom = (transform?: Transform, eventPositions?: EventPosition[]) => {
|
|
371
456
|
if (this.options.onZoomWithoutDefult) {
|
|
372
457
|
let times;
|
|
373
458
|
if (eventPositions) {
|
|
374
|
-
times = sortBy(eventPositions,
|
|
459
|
+
times = sortBy(eventPositions, "offsetX").map((pos: EventPosition) =>
|
|
460
|
+
this.xScales.invert(pos.offsetX)
|
|
461
|
+
);
|
|
375
462
|
}
|
|
376
463
|
this.options.onZoomWithoutDefult(times);
|
|
377
464
|
} else {
|
|
378
|
-
const { ykey, yAxis, ykeyFormatter } = this.options
|
|
465
|
+
const { ykey, yAxis, ykeyFormatter } = this.options;
|
|
379
466
|
let { series } = this.options;
|
|
380
467
|
if (transform) {
|
|
381
468
|
this.transform = transform;
|
|
@@ -410,7 +497,7 @@ export default class TsGraph {
|
|
|
410
497
|
this.options.onZoom(this.getZoomedSeries);
|
|
411
498
|
}
|
|
412
499
|
this.draw(false);
|
|
413
|
-
}
|
|
500
|
+
};
|
|
414
501
|
|
|
415
502
|
handleLegendItemClick = (serieName: string) => {
|
|
416
503
|
const { notDisplayedSeries } = this.options;
|
|
@@ -423,24 +510,24 @@ export default class TsGraph {
|
|
|
423
510
|
});
|
|
424
511
|
}
|
|
425
512
|
this.draw(false);
|
|
426
|
-
}
|
|
513
|
+
};
|
|
427
514
|
|
|
428
515
|
retinaScaled(canvas: HTMLCanvasElement, name: string) {
|
|
429
516
|
const { chart, ratio } = this.options;
|
|
430
|
-
const ctx = canvas.getContext(
|
|
431
|
-
const width = name ===
|
|
432
|
-
const height = name ===
|
|
433
|
-
const left = name ===
|
|
434
|
-
const top = name ===
|
|
517
|
+
const ctx = canvas.getContext("2d")!;
|
|
518
|
+
const width = name === "front" ? chart.containerWidth : chart.width;
|
|
519
|
+
const height = name === "front" ? chart.containerHeight : chart.height;
|
|
520
|
+
const left = name === "front" ? "0px" : `${chart.marginLeft}px`;
|
|
521
|
+
const top = name === "front" ? "0px" : `${chart.marginTop}px`;
|
|
435
522
|
|
|
436
523
|
canvas.width = width * ratio;
|
|
437
524
|
canvas.height = height * ratio;
|
|
438
525
|
canvas.style.width = `${width}px`;
|
|
439
526
|
canvas.style.height = `${height}px`;
|
|
440
|
-
canvas.style.position =
|
|
527
|
+
canvas.style.position = "absolute";
|
|
441
528
|
canvas.style.left = left;
|
|
442
529
|
canvas.style.top = top;
|
|
443
|
-
if (name ===
|
|
530
|
+
if (name === "front") {
|
|
444
531
|
ctx.translate(chart.marginLeft * ratio, chart.marginTop * ratio);
|
|
445
532
|
}
|
|
446
533
|
ctx.scale(ratio, ratio);
|
|
@@ -448,44 +535,54 @@ export default class TsGraph {
|
|
|
448
535
|
|
|
449
536
|
createContainer() {
|
|
450
537
|
const { chart } = this.options;
|
|
451
|
-
const container = document.createElement(
|
|
538
|
+
const container = document.createElement("div");
|
|
452
539
|
|
|
453
|
-
container.style.position =
|
|
540
|
+
container.style.position = "relative";
|
|
454
541
|
container.style.width = `${chart.containerWidth}px`;
|
|
455
542
|
container.style.height = `${chart.containerHeight}px`;
|
|
456
|
-
container.style.overflow =
|
|
457
|
-
container.className =
|
|
543
|
+
container.style.overflow = "hidden";
|
|
544
|
+
container.className = "d3-graph-container";
|
|
458
545
|
chart.renderTo.appendChild(container);
|
|
459
546
|
this.container = container;
|
|
460
547
|
}
|
|
461
548
|
|
|
462
549
|
createCanvas() {
|
|
463
|
-
const {
|
|
550
|
+
const {
|
|
551
|
+
chart: { id },
|
|
552
|
+
} = this.options;
|
|
464
553
|
|
|
465
554
|
function create(this: TsGraphType, name: string) {
|
|
466
|
-
const canvas = document.createElement(
|
|
467
|
-
const context = canvas.getContext(
|
|
555
|
+
const canvas = document.createElement("canvas");
|
|
556
|
+
const context = canvas.getContext("2d");
|
|
468
557
|
|
|
469
|
-
canvas.setAttribute(
|
|
558
|
+
canvas.setAttribute("id", `${id}-${name}Canvas`);
|
|
470
559
|
this.container.appendChild(canvas);
|
|
471
560
|
this[`${name}Canvas`] = canvas;
|
|
472
561
|
this[`${name}Context`] = context;
|
|
473
562
|
this.retinaScaled(canvas, name);
|
|
474
563
|
}
|
|
475
|
-
create.call(this,
|
|
476
|
-
create.call(this,
|
|
477
|
-
create.call(this,
|
|
564
|
+
create.call(this, "back");
|
|
565
|
+
create.call(this, "front");
|
|
566
|
+
create.call(this, "event");
|
|
478
567
|
}
|
|
479
568
|
|
|
480
569
|
initSeries() {
|
|
481
|
-
const {
|
|
570
|
+
const {
|
|
571
|
+
fillNull,
|
|
572
|
+
series,
|
|
573
|
+
xkey,
|
|
574
|
+
ykey,
|
|
575
|
+
ykey2,
|
|
576
|
+
ykeyFormatter,
|
|
577
|
+
oykey,
|
|
578
|
+
yAxis,
|
|
579
|
+
stack,
|
|
580
|
+
} = this.options;
|
|
482
581
|
let xmin = -Infinity;
|
|
483
582
|
let xmax = Infinity;
|
|
484
583
|
let ymin = yAxis.min;
|
|
485
584
|
let ymax = yAxis.max;
|
|
486
585
|
|
|
487
|
-
|
|
488
|
-
|
|
489
586
|
if (Array.isArray(series)) {
|
|
490
587
|
const newSeries: Serie[] = [];
|
|
491
588
|
series.forEach((serie, idx) => {
|
|
@@ -494,13 +591,19 @@ export default class TsGraph {
|
|
|
494
591
|
let flag = false; // 起始标记,后面有连续空值即丢掉
|
|
495
592
|
let dataIdx;
|
|
496
593
|
for (dataIdx = 0; dataIdx < data.length; dataIdx++) {
|
|
497
|
-
const ykeyVal = data[dataIdx][oykey]
|
|
594
|
+
const ykeyVal = data[dataIdx][oykey]
|
|
595
|
+
? data[dataIdx][oykey]
|
|
596
|
+
: getYkeyValue(data[dataIdx][ykey], ykeyFormatter);
|
|
498
597
|
const item = {
|
|
499
598
|
[xkey]: data[dataIdx][xkey],
|
|
500
599
|
[ykey]: ykeyVal,
|
|
501
600
|
[oykey]: ykeyVal,
|
|
502
601
|
};
|
|
503
602
|
|
|
603
|
+
if (ykey2 && ykey2 in data[dataIdx]) {
|
|
604
|
+
item[ykey2] = getYkeyValue(data[dataIdx][ykey2], ykeyFormatter);
|
|
605
|
+
}
|
|
606
|
+
|
|
504
607
|
if (stack.enabled) {
|
|
505
608
|
if (item[oykey] === undefined) {
|
|
506
609
|
item[oykey] = getYkeyValue(item[ykey], ykeyFormatter);
|
|
@@ -508,7 +611,9 @@ export default class TsGraph {
|
|
|
508
611
|
let preIdx = idx;
|
|
509
612
|
if (preIdx > 0) {
|
|
510
613
|
while (preIdx > 0) {
|
|
511
|
-
const preData = Array.isArray(series[preIdx - 1].data)
|
|
614
|
+
const preData = Array.isArray(series[preIdx - 1].data)
|
|
615
|
+
? series[preIdx - 1].data
|
|
616
|
+
: [];
|
|
512
617
|
const preItem = find(preData, (pItem: SerieDataItem) => {
|
|
513
618
|
return pItem[xkey] === item[xkey];
|
|
514
619
|
});
|
|
@@ -530,18 +635,19 @@ export default class TsGraph {
|
|
|
530
635
|
}
|
|
531
636
|
|
|
532
637
|
data[dataIdx] = item;
|
|
533
|
-
|
|
638
|
+
|
|
534
639
|
const x = item[xkey];
|
|
535
640
|
const y = item[ykey];
|
|
536
|
-
|
|
641
|
+
|
|
537
642
|
// filter is invalid points
|
|
538
643
|
if (fillNull === undefined) {
|
|
539
644
|
if (
|
|
540
|
-
!isNaN(y) &&
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
645
|
+
!isNaN(y) &&
|
|
646
|
+
(dataIdx === 0 ||
|
|
647
|
+
dataIdx === data.length - 1 ||
|
|
648
|
+
(dataIdx > 0 &&
|
|
649
|
+
dataIdx < data.length - 1 &&
|
|
650
|
+
typeof y === "number"))
|
|
545
651
|
) {
|
|
546
652
|
newData.push(item);
|
|
547
653
|
flag = true;
|
|
@@ -560,14 +666,14 @@ export default class TsGraph {
|
|
|
560
666
|
if (xmax > x) {
|
|
561
667
|
xmax = x;
|
|
562
668
|
}
|
|
563
|
-
if (typeof y ===
|
|
564
|
-
if (typeof ymin !==
|
|
669
|
+
if (typeof y === "number" && !isNaN(y)) {
|
|
670
|
+
if (typeof ymin !== "number" || y < ymin) {
|
|
565
671
|
ymin = y;
|
|
566
672
|
}
|
|
567
|
-
if (typeof ymax !==
|
|
673
|
+
if (typeof ymax !== "number" || y > ymax) {
|
|
568
674
|
ymax = y;
|
|
569
675
|
}
|
|
570
|
-
} else if (typeof fillNull ===
|
|
676
|
+
} else if (typeof fillNull === "number") {
|
|
571
677
|
if (fillNull < ymin) {
|
|
572
678
|
ymin = fillNull;
|
|
573
679
|
}
|
|
@@ -576,7 +682,7 @@ export default class TsGraph {
|
|
|
576
682
|
}
|
|
577
683
|
}
|
|
578
684
|
}
|
|
579
|
-
|
|
685
|
+
|
|
580
686
|
newSeries.push({
|
|
581
687
|
...serie,
|
|
582
688
|
data: newData,
|
|
@@ -601,15 +707,30 @@ export default class TsGraph {
|
|
|
601
707
|
}
|
|
602
708
|
|
|
603
709
|
initTooltip() {
|
|
604
|
-
this.tooltip = new Tooltip(
|
|
710
|
+
this.tooltip = new Tooltip(
|
|
711
|
+
this.options,
|
|
712
|
+
this.frontContext,
|
|
713
|
+
this.container,
|
|
714
|
+
this.eventCanvas
|
|
715
|
+
);
|
|
605
716
|
}
|
|
606
717
|
|
|
607
718
|
initZoom() {
|
|
608
|
-
this.zoom = new Zoom(
|
|
719
|
+
this.zoom = new Zoom(
|
|
720
|
+
this.options,
|
|
721
|
+
this.handleZoom,
|
|
722
|
+
this.container,
|
|
723
|
+
this.eventCanvas
|
|
724
|
+
);
|
|
609
725
|
}
|
|
610
726
|
|
|
611
727
|
initLegend() {
|
|
612
|
-
this.legend = new Legend(
|
|
728
|
+
this.legend = new Legend(
|
|
729
|
+
this.options,
|
|
730
|
+
this.handleLegendItemClick,
|
|
731
|
+
this.container,
|
|
732
|
+
this.eventCanvas
|
|
733
|
+
);
|
|
613
734
|
}
|
|
614
735
|
|
|
615
736
|
initScales() {
|
|
@@ -633,14 +754,17 @@ export default class TsGraph {
|
|
|
633
754
|
|
|
634
755
|
offEvents() {
|
|
635
756
|
const { cascade, cascadeScope } = this.options.tooltip;
|
|
636
|
-
const plotmoveType = cascadeScope ? `plotmove:${cascadeScope}` :
|
|
637
|
-
const plotleaveType = cascadeScope
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
.on(
|
|
643
|
-
.on(
|
|
757
|
+
const plotmoveType = cascadeScope ? `plotmove:${cascadeScope}` : "plotmove";
|
|
758
|
+
const plotleaveType = cascadeScope
|
|
759
|
+
? `plotleave:${cascadeScope}`
|
|
760
|
+
: "plotleave";
|
|
761
|
+
|
|
762
|
+
d3.select(this.eventCanvas)
|
|
763
|
+
.on("mousemove", null)
|
|
764
|
+
.on("mouseleave", null)
|
|
765
|
+
.on("mousedown", null)
|
|
766
|
+
.on("mouseup", null)
|
|
767
|
+
.on("touchmove", null);
|
|
644
768
|
removeListener(this.options.chart.renderTo, this.handleResize);
|
|
645
769
|
|
|
646
770
|
if (cascade) {
|