@fc-plot/ts-graph 0.16.3 → 0.17.0
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 +39 -23
- package/package.json +1 -1
- package/src/getNearestPoints.ts +36 -17
- package/src/index.ts +294 -168
- package/src/interface.ts +164 -160
- package/src/line.ts +51 -26
- package/src/tooltip.ts +125 -63
- 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,13 +234,14 @@ 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();
|
|
244
|
+
this.initShapes();
|
|
205
245
|
this.legend.updateOptions(options);
|
|
206
246
|
this.draw();
|
|
207
247
|
}
|
|
@@ -212,16 +252,18 @@ export default class TsGraph {
|
|
|
212
252
|
|
|
213
253
|
showTooltip = (eventPosition: EventPosition) => {
|
|
214
254
|
this.tooltip.draw(eventPosition, this);
|
|
215
|
-
}
|
|
255
|
+
};
|
|
216
256
|
|
|
217
257
|
hideTooltip = () => {
|
|
218
258
|
this.tooltip.clear();
|
|
219
|
-
}
|
|
259
|
+
};
|
|
220
260
|
|
|
221
261
|
initEvent() {
|
|
222
262
|
const { cascade, cascadeScope } = this.options.tooltip;
|
|
223
|
-
const plotmoveType = cascadeScope ? `plotmove:${cascadeScope}` :
|
|
224
|
-
const plotleaveType = cascadeScope
|
|
263
|
+
const plotmoveType = cascadeScope ? `plotmove:${cascadeScope}` : "plotmove";
|
|
264
|
+
const plotleaveType = cascadeScope
|
|
265
|
+
? `plotleave:${cascadeScope}`
|
|
266
|
+
: "plotleave";
|
|
225
267
|
let mousedownStatus = false;
|
|
226
268
|
let mousedownPos: EventPosition = {} as EventPosition;
|
|
227
269
|
let mouseupPos: EventPosition = {} as EventPosition;
|
|
@@ -240,72 +282,107 @@ export default class TsGraph {
|
|
|
240
282
|
const handleMouseLeave = throttle(() => {
|
|
241
283
|
_this.tooltip.clear();
|
|
242
284
|
}, 5);
|
|
243
|
-
d3.select(this.eventCanvas)
|
|
244
|
-
|
|
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
|
-
|
|
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
|
+
}
|
|
295
349
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
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
|
+
});
|
|
309
386
|
|
|
310
387
|
if (cascade) {
|
|
311
388
|
eventEmitter.on(plotmoveType, this.showTooltip);
|
|
@@ -313,14 +390,18 @@ export default class TsGraph {
|
|
|
313
390
|
}
|
|
314
391
|
|
|
315
392
|
// TODO: removeListener
|
|
316
|
-
window.addEventListener(
|
|
393
|
+
window.addEventListener("mouseup", () => {
|
|
317
394
|
if (!isEmpty(mousedownPos)) {
|
|
318
395
|
const eventPosition = mouseleavePos;
|
|
319
396
|
mousedownStatus = false;
|
|
320
397
|
_this.zoom.clearMarker();
|
|
321
|
-
_this.zoom.onZoom(
|
|
322
|
-
|
|
323
|
-
|
|
398
|
+
_this.zoom.onZoom(
|
|
399
|
+
mousedownPos,
|
|
400
|
+
eventPosition,
|
|
401
|
+
(transform: Transform) => {
|
|
402
|
+
_this.handleZoom(transform, [mousedownPos, eventPosition]);
|
|
403
|
+
}
|
|
404
|
+
);
|
|
324
405
|
mousedownPos = {} as EventPosition;
|
|
325
406
|
}
|
|
326
407
|
});
|
|
@@ -329,12 +410,17 @@ export default class TsGraph {
|
|
|
329
410
|
}
|
|
330
411
|
|
|
331
412
|
getZoomedSeries = () => {
|
|
332
|
-
const {
|
|
413
|
+
const {
|
|
414
|
+
chart: { renderTo },
|
|
415
|
+
series,
|
|
416
|
+
timestamp,
|
|
417
|
+
xkey,
|
|
418
|
+
} = this.options;
|
|
333
419
|
const renderToWidth = renderTo.offsetWidth;
|
|
334
420
|
return series.map((serie) => {
|
|
335
|
-
if (Object.prototype.toString.call(serie.data) ===
|
|
421
|
+
if (Object.prototype.toString.call(serie.data) === "[object Array]") {
|
|
336
422
|
const data = serie.data.filter((point) => {
|
|
337
|
-
const xVal = timestamp ===
|
|
423
|
+
const xVal = timestamp === "X" ? point[xkey] * 1000 : point[xkey];
|
|
338
424
|
const x = this.xScales(new Date(xVal));
|
|
339
425
|
return x >= 0 && x <= renderToWidth;
|
|
340
426
|
});
|
|
@@ -345,7 +431,7 @@ export default class TsGraph {
|
|
|
345
431
|
}
|
|
346
432
|
return serie;
|
|
347
433
|
});
|
|
348
|
-
}
|
|
434
|
+
};
|
|
349
435
|
|
|
350
436
|
handleResize = () => {
|
|
351
437
|
const { chart } = this.options;
|
|
@@ -358,23 +444,25 @@ export default class TsGraph {
|
|
|
358
444
|
this.options.chart.containerHeight = height;
|
|
359
445
|
this.container.style.width = `${width}px`;
|
|
360
446
|
this.container.style.height = `${height}px`;
|
|
361
|
-
this.retinaScaled(this.backCanvas,
|
|
362
|
-
this.retinaScaled(this.frontCanvas,
|
|
363
|
-
this.retinaScaled(this.eventCanvas,
|
|
447
|
+
this.retinaScaled(this.backCanvas, "back");
|
|
448
|
+
this.retinaScaled(this.frontCanvas, "front");
|
|
449
|
+
this.retinaScaled(this.eventCanvas, "event");
|
|
364
450
|
this.initScales();
|
|
365
451
|
this.initZoom();
|
|
366
452
|
this.draw(false);
|
|
367
|
-
}
|
|
453
|
+
};
|
|
368
454
|
|
|
369
455
|
handleZoom = (transform?: Transform, eventPositions?: EventPosition[]) => {
|
|
370
456
|
if (this.options.onZoomWithoutDefult) {
|
|
371
457
|
let times;
|
|
372
458
|
if (eventPositions) {
|
|
373
|
-
times = sortBy(eventPositions,
|
|
459
|
+
times = sortBy(eventPositions, "offsetX").map((pos: EventPosition) =>
|
|
460
|
+
this.xScales.invert(pos.offsetX)
|
|
461
|
+
);
|
|
374
462
|
}
|
|
375
463
|
this.options.onZoomWithoutDefult(times);
|
|
376
464
|
} else {
|
|
377
|
-
const { ykey, yAxis, ykeyFormatter } = this.options
|
|
465
|
+
const { ykey, yAxis, ykeyFormatter } = this.options;
|
|
378
466
|
let { series } = this.options;
|
|
379
467
|
if (transform) {
|
|
380
468
|
this.transform = transform;
|
|
@@ -409,7 +497,7 @@ export default class TsGraph {
|
|
|
409
497
|
this.options.onZoom(this.getZoomedSeries);
|
|
410
498
|
}
|
|
411
499
|
this.draw(false);
|
|
412
|
-
}
|
|
500
|
+
};
|
|
413
501
|
|
|
414
502
|
handleLegendItemClick = (serieName: string) => {
|
|
415
503
|
const { notDisplayedSeries } = this.options;
|
|
@@ -422,24 +510,24 @@ export default class TsGraph {
|
|
|
422
510
|
});
|
|
423
511
|
}
|
|
424
512
|
this.draw(false);
|
|
425
|
-
}
|
|
513
|
+
};
|
|
426
514
|
|
|
427
515
|
retinaScaled(canvas: HTMLCanvasElement, name: string) {
|
|
428
516
|
const { chart, ratio } = this.options;
|
|
429
|
-
const ctx = canvas.getContext(
|
|
430
|
-
const width = name ===
|
|
431
|
-
const height = name ===
|
|
432
|
-
const left = name ===
|
|
433
|
-
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`;
|
|
434
522
|
|
|
435
523
|
canvas.width = width * ratio;
|
|
436
524
|
canvas.height = height * ratio;
|
|
437
525
|
canvas.style.width = `${width}px`;
|
|
438
526
|
canvas.style.height = `${height}px`;
|
|
439
|
-
canvas.style.position =
|
|
527
|
+
canvas.style.position = "absolute";
|
|
440
528
|
canvas.style.left = left;
|
|
441
529
|
canvas.style.top = top;
|
|
442
|
-
if (name ===
|
|
530
|
+
if (name === "front") {
|
|
443
531
|
ctx.translate(chart.marginLeft * ratio, chart.marginTop * ratio);
|
|
444
532
|
}
|
|
445
533
|
ctx.scale(ratio, ratio);
|
|
@@ -447,44 +535,54 @@ export default class TsGraph {
|
|
|
447
535
|
|
|
448
536
|
createContainer() {
|
|
449
537
|
const { chart } = this.options;
|
|
450
|
-
const container = document.createElement(
|
|
538
|
+
const container = document.createElement("div");
|
|
451
539
|
|
|
452
|
-
container.style.position =
|
|
540
|
+
container.style.position = "relative";
|
|
453
541
|
container.style.width = `${chart.containerWidth}px`;
|
|
454
542
|
container.style.height = `${chart.containerHeight}px`;
|
|
455
|
-
container.style.overflow =
|
|
456
|
-
container.className =
|
|
543
|
+
container.style.overflow = "hidden";
|
|
544
|
+
container.className = "d3-graph-container";
|
|
457
545
|
chart.renderTo.appendChild(container);
|
|
458
546
|
this.container = container;
|
|
459
547
|
}
|
|
460
548
|
|
|
461
549
|
createCanvas() {
|
|
462
|
-
const {
|
|
550
|
+
const {
|
|
551
|
+
chart: { id },
|
|
552
|
+
} = this.options;
|
|
463
553
|
|
|
464
554
|
function create(this: TsGraphType, name: string) {
|
|
465
|
-
const canvas = document.createElement(
|
|
466
|
-
const context = canvas.getContext(
|
|
555
|
+
const canvas = document.createElement("canvas");
|
|
556
|
+
const context = canvas.getContext("2d");
|
|
467
557
|
|
|
468
|
-
canvas.setAttribute(
|
|
558
|
+
canvas.setAttribute("id", `${id}-${name}Canvas`);
|
|
469
559
|
this.container.appendChild(canvas);
|
|
470
560
|
this[`${name}Canvas`] = canvas;
|
|
471
561
|
this[`${name}Context`] = context;
|
|
472
562
|
this.retinaScaled(canvas, name);
|
|
473
563
|
}
|
|
474
|
-
create.call(this,
|
|
475
|
-
create.call(this,
|
|
476
|
-
create.call(this,
|
|
564
|
+
create.call(this, "back");
|
|
565
|
+
create.call(this, "front");
|
|
566
|
+
create.call(this, "event");
|
|
477
567
|
}
|
|
478
568
|
|
|
479
569
|
initSeries() {
|
|
480
|
-
const {
|
|
570
|
+
const {
|
|
571
|
+
fillNull,
|
|
572
|
+
series,
|
|
573
|
+
xkey,
|
|
574
|
+
ykey,
|
|
575
|
+
ykey2,
|
|
576
|
+
ykeyFormatter,
|
|
577
|
+
oykey,
|
|
578
|
+
yAxis,
|
|
579
|
+
stack,
|
|
580
|
+
} = this.options;
|
|
481
581
|
let xmin = -Infinity;
|
|
482
582
|
let xmax = Infinity;
|
|
483
583
|
let ymin = yAxis.min;
|
|
484
584
|
let ymax = yAxis.max;
|
|
485
585
|
|
|
486
|
-
|
|
487
|
-
|
|
488
586
|
if (Array.isArray(series)) {
|
|
489
587
|
const newSeries: Serie[] = [];
|
|
490
588
|
series.forEach((serie, idx) => {
|
|
@@ -493,13 +591,19 @@ export default class TsGraph {
|
|
|
493
591
|
let flag = false; // 起始标记,后面有连续空值即丢掉
|
|
494
592
|
let dataIdx;
|
|
495
593
|
for (dataIdx = 0; dataIdx < data.length; dataIdx++) {
|
|
496
|
-
const ykeyVal = data[dataIdx][oykey]
|
|
594
|
+
const ykeyVal = data[dataIdx][oykey]
|
|
595
|
+
? data[dataIdx][oykey]
|
|
596
|
+
: getYkeyValue(data[dataIdx][ykey], ykeyFormatter);
|
|
497
597
|
const item = {
|
|
498
598
|
[xkey]: data[dataIdx][xkey],
|
|
499
599
|
[ykey]: ykeyVal,
|
|
500
600
|
[oykey]: ykeyVal,
|
|
501
601
|
};
|
|
502
602
|
|
|
603
|
+
if (ykey2 && ykey2 in data[dataIdx]) {
|
|
604
|
+
item[ykey2] = data[dataIdx][ykey2];
|
|
605
|
+
}
|
|
606
|
+
|
|
503
607
|
if (stack.enabled) {
|
|
504
608
|
if (item[oykey] === undefined) {
|
|
505
609
|
item[oykey] = getYkeyValue(item[ykey], ykeyFormatter);
|
|
@@ -507,7 +611,9 @@ export default class TsGraph {
|
|
|
507
611
|
let preIdx = idx;
|
|
508
612
|
if (preIdx > 0) {
|
|
509
613
|
while (preIdx > 0) {
|
|
510
|
-
const preData = Array.isArray(series[preIdx - 1].data)
|
|
614
|
+
const preData = Array.isArray(series[preIdx - 1].data)
|
|
615
|
+
? series[preIdx - 1].data
|
|
616
|
+
: [];
|
|
511
617
|
const preItem = find(preData, (pItem: SerieDataItem) => {
|
|
512
618
|
return pItem[xkey] === item[xkey];
|
|
513
619
|
});
|
|
@@ -529,17 +635,19 @@ export default class TsGraph {
|
|
|
529
635
|
}
|
|
530
636
|
|
|
531
637
|
data[dataIdx] = item;
|
|
532
|
-
|
|
638
|
+
|
|
533
639
|
const x = item[xkey];
|
|
534
640
|
const y = item[ykey];
|
|
535
|
-
|
|
536
641
|
|
|
537
642
|
// filter is invalid points
|
|
538
643
|
if (fillNull === undefined) {
|
|
539
644
|
if (
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
645
|
+
!isNaN(y) &&
|
|
646
|
+
(dataIdx === 0 ||
|
|
647
|
+
dataIdx === data.length - 1 ||
|
|
648
|
+
(dataIdx > 0 &&
|
|
649
|
+
dataIdx < data.length - 1 &&
|
|
650
|
+
typeof y === "number"))
|
|
543
651
|
) {
|
|
544
652
|
newData.push(item);
|
|
545
653
|
flag = true;
|
|
@@ -558,14 +666,14 @@ export default class TsGraph {
|
|
|
558
666
|
if (xmax > x) {
|
|
559
667
|
xmax = x;
|
|
560
668
|
}
|
|
561
|
-
if (typeof y ===
|
|
562
|
-
if (typeof ymin !==
|
|
669
|
+
if (typeof y === "number" && !isNaN(y)) {
|
|
670
|
+
if (typeof ymin !== "number" || y < ymin) {
|
|
563
671
|
ymin = y;
|
|
564
672
|
}
|
|
565
|
-
if (typeof ymax !==
|
|
673
|
+
if (typeof ymax !== "number" || y > ymax) {
|
|
566
674
|
ymax = y;
|
|
567
675
|
}
|
|
568
|
-
} else if (typeof fillNull ===
|
|
676
|
+
} else if (typeof fillNull === "number") {
|
|
569
677
|
if (fillNull < ymin) {
|
|
570
678
|
ymin = fillNull;
|
|
571
679
|
}
|
|
@@ -574,7 +682,7 @@ export default class TsGraph {
|
|
|
574
682
|
}
|
|
575
683
|
}
|
|
576
684
|
}
|
|
577
|
-
|
|
685
|
+
|
|
578
686
|
newSeries.push({
|
|
579
687
|
...serie,
|
|
580
688
|
data: newData,
|
|
@@ -599,15 +707,30 @@ export default class TsGraph {
|
|
|
599
707
|
}
|
|
600
708
|
|
|
601
709
|
initTooltip() {
|
|
602
|
-
this.tooltip = new Tooltip(
|
|
710
|
+
this.tooltip = new Tooltip(
|
|
711
|
+
this.options,
|
|
712
|
+
this.frontContext,
|
|
713
|
+
this.container,
|
|
714
|
+
this.eventCanvas
|
|
715
|
+
);
|
|
603
716
|
}
|
|
604
717
|
|
|
605
718
|
initZoom() {
|
|
606
|
-
this.zoom = new Zoom(
|
|
719
|
+
this.zoom = new Zoom(
|
|
720
|
+
this.options,
|
|
721
|
+
this.handleZoom,
|
|
722
|
+
this.container,
|
|
723
|
+
this.eventCanvas
|
|
724
|
+
);
|
|
607
725
|
}
|
|
608
726
|
|
|
609
727
|
initLegend() {
|
|
610
|
-
this.legend = new Legend(
|
|
728
|
+
this.legend = new Legend(
|
|
729
|
+
this.options,
|
|
730
|
+
this.handleLegendItemClick,
|
|
731
|
+
this.container,
|
|
732
|
+
this.eventCanvas
|
|
733
|
+
);
|
|
611
734
|
}
|
|
612
735
|
|
|
613
736
|
initScales() {
|
|
@@ -631,14 +754,17 @@ export default class TsGraph {
|
|
|
631
754
|
|
|
632
755
|
offEvents() {
|
|
633
756
|
const { cascade, cascadeScope } = this.options.tooltip;
|
|
634
|
-
const plotmoveType = cascadeScope ? `plotmove:${cascadeScope}` :
|
|
635
|
-
const plotleaveType = cascadeScope
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
.on(
|
|
641
|
-
.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);
|
|
642
768
|
removeListener(this.options.chart.renderTo, this.handleResize);
|
|
643
769
|
|
|
644
770
|
if (cascade) {
|