@orbcharts/plugins-basic 3.0.10 → 3.0.11
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/orbcharts-plugins-basic.es.js +5499 -5405
- package/dist/orbcharts-plugins-basic.umd.js +50 -50
- package/package.json +4 -4
- package/src/series/defaults.ts +4 -2
- package/src/series/plugins/Indicator.ts +296 -58
- package/src/series/plugins/Pie.ts +107 -3
- package/src/series/plugins/PieEventTexts.ts +35 -14
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@orbcharts/plugins-basic",
|
3
|
-
"version": "3.0.
|
3
|
+
"version": "3.0.11",
|
4
4
|
"description": "Plugins for OrbCharts",
|
5
5
|
"author": "Blue Planet Inc.",
|
6
6
|
"license": "Apache-2.0",
|
@@ -39,9 +39,9 @@
|
|
39
39
|
"vite-plugin-dts": "^3.7.3"
|
40
40
|
},
|
41
41
|
"dependencies": {
|
42
|
-
"@orbcharts/core": "^3.0.
|
43
|
-
"@orbcharts/core-types": "^3.0.
|
44
|
-
"@orbcharts/plugins-basic-types": "^3.0.
|
42
|
+
"@orbcharts/core": "^3.0.7",
|
43
|
+
"@orbcharts/core-types": "^3.0.5",
|
44
|
+
"@orbcharts/plugins-basic-types": "^3.0.5",
|
45
45
|
"d3": "^7.8.5",
|
46
46
|
"rxjs": "^7.8.1"
|
47
47
|
}
|
package/src/series/defaults.ts
CHANGED
@@ -230,7 +230,9 @@ export const DEFAULT_INDICATOR_PARAMS: IndicatorParams = {
|
|
230
230
|
startAngle: - Math.PI / 2,
|
231
231
|
endAngle: Math.PI / 2,
|
232
232
|
radius: 0.6,
|
233
|
-
|
233
|
+
indicatorType: 'needle',
|
234
|
+
size: 10,
|
234
235
|
colorType: 'label',
|
235
|
-
|
236
|
+
// autoHighlight: false,
|
237
|
+
value: 0,
|
236
238
|
}
|
@@ -2,17 +2,22 @@ import * as d3 from 'd3'
|
|
2
2
|
import {
|
3
3
|
combineLatest,
|
4
4
|
switchMap,
|
5
|
+
mergeMap,
|
6
|
+
mergeWith,
|
7
|
+
concatMap,
|
5
8
|
first,
|
9
|
+
filter,
|
6
10
|
map,
|
7
11
|
takeUntil,
|
8
12
|
Observable,
|
9
13
|
distinctUntilChanged,
|
10
14
|
Subject,
|
11
15
|
BehaviorSubject } from 'rxjs'
|
12
|
-
import type { DefinePluginConfig } from '../../../lib/core-types'
|
16
|
+
import type { ContextSubject, DefinePluginConfig } from '../../../lib/core-types'
|
13
17
|
import {
|
14
18
|
defineSeriesPlugin } from '../../../lib/core'
|
15
19
|
import type {
|
20
|
+
ComputedDataSeries,
|
16
21
|
ComputedDatumSeries,
|
17
22
|
ContainerPosition,
|
18
23
|
EventSeries,
|
@@ -23,16 +28,33 @@ import type { IndicatorParams } from '../../../lib/plugins-basic-types'
|
|
23
28
|
import { DEFAULT_INDICATOR_PARAMS } from '../defaults'
|
24
29
|
import { getDatumColor, getClassName } from '../../utils/orbchartsUtils'
|
25
30
|
import { seriesCenterSelectionObservable } from '../seriesObservables'
|
26
|
-
import {
|
31
|
+
import { LAYER_INDEX_OF_GRAPHIC_COVER } from '../../const'
|
32
|
+
|
33
|
+
interface RenderParams {
|
34
|
+
containerSelection: d3.Selection<SVGGElement, any, any, unknown>
|
35
|
+
angle: number
|
36
|
+
value: number
|
37
|
+
datum: ComputedDatumSeries | null
|
38
|
+
computedData: ComputedDataSeries
|
39
|
+
SeriesDataMap: Map<string, ComputedDatumSeries[]>
|
40
|
+
pointerDistance: number
|
41
|
+
fullParams: IndicatorParams
|
42
|
+
fullChartParams: ChartParams
|
43
|
+
graphicColor: string
|
44
|
+
event$: Subject<EventSeries>
|
45
|
+
}
|
27
46
|
|
28
47
|
const pluginName = 'Indicator'
|
29
48
|
const indicatorGClassName = getClassName(pluginName, 'indicator-g')
|
30
49
|
const triangleGClassName = getClassName(pluginName, 'triangle-g')
|
50
|
+
const lineGClassName = getClassName(pluginName, 'line-g')
|
51
|
+
const needleGClassName = getClassName(pluginName, 'needle-g')
|
52
|
+
const pinGClassName = getClassName(pluginName, 'pin-g')
|
31
53
|
|
32
54
|
const pluginConfig: DefinePluginConfig<typeof pluginName, typeof DEFAULT_INDICATOR_PARAMS> = {
|
33
55
|
name: pluginName,
|
34
56
|
defaultParams: DEFAULT_INDICATOR_PARAMS,
|
35
|
-
layerIndex:
|
57
|
+
layerIndex: LAYER_INDEX_OF_GRAPHIC_COVER,
|
36
58
|
validator: (params, { validateColumns }) => {
|
37
59
|
const result = validateColumns(params, {
|
38
60
|
startAngle: {
|
@@ -44,12 +66,19 @@ const pluginConfig: DefinePluginConfig<typeof pluginName, typeof DEFAULT_INDICAT
|
|
44
66
|
radius: {
|
45
67
|
toBeTypes: ['number'],
|
46
68
|
},
|
69
|
+
indicatorType: {
|
70
|
+
toBe: '"line" | "needle" | "pin" | "triangle"',
|
71
|
+
test: (value: any) => ['line', 'needle', 'pin', 'triangle'].includes(value)
|
72
|
+
},
|
47
73
|
size: {
|
48
74
|
toBeTypes: ['number'],
|
49
75
|
},
|
50
76
|
colorType: {
|
51
77
|
toBeOption: 'ColorType'
|
52
78
|
},
|
79
|
+
// autoHighlight: {
|
80
|
+
// toBeTypes: ['boolean'],
|
81
|
+
// },
|
53
82
|
value: {
|
54
83
|
toBeTypes: ['number'],
|
55
84
|
},
|
@@ -58,15 +87,17 @@ const pluginConfig: DefinePluginConfig<typeof pluginName, typeof DEFAULT_INDICAT
|
|
58
87
|
}
|
59
88
|
}
|
60
89
|
|
61
|
-
function
|
90
|
+
function createIndicatorG({ containerSelection, angle, datum, value, computedData, SeriesDataMap, fullParams, fullChartParams, event$ }: {
|
62
91
|
containerSelection: d3.Selection<SVGGElement, any, any, unknown>
|
63
92
|
angle: number
|
64
|
-
|
93
|
+
datum: ComputedDatumSeries | null
|
94
|
+
value: number
|
95
|
+
computedData: ComputedDataSeries
|
96
|
+
SeriesDataMap: Map<string, ComputedDatumSeries[]>
|
65
97
|
fullParams: IndicatorParams
|
66
98
|
fullChartParams: ChartParams
|
67
|
-
|
99
|
+
event$: Subject<EventSeries>
|
68
100
|
}) {
|
69
|
-
|
70
101
|
const indicatorG = containerSelection.selectAll(`g.${indicatorGClassName}`)
|
71
102
|
.data([angle])
|
72
103
|
.join(
|
@@ -77,11 +108,56 @@ function renderIndicatorTriangle ({ containerSelection, angle, pointerDistance,
|
|
77
108
|
exit => exit.remove()
|
78
109
|
)
|
79
110
|
|
80
|
-
indicatorG
|
111
|
+
const transitionG = indicatorG
|
81
112
|
.transition()
|
82
113
|
.duration(fullChartParams.transitionDuration)
|
83
114
|
.attr('transform', `rotate(${angle})`)
|
84
115
|
|
116
|
+
const series = SeriesDataMap.get(datum.seriesLabel)!
|
117
|
+
|
118
|
+
// work around(暫時使用這個方式來共享value)
|
119
|
+
transitionG
|
120
|
+
.tween('move', (self, t) => {
|
121
|
+
return (t) => {
|
122
|
+
event$.next({
|
123
|
+
type: 'series',
|
124
|
+
pluginName,
|
125
|
+
eventName: 'transitionMove',
|
126
|
+
event: undefined,
|
127
|
+
highlightTarget: fullChartParams.highlightTarget,
|
128
|
+
datum: datum,
|
129
|
+
series: series,
|
130
|
+
seriesIndex: datum.seriesIndex,
|
131
|
+
seriesLabel: datum.seriesLabel,
|
132
|
+
data: computedData,
|
133
|
+
mark: value, // work around
|
134
|
+
tween: t
|
135
|
+
})
|
136
|
+
}
|
137
|
+
})
|
138
|
+
.on('end', (self, t) => {
|
139
|
+
event$.next({
|
140
|
+
type: 'series',
|
141
|
+
pluginName,
|
142
|
+
eventName: 'transitionEnd',
|
143
|
+
event: undefined,
|
144
|
+
highlightTarget: fullChartParams.highlightTarget,
|
145
|
+
datum: datum,
|
146
|
+
series: series,
|
147
|
+
seriesIndex: datum.seriesIndex,
|
148
|
+
seriesLabel: datum.seriesLabel,
|
149
|
+
data: computedData,
|
150
|
+
mark: value // work around
|
151
|
+
})
|
152
|
+
})
|
153
|
+
|
154
|
+
return indicatorG
|
155
|
+
}
|
156
|
+
|
157
|
+
function renderIndicatorTriangle ({ containerSelection, angle, value, datum, computedData, SeriesDataMap, pointerDistance, fullParams, fullChartParams, graphicColor, event$ }: RenderParams) {
|
158
|
+
|
159
|
+
const indicatorG = createIndicatorG({ containerSelection, angle, value, datum, computedData, SeriesDataMap, fullParams, fullChartParams, event$ })
|
160
|
+
|
85
161
|
indicatorG
|
86
162
|
.selectAll(`g.${triangleGClassName}`)
|
87
163
|
.data([pointerDistance])
|
@@ -98,16 +174,96 @@ function renderIndicatorTriangle ({ containerSelection, angle, pointerDistance,
|
|
98
174
|
.attr('fill', graphicColor)
|
99
175
|
}
|
100
176
|
|
177
|
+
function renderIndicatorLine ({ containerSelection, angle, value, datum, computedData, SeriesDataMap, pointerDistance, fullParams, fullChartParams, graphicColor, event$ }: RenderParams) {
|
178
|
+
const indicatorG = createIndicatorG({ containerSelection, angle, value, datum, computedData, SeriesDataMap, fullParams, fullChartParams, event$ })
|
179
|
+
|
180
|
+
indicatorG
|
181
|
+
.selectAll(`g.${lineGClassName}`)
|
182
|
+
.data([pointerDistance])
|
183
|
+
.join('g')
|
184
|
+
.attr('class', lineGClassName)
|
185
|
+
.selectAll('rect')
|
186
|
+
.data([fullParams.size])
|
187
|
+
.join('rect')
|
188
|
+
.attr('x', -fullParams.size / 2) // 水平置中
|
189
|
+
.attr('y', -pointerDistance) // 從中心向上延伸
|
190
|
+
.attr('width', fullParams.size) // 寬度為 size
|
191
|
+
.attr('height', pointerDistance) // 長度為 pointerDistance
|
192
|
+
.attr('fill', graphicColor)
|
193
|
+
}
|
194
|
+
|
195
|
+
function renderIndicatorNeedle ({ containerSelection, angle, value, datum, computedData, SeriesDataMap, pointerDistance, fullParams, fullChartParams, graphicColor, event$ }: RenderParams) {
|
196
|
+
const indicatorG = createIndicatorG({ containerSelection, angle, value, datum, computedData, SeriesDataMap, fullParams, fullChartParams, event$ })
|
197
|
+
|
198
|
+
indicatorG
|
199
|
+
.selectAll(`g.${needleGClassName}`)
|
200
|
+
.data([pointerDistance])
|
201
|
+
.join('g')
|
202
|
+
.attr('class', needleGClassName)
|
203
|
+
.selectAll('path')
|
204
|
+
.data([fullParams.size])
|
205
|
+
.join('path')
|
206
|
+
.attr('d', () => {
|
207
|
+
const width = fullParams.size
|
208
|
+
|
209
|
+
// 建構4角菱形路徑
|
210
|
+
const points = [
|
211
|
+
[0, -pointerDistance], // 頂點(針尖)
|
212
|
+
[width / 2, 0], // 右側最寬點
|
213
|
+
[0, width / 2], // 尾部
|
214
|
+
[-width / 2, 0] // 左側最寬點
|
215
|
+
]
|
216
|
+
|
217
|
+
return `M${points.map(p => p.join(',')).join('L')}Z`
|
218
|
+
})
|
219
|
+
.attr('fill', graphicColor)
|
220
|
+
}
|
221
|
+
|
222
|
+
function renderIndicatorPin ({ containerSelection, angle, value, datum, computedData, SeriesDataMap, pointerDistance, fullParams, fullChartParams, graphicColor, event$ }: RenderParams) {
|
223
|
+
const indicatorG = createIndicatorG({ containerSelection, angle, value, datum, computedData, SeriesDataMap, fullParams, fullChartParams, event$ })
|
224
|
+
|
225
|
+
const pinG = indicatorG
|
226
|
+
.selectAll(`g.${pinGClassName}`)
|
227
|
+
.data([pointerDistance])
|
228
|
+
.join('g')
|
229
|
+
.attr('class', pinGClassName)
|
230
|
+
|
231
|
+
// 繪製大頭針的針身(細線)- 從中心向外延伸
|
232
|
+
pinG
|
233
|
+
.selectAll('line.pin-shaft')
|
234
|
+
.data([1])
|
235
|
+
.join('line')
|
236
|
+
.attr('class', 'pin-shaft')
|
237
|
+
.attr('x1', 0)
|
238
|
+
.attr('y1', 0) // 從中心開始
|
239
|
+
.attr('x2', 0)
|
240
|
+
.attr('y2', -pointerDistance) // 向外延伸到 pointerDistance
|
241
|
+
.attr('stroke', graphicColor)
|
242
|
+
.attr('stroke-width', Math.min(fullParams.size * 0.2, 2)) // 針身較細
|
243
|
+
.attr('stroke-linecap', 'round')
|
244
|
+
|
245
|
+
// 繪製大頭針的頭部(圓形)- 放在中心位置
|
246
|
+
pinG
|
247
|
+
.selectAll('circle.pin-head')
|
248
|
+
.data([fullParams.size])
|
249
|
+
.join('circle')
|
250
|
+
.attr('class', 'pin-head')
|
251
|
+
.attr('cx', 0)
|
252
|
+
.attr('cy', 0) // 頭部在中心
|
253
|
+
.attr('r', fullParams.size / 2) // 頭部半徑為 size 的一半
|
254
|
+
.attr('fill', graphicColor)
|
255
|
+
}
|
256
|
+
|
101
257
|
function createEachGraphic (pluginName: string, context: {
|
102
258
|
containerSelection: d3.Selection<SVGGElement, any, any, unknown>
|
103
|
-
|
259
|
+
renderFn: (params: RenderParams) => void
|
104
260
|
containerVisibleComputedSortedData$: Observable<ComputedDatumSeries[]>
|
105
|
-
// SeriesDataMap$: Observable<Map<string, ComputedDatumSeries[]>>
|
106
261
|
fullParams$: Observable<IndicatorParams>
|
107
262
|
fullChartParams$: Observable<ChartParams>
|
108
|
-
// textSizePx$: Observable<number>
|
109
|
-
// seriesHighlight$: Observable<ComputedDatumSeries[]>
|
110
263
|
seriesContainerPosition$: Observable<ContainerPosition>
|
264
|
+
computedData$: Observable<ComputedDatumSeries[][]>
|
265
|
+
SeriesDataMap$: Observable<Map<string, ComputedDatumSeries[]>>
|
266
|
+
subject: ContextSubject<"series">
|
111
267
|
event$: Subject<EventSeries>
|
112
268
|
}) {
|
113
269
|
const destroy$ = new Subject()
|
@@ -119,7 +275,7 @@ function createEachGraphic (pluginName: string, context: {
|
|
119
275
|
distinctUntilChanged()
|
120
276
|
)
|
121
277
|
|
122
|
-
const
|
278
|
+
const valueToAngleScale$ = combineLatest({
|
123
279
|
fullParams: context.fullParams$,
|
124
280
|
containerValueSum: containerValueSum$,
|
125
281
|
}).pipe(
|
@@ -138,14 +294,14 @@ function createEachGraphic (pluginName: string, context: {
|
|
138
294
|
|
139
295
|
const angle$ = combineLatest({
|
140
296
|
value: value$,
|
141
|
-
|
297
|
+
valueToAngleScale: valueToAngleScale$,
|
142
298
|
containerValueSum: containerValueSum$,
|
143
299
|
}).pipe(
|
144
300
|
switchMap(async d => d),
|
145
|
-
map(({ value,
|
301
|
+
map(({ value, valueToAngleScale, containerValueSum }) => {
|
146
302
|
// value 限制在 0 ~ containerValueSum 之間
|
147
303
|
const validValue = Math.max(Math.min(value, containerValueSum), 0)
|
148
|
-
return
|
304
|
+
return valueToAngleScale(validValue)
|
149
305
|
}),
|
150
306
|
distinctUntilChanged()
|
151
307
|
)
|
@@ -165,33 +321,38 @@ function createEachGraphic (pluginName: string, context: {
|
|
165
321
|
)
|
166
322
|
|
167
323
|
// indicator 的 value 對應到 data 區間
|
168
|
-
const
|
324
|
+
const datum$ = combineLatest({
|
169
325
|
value: value$,
|
170
326
|
containerVisibleComputedSortedData: context.containerVisibleComputedSortedData$,
|
171
327
|
}).pipe(
|
172
328
|
switchMap(async d => d),
|
173
329
|
map(({ value, containerVisibleComputedSortedData }) => {
|
174
|
-
let seriesIndex = 0
|
330
|
+
// let seriesIndex = 0
|
331
|
+
let datum: ComputedDatumSeries | null = null
|
175
332
|
let stackedValue = 0
|
176
333
|
for (let i = 0; i < containerVisibleComputedSortedData.length; i++) {
|
177
334
|
const datumValue = containerVisibleComputedSortedData[i].value ?? 0
|
178
335
|
stackedValue += datumValue
|
179
336
|
if (stackedValue >= value) {
|
180
|
-
seriesIndex = containerVisibleComputedSortedData[i].seriesIndex
|
337
|
+
// seriesIndex = containerVisibleComputedSortedData[i].seriesIndex
|
338
|
+
datum = containerVisibleComputedSortedData[i]
|
181
339
|
break
|
182
340
|
}
|
183
341
|
if (i === containerVisibleComputedSortedData.length - 1) {
|
184
|
-
seriesIndex = containerVisibleComputedSortedData[i].seriesIndex
|
342
|
+
// seriesIndex = containerVisibleComputedSortedData[i].seriesIndex
|
343
|
+
datum = containerVisibleComputedSortedData[i]
|
185
344
|
}
|
186
345
|
}
|
187
|
-
return
|
346
|
+
return datum
|
188
347
|
}),
|
189
348
|
distinctUntilChanged()
|
190
349
|
)
|
191
350
|
|
192
351
|
const graphicColor$ = combineLatest({
|
193
352
|
value: value$,
|
194
|
-
valueSeriesIndex:
|
353
|
+
valueSeriesIndex: datum$.pipe(
|
354
|
+
map(d => d ? d.seriesIndex : 0),
|
355
|
+
),
|
195
356
|
// containerVisibleComputedSortedData: context.containerVisibleComputedSortedData$,
|
196
357
|
fullParams: context.fullParams$,
|
197
358
|
fullChartParams: context.fullChartParams$,
|
@@ -217,20 +378,71 @@ function createEachGraphic (pluginName: string, context: {
|
|
217
378
|
distinctUntilChanged()
|
218
379
|
)
|
219
380
|
|
381
|
+
// 紀錄目前的 chartParams
|
382
|
+
let chartParamsRef: ChartParams | null = null
|
383
|
+
context.fullChartParams$
|
384
|
+
.pipe(takeUntil(destroy$))
|
385
|
+
.subscribe(params => {
|
386
|
+
chartParamsRef = params
|
387
|
+
})
|
388
|
+
|
389
|
+
// const newDefaultHighlight$ = combineLatest({
|
390
|
+
// datum: datum$,
|
391
|
+
// fullChartParams: context.fullChartParams$,
|
392
|
+
// }).pipe(
|
393
|
+
// switchMap(async d => d),
|
394
|
+
// map(data => {
|
395
|
+
// return data.fullChartParams.highlightTarget === 'datum'
|
396
|
+
// ? data.datum.id
|
397
|
+
// : data.fullChartParams.highlightTarget === 'series'
|
398
|
+
// ? data.datum.seriesLabel
|
399
|
+
// : null
|
400
|
+
// }),
|
401
|
+
// distinctUntilChanged()
|
402
|
+
// )
|
403
|
+
|
404
|
+
// autoHighlight
|
405
|
+
// context.fullParams$.pipe(
|
406
|
+
// map(params => params.autoHighlight),
|
407
|
+
// filter(autoHighlight => autoHighlight === true),
|
408
|
+
// mergeWith(context.event$.pipe(
|
409
|
+
// filter(event => event.eventName === 'mouseout'),
|
410
|
+
// )),
|
411
|
+
// switchMap(() => newDefaultHighlight$),
|
412
|
+
// takeUntil(destroy$),
|
413
|
+
// ).subscribe(newDefaultHighlight => {
|
414
|
+
// if (!newDefaultHighlight) {
|
415
|
+
// return
|
416
|
+
// }
|
417
|
+
// context.subject.chartParams$.next({
|
418
|
+
// ...chartParamsRef,
|
419
|
+
// highlightDefault: newDefaultHighlight
|
420
|
+
// })
|
421
|
+
// })
|
422
|
+
|
220
423
|
combineLatest({
|
221
424
|
fullParams: context.fullParams$,
|
222
425
|
fullChartParams: context.fullChartParams$,
|
223
426
|
angle: angle$,
|
224
427
|
pointerDistance: pointerDistance$,
|
225
428
|
graphicColor: graphicColor$,
|
429
|
+
value: value$,
|
430
|
+
datum: datum$,
|
431
|
+
computedData: context.computedData$,
|
432
|
+
SeriesDataMap: context.SeriesDataMap$,
|
226
433
|
}).subscribe(data => {
|
227
|
-
|
434
|
+
context.renderFn({
|
228
435
|
containerSelection: context.containerSelection,
|
229
436
|
angle: data.angle,
|
437
|
+
value: data.fullParams.value,
|
438
|
+
datum: data.datum,
|
439
|
+
computedData: data.computedData,
|
440
|
+
SeriesDataMap: data.SeriesDataMap,
|
230
441
|
pointerDistance: data.pointerDistance,
|
231
442
|
fullParams: data.fullParams,
|
232
443
|
fullChartParams: data.fullChartParams,
|
233
444
|
graphicColor: data.graphicColor,
|
445
|
+
event$: context.event$
|
234
446
|
})
|
235
447
|
})
|
236
448
|
|
@@ -242,7 +454,15 @@ function createEachGraphic (pluginName: string, context: {
|
|
242
454
|
|
243
455
|
|
244
456
|
export const Indicator = defineSeriesPlugin(pluginConfig)(({ selection, observer, subject }) => {
|
245
|
-
|
457
|
+
subject.plugins$
|
458
|
+
// .pipe(
|
459
|
+
// map(plugins => plugins.find(p => p.name === 'Indicator')),
|
460
|
+
// filter(p => !!p),
|
461
|
+
// switchMap(p => p.params$)
|
462
|
+
// )
|
463
|
+
.subscribe(params => {
|
464
|
+
console.log('Indicator params', params)
|
465
|
+
})
|
246
466
|
const destroy$ = new Subject()
|
247
467
|
|
248
468
|
const { seriesCenterSelection$ } = seriesCenterSelectionObservable({
|
@@ -254,43 +474,61 @@ export const Indicator = defineSeriesPlugin(pluginConfig)(({ selection, observer
|
|
254
474
|
|
255
475
|
const unsubscribeFnArr: (() => void)[] = []
|
256
476
|
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
)
|
273
|
-
|
274
|
-
const containerPosition$ = observer.seriesContainerPosition$.pipe(
|
275
|
-
takeUntil(destroy$),
|
276
|
-
map(data => data[containerIndex] ?? data[0])
|
277
|
-
)
|
278
|
-
|
279
|
-
unsubscribeFnArr[containerIndex] = createEachGraphic(pluginName, {
|
280
|
-
containerSelection: containerSelection,
|
281
|
-
// computedData$: observer.computedData$,
|
282
|
-
containerVisibleComputedSortedData$: containerVisibleComputedSortedData$,
|
283
|
-
// SeriesDataMap$: observer.SeriesDataMap$,
|
284
|
-
fullParams$: observer.fullParams$,
|
285
|
-
fullChartParams$: observer.fullChartParams$,
|
286
|
-
// textSizePx$: observer.textSizePx$,
|
287
|
-
// seriesHighlight$: observer.seriesHighlight$,
|
288
|
-
seriesContainerPosition$: containerPosition$,
|
289
|
-
event$: subject.event$,
|
290
|
-
})
|
477
|
+
const renderFn$ = observer.fullParams$.pipe(
|
478
|
+
map(params => {
|
479
|
+
if (params.indicatorType === 'triangle') {
|
480
|
+
return renderIndicatorTriangle
|
481
|
+
} else if (params.indicatorType === 'line') {
|
482
|
+
return renderIndicatorLine
|
483
|
+
} else if (params.indicatorType === 'needle') {
|
484
|
+
return renderIndicatorNeedle
|
485
|
+
} else if (params.indicatorType === 'pin') {
|
486
|
+
return renderIndicatorPin
|
487
|
+
} else {
|
488
|
+
return renderIndicatorTriangle
|
489
|
+
}
|
490
|
+
}),
|
491
|
+
)
|
291
492
|
|
493
|
+
combineLatest({
|
494
|
+
seriesCenterSelection: seriesCenterSelection$,
|
495
|
+
renderFn: renderFn$,
|
496
|
+
}).pipe(
|
497
|
+
switchMap(async d => d),
|
498
|
+
takeUntil(destroy$)
|
499
|
+
).subscribe(data => {
|
500
|
+
// 每次重新計算時,清除之前的訂閱
|
501
|
+
unsubscribeFnArr.forEach(fn => fn())
|
502
|
+
|
503
|
+
data.seriesCenterSelection.each((d, containerIndex, g) => {
|
504
|
+
|
505
|
+
const containerSelection = d3.select(g[containerIndex])
|
506
|
+
|
507
|
+
const containerVisibleComputedSortedData$ = observer.visibleComputedSortedData$.pipe(
|
508
|
+
takeUntil(destroy$),
|
509
|
+
map(data => data[containerIndex] ?? data[0])
|
510
|
+
)
|
511
|
+
|
512
|
+
const containerPosition$ = observer.seriesContainerPosition$.pipe(
|
513
|
+
takeUntil(destroy$),
|
514
|
+
map(data => data[containerIndex] ?? data[0])
|
515
|
+
)
|
516
|
+
|
517
|
+
unsubscribeFnArr[containerIndex] = createEachGraphic(pluginName, {
|
518
|
+
containerSelection: containerSelection,
|
519
|
+
renderFn: data.renderFn,
|
520
|
+
containerVisibleComputedSortedData$: containerVisibleComputedSortedData$,
|
521
|
+
fullParams$: observer.fullParams$,
|
522
|
+
fullChartParams$: observer.fullChartParams$,
|
523
|
+
seriesContainerPosition$: containerPosition$,
|
524
|
+
computedData$: observer.computedData$,
|
525
|
+
SeriesDataMap$: observer.SeriesDataMap$,
|
526
|
+
subject,
|
527
|
+
event$: subject.event$,
|
292
528
|
})
|
529
|
+
|
293
530
|
})
|
531
|
+
})
|
294
532
|
|
295
533
|
return () => {
|
296
534
|
destroy$.next(undefined)
|
@@ -151,6 +151,93 @@ function renderPie ({ selection, data, arc, pathClassName, fullParams, fullChart
|
|
151
151
|
return pathSelection
|
152
152
|
}
|
153
153
|
|
154
|
+
// function renderGauge ({ selection, data, arc, pathClassName, fullParams, fullChartParams, axisWidth }: {
|
155
|
+
// selection: d3.Selection<SVGGElement, unknown, any, unknown>
|
156
|
+
// data: PieDatum[]
|
157
|
+
// arc: d3.Arc<any, d3.DefaultArcObject>
|
158
|
+
// pathClassName: string
|
159
|
+
// fullParams: PieParams
|
160
|
+
// fullChartParams: ChartParams
|
161
|
+
// axisWidth: number
|
162
|
+
// }) {
|
163
|
+
// const gaugeClassName = getClassName('Gauge', 'tick')
|
164
|
+
// const gaugeLabelClassName = getClassName('Gauge', 'label')
|
165
|
+
|
166
|
+
// // 計算總角度範圍
|
167
|
+
// const totalAngle = fullParams.endAngle - fullParams.startAngle
|
168
|
+
// const totalTicks = 20 // 總刻度數量
|
169
|
+
// const tickInterval = totalAngle / totalTicks
|
170
|
+
|
171
|
+
// // 計算刻度資料
|
172
|
+
// const tickData = Array.from({ length: totalTicks + 1 }, (_, i) => {
|
173
|
+
// const angle = fullParams.startAngle + (i * tickInterval)
|
174
|
+
// const isLongTick = i % 5 === 0 // 每5格一個長線
|
175
|
+
// return {
|
176
|
+
// angle,
|
177
|
+
// isLongTick,
|
178
|
+
// value: Math.round((i / totalTicks) * 100) // 0-100的數值
|
179
|
+
// }
|
180
|
+
// })
|
181
|
+
|
182
|
+
// // 計算實際像素半徑
|
183
|
+
// const arcScale = d3.scaleLinear()
|
184
|
+
// .domain([0, 1])
|
185
|
+
// .range([0, axisWidth / 2])
|
186
|
+
|
187
|
+
// const outerRadius = arcScale(fullParams.outerRadius)
|
188
|
+
// const innerRadius = arcScale(fullParams.innerRadius)
|
189
|
+
// const longTickLength = (outerRadius - innerRadius) * 0.3
|
190
|
+
// const shortTickLength = (outerRadius - innerRadius) * 0.15
|
191
|
+
|
192
|
+
// // 繪製刻度線
|
193
|
+
// const tickSelection = selection
|
194
|
+
// .selectAll(`line.${gaugeClassName}`)
|
195
|
+
// .data(tickData)
|
196
|
+
// .join('line')
|
197
|
+
// .classed(gaugeClassName, true)
|
198
|
+
// .attr('x1', d => {
|
199
|
+
// const radius = outerRadius
|
200
|
+
// return Math.cos(d.angle - Math.PI / 2) * radius
|
201
|
+
// })
|
202
|
+
// .attr('y1', d => {
|
203
|
+
// const radius = outerRadius
|
204
|
+
// return Math.sin(d.angle - Math.PI / 2) * radius
|
205
|
+
// })
|
206
|
+
// .attr('x2', d => {
|
207
|
+
// const radius = outerRadius - (d.isLongTick ? longTickLength : shortTickLength)
|
208
|
+
// return Math.cos(d.angle - Math.PI / 2) * radius
|
209
|
+
// })
|
210
|
+
// .attr('y2', d => {
|
211
|
+
// const radius = outerRadius - (d.isLongTick ? longTickLength : shortTickLength)
|
212
|
+
// return Math.sin(d.angle - Math.PI / 2) * radius
|
213
|
+
// })
|
214
|
+
// .attr('stroke', fullChartParams.colors[fullChartParams.colorScheme].primary)
|
215
|
+
// .attr('stroke-width', d => d.isLongTick ? 2 : 1)
|
216
|
+
// .attr('stroke-linecap', 'round')
|
217
|
+
|
218
|
+
// // 繪製數字標籤(只在長線上)
|
219
|
+
// const labelSelection = selection
|
220
|
+
// .selectAll(`text.${gaugeLabelClassName}`)
|
221
|
+
// .data(tickData.filter(d => d.isLongTick))
|
222
|
+
// .join('text')
|
223
|
+
// .classed(gaugeLabelClassName, true)
|
224
|
+
// .attr('x', d => {
|
225
|
+
// const radius = outerRadius + 15 // 稍微往外一點
|
226
|
+
// return Math.cos(d.angle - Math.PI / 2) * radius
|
227
|
+
// })
|
228
|
+
// .attr('y', d => {
|
229
|
+
// const radius = outerRadius + 15
|
230
|
+
// return Math.sin(d.angle - Math.PI / 2) * radius
|
231
|
+
// })
|
232
|
+
// .attr('text-anchor', 'middle')
|
233
|
+
// .attr('dominant-baseline', 'middle')
|
234
|
+
// .attr('fill', fullChartParams.colors[fullChartParams.colorScheme].primary)
|
235
|
+
// .attr('font-size', '12px')
|
236
|
+
// .text(d => d.value)
|
237
|
+
|
238
|
+
// return { tickSelection, labelSelection }
|
239
|
+
// }
|
240
|
+
|
154
241
|
function highlight ({ pathSelection, ids, fullChartParams, arc, arcHighlight }: {
|
155
242
|
pathSelection: d3.Selection<SVGPathElement, PieDatum, any, any>
|
156
243
|
ids: string[]
|
@@ -314,6 +401,14 @@ function createEachPie (pluginName: string, context: {
|
|
314
401
|
distinctUntilChanged()
|
315
402
|
)
|
316
403
|
|
404
|
+
// highlight的對象(不做成observable是因為要避免觸發監聽)
|
405
|
+
let seriesHighlight: ComputedDatumSeries[] = []
|
406
|
+
context.seriesHighlight$
|
407
|
+
.pipe(
|
408
|
+
takeUntil(destroy$)
|
409
|
+
)
|
410
|
+
.subscribe(d => seriesHighlight = d)
|
411
|
+
|
317
412
|
const pathSelection$ = new Observable<d3.Selection<SVGPathElement, PieDatum, any, any>>(subscriber => {
|
318
413
|
combineLatest({
|
319
414
|
pieData: pieData$,
|
@@ -359,6 +454,16 @@ function createEachPie (pluginName: string, context: {
|
|
359
454
|
fullParams: data.fullParams,
|
360
455
|
fullChartParams: data.fullChartParams,
|
361
456
|
})
|
457
|
+
|
458
|
+
// renderGauge({
|
459
|
+
// selection: context.containerSelection,
|
460
|
+
// data: tweenData,
|
461
|
+
// arc: data.arc,
|
462
|
+
// pathClassName,
|
463
|
+
// fullParams: data.fullParams,
|
464
|
+
// fullChartParams: data.fullChartParams,
|
465
|
+
// axisWidth: 500
|
466
|
+
// })
|
362
467
|
|
363
468
|
// @Q@ 想盡量減清效能負擔所以取消掉
|
364
469
|
// context.event$.next({
|
@@ -422,13 +527,12 @@ function createEachPie (pluginName: string, context: {
|
|
422
527
|
eventName: 'transitionEnd',
|
423
528
|
event: undefined,
|
424
529
|
highlightTarget: data.highlightTarget,
|
425
|
-
|
530
|
+
data: data.computedData,
|
426
531
|
series: [],
|
427
532
|
seriesIndex: -1,
|
428
533
|
seriesLabel: '',
|
429
|
-
|
534
|
+
datum: null
|
430
535
|
})
|
431
|
-
|
432
536
|
|
433
537
|
})
|
434
538
|
|