@orbcharts/plugins-basic 3.0.0-alpha.63 → 3.0.0-alpha.65

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.
@@ -27,134 +27,378 @@ import { seriesCenterSelectionObservable } from '../seriesObservables'
27
27
  interface RenderDatum {
28
28
  pieDatum: PieDatum
29
29
  arcIndex: number
30
- arcLabel: string
30
+ arcLabels: string[]
31
+ lineStartX: number
32
+ lineStartY: number
33
+ lineStartMouseoverX: number
34
+ lineStartMouseoverY: number
31
35
  x: number
32
36
  y: number
33
37
  mouseoverX: number
34
38
  mouseoverY: number
39
+ textWidth: number, // 文字寬度
40
+ collisionShiftX: number // 避免碰撞的位移
41
+ collisionShiftY: number
42
+ quadrant: number // 第幾象限
35
43
  }
36
44
 
37
45
  const pluginName = 'PieLabels'
46
+ const labelGClassName = getClassName(pluginName, 'label-g')
47
+ const lineGClassName = getClassName(pluginName, 'line-g')
38
48
  const textClassName = getClassName(pluginName, 'text')
39
49
 
40
- function makeRenderData (pieData: PieDatum[], arc: d3.Arc<any, d3.DefaultArcObject>, mouseoverArc: d3.Arc<any, d3.DefaultArcObject>, centroid: number): RenderDatum[] {
50
+ const pieOuterCentroid = 2
51
+
52
+ function makeRenderData ({ pieData, arc, arcMouseover, labelCentroid, lineStartCentroid, fullParams }: {
53
+ pieData: PieDatum[]
54
+ arc: d3.Arc<any, d3.DefaultArcObject>
55
+ arcMouseover: d3.Arc<any, d3.DefaultArcObject>
56
+ labelCentroid: number
57
+ lineStartCentroid: number
58
+ fullParams: PieLabelsParams
59
+ }): RenderDatum[] {
41
60
  return pieData
42
61
  .map((d, i) => {
43
62
  const [_x, _y] = arc!.centroid(d as any)
44
- const [_mouseoverX, _mouseoverY] = mouseoverArc!.centroid(d as any)
63
+ const [_mouseoverX, _mouseoverY] = arcMouseover!.centroid(d as any)
64
+ const arcLabel = fullParams.labelFn(d.data)
45
65
  return {
46
66
  pieDatum: d,
47
67
  arcIndex: i,
48
- arcLabel: d.data.label,
49
- x: _x * centroid!,
50
- y: _y * centroid!,
51
- mouseoverX: _mouseoverX * centroid!,
52
- mouseoverY: _mouseoverY * centroid!
68
+ arcLabels: arcLabel.split('\n'),
69
+ lineStartX: _x * lineStartCentroid,
70
+ lineStartY: _y * lineStartCentroid,
71
+ lineStartMouseoverX: _mouseoverX * lineStartCentroid,
72
+ lineStartMouseoverY: _mouseoverY * lineStartCentroid,
73
+ x: _x * labelCentroid!,
74
+ y: _y * labelCentroid!,
75
+ mouseoverX: _mouseoverX * labelCentroid!,
76
+ mouseoverY: _mouseoverY * labelCentroid!,
77
+ textWidth: 0, // 後面再做計算
78
+ collisionShiftX: 0, // 後面再做計算
79
+ collisionShiftY: 0, // 後面再做計算
80
+ quadrant: _x >= 0 && _y <= 0
81
+ ? 1
82
+ : _x < 0 && _y <= 0
83
+ ? 2
84
+ : _x < 0 && _y > 0
85
+ ? 3
86
+ : 4
53
87
  }
54
88
  })
55
89
  .filter(d => d.pieDatum.data.visible)
56
90
  }
57
91
 
58
92
  // 繪製圓餅圖
59
- function renderLabel (selection: d3.Selection<SVGGElement, undefined, any, any>, data: RenderDatum[], pluginParams: PieLabelsParams, fullChartParams: ChartParams) {
93
+ function renderLabel ({ labelGSelection, data, fullParams, fullChartParams, textSizePx }: {
94
+ labelGSelection: d3.Selection<SVGGElement, undefined, any, any>
95
+ data: RenderDatum[]
96
+ fullParams: PieLabelsParams
97
+ fullChartParams: ChartParams
98
+ textSizePx: number
99
+ }) {
60
100
  // console.log(data)
61
101
  // let update = this.gSelection.selectAll('g').data(pieData)
62
- let update: d3.Selection<SVGPathElement, RenderDatum, any, any> = selection
63
- .selectAll<SVGPathElement, RenderDatum>('text')
102
+ const textSelection = labelGSelection
103
+ .selectAll<SVGTextElement, RenderDatum>('text')
64
104
  .data(data, d => d.pieDatum.id)
65
- let enter = update.enter()
66
- .append<SVGPathElement>('text')
105
+ .join('text')
67
106
  .classed(textClassName, true)
68
- let exit = update.exit()
69
-
70
- enter
71
- .append('text')
72
-
73
- const labelSelection = update.merge(enter)
74
- labelSelection
75
107
  .attr('font-weight', 'bold')
76
- .attr('text-anchor', 'middle')
77
- .style('dominant-baseline', 'middle')
108
+ .attr('text-anchor', d => d.quadrant == 1 || d.quadrant == 4 ? 'start' : 'end')
109
+ .style('dominant-baseline', d => d.quadrant == 1 || d.quadrant == 2 ? 'auto' : 'hanging')
78
110
  // .style('pointer-events', 'none')
79
111
  .style('cursor', d => fullChartParams.highlightTarget && fullChartParams.highlightTarget != 'none'
80
112
  ? 'pointer'
81
113
  : 'none')
82
- // .text((d, i) => d.arcLabel)
83
- .text(d => pluginParams.labelFn(d.pieDatum.data))
114
+ // .text(d => fullParams.labelFn(d.pieDatum.data))
84
115
  .attr('font-size', fullChartParams.styles.textSize)
85
- .attr('fill', (d, i) => getDatumColor({ datum: d.pieDatum.data, colorType: pluginParams.labelColorType, fullChartParams }))
116
+ .attr('x', 0)
117
+ .attr('y', 0)
118
+ .attr('fill', (d, i) => getDatumColor({ datum: d.pieDatum.data, colorType: fullParams.labelColorType, fullChartParams }))
119
+ .each((d, i, n) => {
120
+ const textNode = d3.select<SVGTextElement, RenderDatum>(n[i])
121
+ .selectAll('tspan')
122
+ .data(d.arcLabels)
123
+ .join('tspan')
124
+ .attr('x', 0)
125
+ .attr('y', (_d, _i) => d.quadrant == 1 || d.quadrant == 2
126
+ ? - (d.arcLabels.length - 1 - _i) * textSizePx
127
+ : _i * textSizePx)
128
+ .text(d => d)
129
+ })
130
+ textSelection
86
131
  .transition()
87
132
  .attr('transform', (d) => {
88
133
  // console.log('transform', d)
89
134
  return 'translate(' + d.x + ',' + d.y + ')'
90
135
  })
91
136
  // .on('end', () => initHighlight({ labelSelection, data, fullChartParams }))
92
- exit.remove()
93
137
 
94
138
  // 如無新增資料則不用等動畫
95
139
  // if (enter.size() == 0) {
96
140
  // this.initHighlight()
97
141
  // }
98
142
 
99
- return labelSelection
143
+ return textSelection
100
144
  }
101
145
 
102
- // function initHighlight ({ labelSelection, data, fullChartParams }: {
103
- // labelSelection: (d3.Selection<SVGPathElement, RenderDatum, any, any>)
104
- // data: RenderDatum[]
105
- // fullChartParams: ChartParams
106
- // }) {
107
- // removeHighlight({ labelSelection })
108
- // // if (fullParams.highlightSeriesId || fullParams.highlightDatumId) {
109
- // highlight({
110
- // labelSelection,
111
- // data,
112
- // id: fullChartParams.highlightDefault,
113
- // label: fullChartParams.highlightDefault,
114
- // fullChartParams
115
- // })
116
- // // }
146
+ // // 獲取每個文字元素的邊界框並檢查是否重疊
147
+ // function resolveCollisions(labelSelection: d3.Selection<SVGPathElement, RenderDatum, any, any>, data: RenderDatum[]) {
148
+ // const textArray = labelSelection.nodes();
149
+ // const padding = 10; // 調整文字間的間距
150
+
151
+ // // 存儲每個標籤的當前位置
152
+ // const positions = textArray.map((textNode, i) => {
153
+ // const bbox = textNode.getBBox();
154
+ // // const arcCentroid = arc.centroid(data[i]);
155
+ // const arcCentroid = [data[i].x, data[i].y];
156
+ // return {
157
+ // node: textNode,
158
+ // x: arcCentroid[0],
159
+ // y: arcCentroid[1],
160
+ // width: bbox.width,
161
+ // height: bbox.height
162
+ // };
163
+ // });
164
+ // // console.log('positions', positions)
165
+
166
+ // for (let i = 0; i < positions.length; i++) {
167
+ // const a = positions[i];
168
+
169
+ // for (let j = i + 1; j < positions.length; j++) {
170
+ // const b = positions[j];
171
+
172
+ // // 檢查是否重疊
173
+ // if (!(a.x + a.width / 2 < b.x - b.width / 2 ||
174
+ // a.x - a.width / 2 > b.x + b.width / 2 ||
175
+ // a.y + a.height / 2 < b.y - b.height / 2 ||
176
+ // a.y - a.height / 2 > b.y + b.height / 2)) {
177
+
178
+ // // 如果有重疊,則位移其中一個文字,這裡我們進行上下位移
179
+ // const moveDown = (b.y > a.y) ? padding : -padding; // 決定方向
180
+ // b.y += moveDown; // 更新 b 的 y 座標
181
+
182
+ // // 更新 b 的 x 座標,根據與 a 的位置差異進行微調
183
+ // const moveRight = (b.x > a.x) ? padding : -padding;
184
+ // b.x += moveRight;
185
+
186
+ // // // 重新設置 b 的 transform 來移動
187
+ // d3.select(b.node)
188
+ // .transition()
189
+ // .attr("transform", `translate(${b.x},${b.y})`);
190
+
191
+ // data[j].collisionShiftX = moveRight
192
+ // data[j].collisionShiftY = moveDown
193
+ // }
194
+ // }
195
+ // }
117
196
  // }
118
197
 
119
- function highlight ({ labelSelection, ids, fullChartParams }: {
120
- labelSelection: (d3.Selection<SVGPathElement, RenderDatum, any, any>)
198
+ // 獲取每個文字元素的邊界框並檢查是否重疊
199
+ function resolveCollisions(textSelection: d3.Selection<SVGTextElement, RenderDatum, any, any>, data: RenderDatum[], textSizePx: number) {
200
+ const textArray = textSelection.nodes();
201
+ const padding = textSizePx // 調整文字間的間距
202
+
203
+ // 存儲每個標籤的當前位置
204
+ const positions = textArray.map((textNode, i) => {
205
+ const bbox = textNode.getBBox();
206
+ // const arcCentroid = arc.centroid(data[i]);
207
+ const arcCentroid = [data[i].x, data[i].y];
208
+ return {
209
+ node: textNode,
210
+ x: arcCentroid[0],
211
+ y: arcCentroid[1],
212
+ width: bbox.width,
213
+ height: bbox.height
214
+ }
215
+ })
216
+
217
+ // 順時針碰撞檢測(只處理 2、4 象限,將較後面的文字碰撞時往外偏移)
218
+ for (let i = 0; i < positions.length; i++) {
219
+ const a = positions[i]
220
+
221
+ for (let j = i + 1; j < positions.length; j++) {
222
+ const b = positions[j]
223
+
224
+ // 記錄文字寬度
225
+ data[i].textWidth = a.width
226
+
227
+ const ax = a.x + data[i].collisionShiftX
228
+ const ay = a.y + data[i].collisionShiftY
229
+ const bx = b.x + data[j].collisionShiftX
230
+ const by = b.y + data[j].collisionShiftY
231
+
232
+ // 檢查是否重疊
233
+ if (!(ax + a.width / 2 < bx - b.width / 2 ||
234
+ ax - a.width / 2 > bx + b.width / 2 ||
235
+ ay + a.height / 2 < by - b.height / 2 ||
236
+ ay - a.height / 2 > by + b.height / 2)) {
237
+
238
+ if (data[j].quadrant == 2) {
239
+ const moveDown = (by > ay)
240
+ ? -padding * 2
241
+ : -padding
242
+ data[j].collisionShiftY += moveDown // 由後一個累加高度
243
+ } else if (data[j].quadrant == 4) {
244
+ const moveDown = (by > ay)
245
+ ? padding
246
+ : padding * 2
247
+ data[j].collisionShiftY += moveDown // 由後一個累加高度
248
+ }
249
+ }
250
+ }
251
+ }
252
+
253
+ // 逆時針碰撞檢測(只處理 1、3 象限,將較前面的文字碰撞時往外偏移)
254
+ for (let i = positions.length - 1; i >= 0; i--) {
255
+ const a = positions[i]
256
+
257
+ for (let j = i - 1; j >= 0; j--) {
258
+ const b = positions[j]
259
+
260
+ // 記錄文字寬度
261
+ data[i].textWidth = a.width
262
+
263
+ const ax = a.x + data[i].collisionShiftX
264
+ const ay = a.y + data[i].collisionShiftY
265
+ const bx = b.x + data[j].collisionShiftX
266
+ const by = b.y + data[j].collisionShiftY
267
+
268
+ // 檢查是否重疊
269
+ if (!(ax + a.width / 2 < bx - b.width / 2 ||
270
+ ax - a.width / 2 > bx + b.width / 2 ||
271
+ ay + a.height / 2 < by - b.height / 2 ||
272
+ ay - a.height / 2 > by + b.height / 2)) {
273
+
274
+ if (data[j].quadrant == 1) {
275
+ const moveDown = (by > ay)
276
+ ? -padding * 2
277
+ : -padding
278
+ data[j].collisionShiftY += moveDown // 由前一個累加高度
279
+ } else if (data[j].quadrant == 3) {
280
+ const moveDown = (by > ay)
281
+ ? padding
282
+ : padding * 2
283
+ data[j].collisionShiftY += moveDown // 由前一個累加高度
284
+ }
285
+ }
286
+ }
287
+ }
288
+
289
+ // 全部算完再來 render
290
+ textSelection
291
+ .data(data)
292
+ .transition()
293
+ .attr('transform', (d) => {
294
+ return `translate(${d.x + d.collisionShiftX},${d.y + d.collisionShiftY})`
295
+ })
296
+ }
297
+
298
+ function renderLine ({ lineGSelection, data, fullParams, fullChartParams }: {
299
+ lineGSelection: d3.Selection<SVGGElement, undefined, any, any>
300
+ data: RenderDatum[]
301
+ fullParams: PieLabelsParams
302
+ fullChartParams: ChartParams
303
+ }) {
304
+
305
+ // 只顯示在有偏移的標籤
306
+ const filteredData = data.filter(d => d.collisionShiftX || d.collisionShiftY)
307
+
308
+ // 添加標籤的連接線
309
+ const lines = lineGSelection.selectAll<SVGPolylineElement, RenderDatum>("polyline")
310
+ .data(filteredData, d => d.pieDatum.id)
311
+ .join("polyline")
312
+ .attr("stroke", d => getDatumColor({ datum: d.pieDatum.data, colorType: fullParams.labelColorType, fullChartParams }))
313
+ .attr("stroke-width", 1)
314
+ .attr("fill", "none")
315
+ .attr("points", (d) => {
316
+ return [[d.lineStartX, d.lineStartY], [d.lineStartX, d.lineStartY]] as any // 畫出從弧線中心到延伸點的線
317
+ })
318
+ lines
319
+ .transition()
320
+ .attr("points", (d) => {
321
+ // const pos = arc.centroid(d) // 起點:弧線的中心點
322
+ // const outerPos = [pos[0] * 2.5, pos[1] * 2.5] // 外部延伸的點(乘以倍率來延長線段)
323
+
324
+ let lineEndX = d.x + d.collisionShiftX
325
+ let lineEndY = d.y + d.collisionShiftY
326
+ // if (d.lineStartX >= Math.abs(d.lineStartY)) {
327
+ // lineEndX -= d.textWidth / 2
328
+ // } else if (d.lineStartX <= - Math.abs(d.lineStartY)) {
329
+ // lineEndX += d.textWidth / 2
330
+ // }
331
+
332
+ return [[lineEndX, lineEndY], [d.lineStartX, d.lineStartY]] as any // 畫出從弧線中心到延伸點的線
333
+ })
334
+
335
+ return lines
336
+ }
337
+
338
+ function highlight ({ textSelection, lineSelection, ids, fullChartParams }: {
339
+ textSelection: d3.Selection<SVGTextElement, RenderDatum, any, any>
340
+ lineSelection: d3.Selection<SVGPolylineElement, RenderDatum, any, any>
121
341
  ids: string[]
122
342
  fullChartParams: ChartParams
123
343
  }) {
124
- labelSelection.interrupt('highlight')
344
+ textSelection.interrupt('highlight')
345
+ lineSelection.interrupt('highlight')
125
346
 
126
347
  if (!ids.length) {
127
- labelSelection
128
- .transition()
348
+ textSelection
349
+ .transition('highlight')
129
350
  .duration(200)
130
351
  .attr('transform', (d) => {
131
- return 'translate(' + d.x + ',' + d.y + ')'
352
+ return `translate(${d.x + d.collisionShiftX},${d.y + d.collisionShiftY})`
132
353
  })
133
354
  .style('opacity', 1)
355
+ lineSelection
356
+ .transition('highlight')
357
+ .duration(200)
358
+ .style('opacity', 1)
134
359
  return
135
360
  }
136
361
 
137
- labelSelection.each((d, i, n) => {
138
- const segment = d3.select<SVGPathElement, RenderDatum>(n[i])
362
+ textSelection.each((d, i, n) => {
363
+ const segment = d3.select<SVGTextElement, RenderDatum>(n[i])
139
364
 
140
365
  if (ids.includes(d.pieDatum.id)) {
141
366
  segment
142
367
  .style('opacity', 1)
143
- .transition()
368
+ .transition('highlight')
144
369
  .duration(200)
145
370
  .attr('transform', (d) => {
146
- return 'translate(' + d.mouseoverX + ',' + d.mouseoverY + ')'
371
+ // 如果已經有偏移過,則使用偏移後的位置(如果再改變的話很容易造成文字重疊)
372
+ if (d.collisionShiftX || d.collisionShiftY) {
373
+ return `translate(${d.x + d.collisionShiftX},${d.y + d.collisionShiftY})`
374
+ }
375
+ return `translate(${d.mouseoverX + d.collisionShiftX},${d.mouseoverY + d.collisionShiftY})`
147
376
  })
148
377
  } else {
149
378
  segment
150
379
  .style('opacity', fullChartParams.styles.unhighlightedOpacity)
151
- .transition()
380
+ .transition('highlight')
152
381
  .duration(200)
153
382
  .attr('transform', (d) => {
154
- return 'translate(' + d.x + ',' + d.y + ')'
383
+ return `translate(${d.x + d.collisionShiftX},${d.y + d.collisionShiftY})`
155
384
  })
156
385
  }
157
386
  })
387
+ lineSelection.each((d, i, n) => {
388
+ const segment = d3.select<SVGPolylineElement, RenderDatum>(n[i])
389
+
390
+ if (ids.includes(d.pieDatum.data.id)) {
391
+ segment
392
+ .style('opacity', 1)
393
+ .transition('highlight')
394
+ .duration(200)
395
+ } else {
396
+ segment
397
+ .style('opacity', fullChartParams.styles.unhighlightedOpacity)
398
+ .transition('highlight')
399
+ .duration(200)
400
+ }
401
+ })
158
402
  }
159
403
 
160
404
 
@@ -165,14 +409,23 @@ function createEachPieLabel (pluginName: string, context: {
165
409
  // SeriesDataMap$: Observable<Map<string, ComputedDatumSeries[]>>
166
410
  fullParams$: Observable<PieLabelsParams>
167
411
  fullChartParams$: Observable<ChartParams>
412
+ textSizePx$: Observable<number>
168
413
  seriesHighlight$: Observable<ComputedDatumSeries[]>
169
414
  seriesContainerPosition$: Observable<SeriesContainerPosition>
170
415
  event$: Subject<EventSeries>
171
416
  }) {
172
417
  const destroy$ = new Subject()
173
418
 
419
+ context.containerSelection.selectAll('g').remove()
420
+
421
+ const lineGSelection: d3.Selection<SVGGElement, any, any, unknown> = context.containerSelection.append('g')
422
+ lineGSelection.classed(lineGClassName, true)
423
+ const labelGSelection: d3.Selection<SVGGElement, any, any, unknown> = context.containerSelection.append('g')
424
+ labelGSelection.classed(labelGClassName, true)
425
+
174
426
  // const graphicSelection: d3.Selection<SVGGElement, any, any, any> = selection.append('g')
175
- let labelSelection$: Subject<d3.Selection<SVGPathElement, RenderDatum, any, any>> = new Subject()
427
+ const textSelection$: Subject<d3.Selection<SVGTextElement, RenderDatum, any, any>> = new Subject()
428
+ const lineSelection$: Subject<d3.Selection<SVGPolylineElement, RenderDatum, any, any>> = new Subject()
176
429
  let renderData: RenderDatum[] = []
177
430
 
178
431
  const shorterSideWith$ = context.seriesContainerPosition$.pipe(
@@ -181,18 +434,28 @@ function createEachPieLabel (pluginName: string, context: {
181
434
  distinctUntilChanged()
182
435
  )
183
436
 
437
+ const lineStartCentroid$ = context.fullParams$.pipe(
438
+ takeUntil(destroy$),
439
+ map(d => {
440
+ return d.labelCentroid >= pieOuterCentroid
441
+ ? pieOuterCentroid // 當 label在 pie的外側時,線條從 pie的邊緣開始
442
+ : d.labelCentroid // 當 label在 pie的內側時,線條從 label未偏移前的位置開始
443
+
444
+ })
445
+ )
446
+
184
447
  combineLatest({
185
448
  shorterSideWith: shorterSideWith$,
186
449
  containerVisibleComputedLayoutData: context.containerVisibleComputedLayoutData$,
187
450
  fullParams: context.fullParams$,
188
- fullChartParams: context.fullChartParams$
451
+ fullChartParams: context.fullChartParams$,
452
+ textSizePx: context.textSizePx$,
453
+ lineStartCentroid: lineStartCentroid$
189
454
  }).pipe(
190
455
  takeUntil(destroy$),
191
456
  switchMap(async (d) => d),
192
457
  ).subscribe(data => {
193
458
 
194
- // const shorterSideWith = data.layout.width < data.layout.height ? data.layout.width : data.layout.height
195
-
196
459
  // 弧產生器 (d3.arc())
197
460
  const arc = makeD3Arc({
198
461
  axisWidth: data.shorterSideWith,
@@ -216,16 +479,45 @@ function createEachPieLabel (pluginName: string, context: {
216
479
  endAngle: data.fullParams.endAngle
217
480
  })
218
481
 
219
- renderData = makeRenderData(pieData, arc, arcMouseover, data.fullParams.labelCentroid)
482
+ renderData = makeRenderData({
483
+ pieData,
484
+ arc,
485
+ arcMouseover,
486
+ labelCentroid: data.fullParams.labelCentroid,
487
+ lineStartCentroid: data.lineStartCentroid,
488
+ fullParams: data.fullParams
489
+ })
490
+
491
+ // 先移除線條,等偏移後再重新繪製
492
+ lineGSelection.selectAll('polyline').remove()
493
+
494
+ const textSelection = renderLabel({
495
+ labelGSelection,
496
+ data: renderData,
497
+ fullParams: data.fullParams,
498
+ fullChartParams: data.fullChartParams,
499
+ textSizePx: data.textSizePx
500
+ })
501
+
502
+ // 等 label 本身的 transition 結束後再進行碰撞檢測
503
+ setTimeout(() => {
504
+ // resolveCollisions(labelSelection, renderData)
505
+ // 偏移 label
506
+ resolveCollisions(textSelection, renderData, data.textSizePx)
507
+
508
+ const lineSelection = renderLine({ lineGSelection, data: renderData, fullParams: data.fullParams, fullChartParams: data.fullChartParams })
509
+
510
+ lineSelection$.next(lineSelection)
220
511
 
221
- const labelSelection = renderLabel(context.containerSelection, renderData, data.fullParams, data.fullChartParams)
512
+ }, 1000)
222
513
 
223
- labelSelection$.next(labelSelection)
514
+ textSelection$.next(textSelection)
224
515
 
225
516
  })
226
517
 
227
518
  combineLatest({
228
- labelSelection: labelSelection$,
519
+ textSelection: textSelection$,
520
+ lineSelection: lineSelection$,
229
521
  highlight: context.seriesHighlight$.pipe(
230
522
  map(data => data.map(d => d.id))
231
523
  ),
@@ -235,7 +527,8 @@ function createEachPieLabel (pluginName: string, context: {
235
527
  switchMap(async d => d)
236
528
  ).subscribe(data => {
237
529
  highlight({
238
- labelSelection: data.labelSelection,
530
+ textSelection: data.textSelection,
531
+ lineSelection: data.lineSelection,
239
532
  ids: data.highlight,
240
533
  fullChartParams: data.fullChartParams,
241
534
  })
@@ -290,6 +583,7 @@ export const PieLabels = defineSeriesPlugin(pluginName, DEFAULT_PIE_LABELS_PARAM
290
583
  // SeriesDataMap$: observer.SeriesDataMap$,
291
584
  fullParams$: observer.fullParams$,
292
585
  fullChartParams$: observer.fullChartParams$,
586
+ textSizePx$: observer.textSizePx$,
293
587
  seriesHighlight$: observer.seriesHighlight$,
294
588
  seriesContainerPosition$: containerPosition$,
295
589
  event$: subject.event$,
@@ -24,7 +24,7 @@ import {
24
24
  import { DEFAULT_ROSE_PARAMS } from '../defaults'
25
25
  // import { makePieData } from '../seriesUtils'
26
26
  // import { getD3TransitionEase, makeD3Arc } from '../../utils/d3Utils'
27
- import { getClassName } from '../../utils/orbchartsUtils'
27
+ import { getDatumColor, getClassName } from '../../utils/orbchartsUtils'
28
28
  import { seriesCenterSelectionObservable } from '../seriesObservables'
29
29
 
30
30
  // @Q@ 暫時先寫在這裡,之後pie一起重構後再放到seriesUtils
@@ -39,15 +39,15 @@ const pluginName = 'Rose'
39
39
  const roseInnerRadius = 0
40
40
  const roseStartAngle = 0
41
41
  const roseEndAngle = Math.PI * 2
42
- const rosePadAngle = 0
43
42
 
44
- function makeTweenArcFn ({ cornerRadius, outerRadius, axisWidth, maxValue, arcScaleType }: {
43
+ function makeTweenArcFn ({ cornerRadius, outerRadius, axisWidth, maxValue, arcScaleType, fullParams }: {
45
44
  // interpolateRadius: (t: number) => number
46
45
  outerRadius: number
47
46
  cornerRadius: number
48
47
  axisWidth: number
49
48
  maxValue: number
50
49
  arcScaleType: 'radius' | 'area'
50
+ fullParams: RoseParams
51
51
  }): (d: PieDatum) => (t: number) => string {
52
52
 
53
53
  const outerRadiusWidth = (axisWidth / 2) * outerRadius
@@ -78,7 +78,7 @@ function makeTweenArcFn ({ cornerRadius, outerRadius, axisWidth, maxValue, arcSc
78
78
  const arc = d3.arc()
79
79
  .innerRadius(0)
80
80
  .outerRadius(outerRadius)
81
- .padAngle(rosePadAngle)
81
+ .padAngle(fullParams.padAngle)
82
82
  .padRadius(outerRadius)
83
83
  .cornerRadius(cornerRadius)
84
84
 
@@ -151,8 +151,8 @@ function highlight ({ pathSelection, ids, fullParams, fullChartParams, tweenArc
151
151
  .attr('d', (d: PieDatum) => {
152
152
  return tweenArc({
153
153
  ...d,
154
- startAngle: d.startAngle - fullParams.mouseoverAngleIncrease,
155
- endAngle: d.endAngle + fullParams.mouseoverAngleIncrease
154
+ startAngle: d.startAngle - fullParams.angleIncreaseWhileHighlight,
155
+ endAngle: d.endAngle + fullParams.angleIncreaseWhileHighlight
156
156
  })(1)
157
157
  })
158
158
  // .on('interrupt', () => {
@@ -215,7 +215,7 @@ function createEachRose (pluginName: string, context: {
215
215
  value: d.value,
216
216
  startAngle: eachAngle * i,
217
217
  endAngle: eachAngle * (i + 1),
218
- padAngle: rosePadAngle,
218
+ padAngle: data.fullParams.padAngle,
219
219
  prevValue: (lastPieData[i] && lastPieData[i].id === d.id) ? lastPieData[i].value : 0
220
220
  }
221
221
  })
@@ -250,7 +250,8 @@ function createEachRose (pluginName: string, context: {
250
250
  outerRadius: data.fullParams.outerRadius,
251
251
  axisWidth: data.axisWidth,
252
252
  maxValue: data.maxValue,
253
- arcScaleType: data.fullParams.arcScaleType
253
+ arcScaleType: data.fullParams.arcScaleType,
254
+ fullParams: data.fullParams
254
255
  })
255
256
  })
256
257
  )
@@ -269,6 +270,8 @@ function createEachRose (pluginName: string, context: {
269
270
  pieData: pieData$,
270
271
  tweenArc: tweenArc$,
271
272
  transitionDuration: transitionDuration$,
273
+ fullParams: context.fullParams$,
274
+ fullChartParams: context.fullChartParams$
272
275
  }).pipe(
273
276
  takeUntil(destroy$),
274
277
  switchMap(async d => d)
@@ -287,6 +290,12 @@ function createEachRose (pluginName: string, context: {
287
290
  .classed(pathClassName, true)
288
291
  .style('cursor', 'pointer')
289
292
  .attr('fill', (d, i) => d.data.color)
293
+ .attr('stroke', (d, i) => getDatumColor({
294
+ datum: d.data,
295
+ colorType: data.fullParams.strokeColorType,
296
+ fullChartParams: data.fullChartParams
297
+ }))
298
+ .attr('stroke-width', data.fullParams.strokeWidth)
290
299
  pathSelection.interrupt('graphicMove')
291
300
  pathSelection
292
301
  .transition('graphicMove')