From e36ce302ceaff101a37519c9d587ab59cf428cab Mon Sep 17 00:00:00 2001 From: Frank William <65594180+ai-qing-hai@users.noreply.github.com> Date: Thu, 14 Mar 2024 19:43:22 +0800 Subject: [PATCH] feat(interaction): add elementpointmove interaction (#6110) * feat(interaction): add elementpointmove interaction * feat(interaction): add elementpointmove interaction * feat(interaction): add elementpointmove interaction * feat(interaction): add elementpointmove interaction * feat(interaction): add elementpointmove interaction --------- Co-authored-by: wb-xcf804241 --- .../static/flareElementPointMoveArea.svg | 1619 +++++++++++++++++ .../flareElementPointMoveAreaNormalizeY.svg | 1586 ++++++++++++++++ .../static/flareElementPointMoveInterval.svg | 1407 ++++++++++++++ ...lareElementPointMoveIntervalNormalizeY.svg | 1529 ++++++++++++++++ ...ntPointMoveIntervalNormalizeYTranspose.svg | 1527 ++++++++++++++++ ...flareElementPointMoveIntervalTranspose.svg | 1403 ++++++++++++++ .../static/flareElementPointMoveLine.svg | 1560 ++++++++++++++++ .../static/flareElementPointMovePie.svg | 642 +++++++ .../static/flareElementPointMoveRadar.svg | 1273 +++++++++++++ ...lare-element-point-move-area-normalizeY.ts | 44 + .../static/flare-element-point-move-area.ts | 34 + ...oint-move-interval-normalizeY-transpose.ts | 44 + ...-element-point-move-interval-normalizeY.ts | 43 + ...e-element-point-move-interval-transpose.ts | 40 + .../flare-element-point-move-interval.ts | 39 + .../static/flare-element-point-move-line.ts | 41 + .../static/flare-element-point-move-pie.ts | 36 + .../static/flare-element-point-move-radar.ts | 64 + __tests__/plots/static/index.ts | 9 + __tests__/unit/lib/core.spec.ts | 2 + __tests__/unit/lib/std.spec.ts | 2 + .../data/demo/area-element-point-move.ts | 41 + .../area-normalizeY-element-point-move.ts | 50 + .../data/demo/bar-element-point-move.ts | 43 + .../demo/bar-normalizeY-element-point-move.ts | 45 + .../data/demo/column-element-point-move.ts | 43 + .../column-normalizeY-element-point-move.ts | 44 + .../demo/line-element-point-move-polar.ts | 73 + .../data/demo/line-element-point-move.ts | 72 + site/examples/interaction/data/demo/meta.json | 80 + .../data/demo/pie-element-point-move.ts | 38 + site/examples/interaction/data/index.en.md | 4 + site/examples/interaction/data/index.zh.md | 4 + src/interaction/elementPointMove.ts | 658 +++++++ src/interaction/index.ts | 1 + src/interaction/treemapDrillDown.ts | 6 +- src/interaction/utils.ts | 52 + src/lib/core.ts | 2 + 38 files changed, 14195 insertions(+), 5 deletions(-) create mode 100644 __tests__/integration/snapshots/static/flareElementPointMoveArea.svg create mode 100644 __tests__/integration/snapshots/static/flareElementPointMoveAreaNormalizeY.svg create mode 100644 __tests__/integration/snapshots/static/flareElementPointMoveInterval.svg create mode 100644 __tests__/integration/snapshots/static/flareElementPointMoveIntervalNormalizeY.svg create mode 100644 __tests__/integration/snapshots/static/flareElementPointMoveIntervalNormalizeYTranspose.svg create mode 100644 __tests__/integration/snapshots/static/flareElementPointMoveIntervalTranspose.svg create mode 100644 __tests__/integration/snapshots/static/flareElementPointMoveLine.svg create mode 100644 __tests__/integration/snapshots/static/flareElementPointMovePie.svg create mode 100644 __tests__/integration/snapshots/static/flareElementPointMoveRadar.svg create mode 100644 __tests__/plots/static/flare-element-point-move-area-normalizeY.ts create mode 100644 __tests__/plots/static/flare-element-point-move-area.ts create mode 100644 __tests__/plots/static/flare-element-point-move-interval-normalizeY-transpose.ts create mode 100644 __tests__/plots/static/flare-element-point-move-interval-normalizeY.ts create mode 100644 __tests__/plots/static/flare-element-point-move-interval-transpose.ts create mode 100644 __tests__/plots/static/flare-element-point-move-interval.ts create mode 100644 __tests__/plots/static/flare-element-point-move-line.ts create mode 100644 __tests__/plots/static/flare-element-point-move-pie.ts create mode 100644 __tests__/plots/static/flare-element-point-move-radar.ts create mode 100644 site/examples/interaction/data/demo/area-element-point-move.ts create mode 100644 site/examples/interaction/data/demo/area-normalizeY-element-point-move.ts create mode 100644 site/examples/interaction/data/demo/bar-element-point-move.ts create mode 100644 site/examples/interaction/data/demo/bar-normalizeY-element-point-move.ts create mode 100644 site/examples/interaction/data/demo/column-element-point-move.ts create mode 100644 site/examples/interaction/data/demo/column-normalizeY-element-point-move.ts create mode 100644 site/examples/interaction/data/demo/line-element-point-move-polar.ts create mode 100644 site/examples/interaction/data/demo/line-element-point-move.ts create mode 100644 site/examples/interaction/data/demo/meta.json create mode 100644 site/examples/interaction/data/demo/pie-element-point-move.ts create mode 100644 site/examples/interaction/data/index.en.md create mode 100644 site/examples/interaction/data/index.zh.md create mode 100644 src/interaction/elementPointMove.ts diff --git a/__tests__/integration/snapshots/static/flareElementPointMoveArea.svg b/__tests__/integration/snapshots/static/flareElementPointMoveArea.svg new file mode 100644 index 0000000000..dbc78a9fbf --- /dev/null +++ b/__tests__/integration/snapshots/static/flareElementPointMoveArea.svg @@ -0,0 +1,1619 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + type1 + + + + + + + + + + + + + + + + + + + + type2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1991 + + + + + + + 1992 + + + + + + + 1993 + + + + + + + 1994 + + + + + + + 1995 + + + + + + + 1996 + + + + + + + 1997 + + + + + + + 1998 + + + + + + + 1999 + + + + + + + + + year + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + + + + 2 + + + + + + + 4 + + + + + + + 6 + + + + + + + 8 + + + + + + + 10 + + + + + + + 12 + + + + + + + 14 + + + + + + + + + value + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/__tests__/integration/snapshots/static/flareElementPointMoveAreaNormalizeY.svg b/__tests__/integration/snapshots/static/flareElementPointMoveAreaNormalizeY.svg new file mode 100644 index 0000000000..d0012c8ad7 --- /dev/null +++ b/__tests__/integration/snapshots/static/flareElementPointMoveAreaNormalizeY.svg @@ -0,0 +1,1586 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + type1 + + + + + + + + + + + + + + + + + + + + type2 + + + + + + + + + + + + + + + + + + + + type3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1991 + + + + + + + 1992 + + + + + + + 1993 + + + + + + + 1994 + + + + + + + 1995 + + + + + + + 1996 + + + + + + + 1997 + + + + + + + 1998 + + + + + + + 1999 + + + + + + + + + year + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + + + + 0.2 + + + + + + + 0.4 + + + + + + + 0.6 + + + + + + + 0.8 + + + + + + + 1 + + + + + + + + + value + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/__tests__/integration/snapshots/static/flareElementPointMoveInterval.svg b/__tests__/integration/snapshots/static/flareElementPointMoveInterval.svg new file mode 100644 index 0000000000..7bd5b58052 --- /dev/null +++ b/__tests__/integration/snapshots/static/flareElementPointMoveInterval.svg @@ -0,0 +1,1407 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + London + + + + + + + + + + + + + + + + + + + + Berlin + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Jan. + + + + + + + Feb. + + + + + + + Mar. + + + + + + + Apr. + + + + + + + May + + + + + + + Jun. + + + + + + + Jul. + + + + + + + Aug. + + + + + + + + + 月份 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + + + + 50 + + + + + + + 100 + + + + + + + 150 + + + + + + + + + 月均降雨量 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/__tests__/integration/snapshots/static/flareElementPointMoveIntervalNormalizeY.svg b/__tests__/integration/snapshots/static/flareElementPointMoveIntervalNormalizeY.svg new file mode 100644 index 0000000000..67dc454117 --- /dev/null +++ b/__tests__/integration/snapshots/static/flareElementPointMoveIntervalNormalizeY.svg @@ -0,0 +1,1529 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + London + + + + + + + + + + + + + + + + + + + + Berlin + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + May + + + + + + + Mar. + + + + + + + Jun. + + + + + + + Jul. + + + + + + + Jan. + + + + + + + Feb. + + + + + + + Aug. + + + + + + + Apr. + + + + + + + + + 月份 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + + + + 0.2 + + + + + + + 0.4 + + + + + + + 0.6 + + + + + + + 0.8 + + + + + + + 1 + + + + + + + + + 月均降雨量 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/__tests__/integration/snapshots/static/flareElementPointMoveIntervalNormalizeYTranspose.svg b/__tests__/integration/snapshots/static/flareElementPointMoveIntervalNormalizeYTranspose.svg new file mode 100644 index 0000000000..2b723e7a7b --- /dev/null +++ b/__tests__/integration/snapshots/static/flareElementPointMoveIntervalNormalizeYTranspose.svg @@ -0,0 +1,1527 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + London + + + + + + + + + + + + + + + + + + + + Berlin + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + May + + + + + + + Mar. + + + + + + + Jun. + + + + + + + Jul. + + + + + + + Jan. + + + + + + + Feb. + + + + + + + Aug. + + + + + + + Apr. + + + + + + + + + 月份 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + + + + 0.2 + + + + + + + 0.4 + + + + + + + 0.6 + + + + + + + 0.8 + + + + + + + 1 + + + + + + + + + 月均降雨量 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/__tests__/integration/snapshots/static/flareElementPointMoveIntervalTranspose.svg b/__tests__/integration/snapshots/static/flareElementPointMoveIntervalTranspose.svg new file mode 100644 index 0000000000..5638f6056e --- /dev/null +++ b/__tests__/integration/snapshots/static/flareElementPointMoveIntervalTranspose.svg @@ -0,0 +1,1403 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + London + + + + + + + + + + + + + + + + + + + + Berlin + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Jan. + + + + + + + Feb. + + + + + + + Mar. + + + + + + + Apr. + + + + + + + May + + + + + + + Jun. + + + + + + + Jul. + + + + + + + Aug. + + + + + + + + + 月份 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + + + + 50 + + + + + + + 100 + + + + + + + 150 + + + + + + + + + 月均降雨量 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/__tests__/integration/snapshots/static/flareElementPointMoveLine.svg b/__tests__/integration/snapshots/static/flareElementPointMoveLine.svg new file mode 100644 index 0000000000..b3b2d488c4 --- /dev/null +++ b/__tests__/integration/snapshots/static/flareElementPointMoveLine.svg @@ -0,0 +1,1560 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + type1 + + + + + + + + + + + + + + + + + + + + type2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1991 + + + + + + + 1992 + + + + + + + 1993 + + + + + + + 1994 + + + + + + + 1995 + + + + + + + 1996 + + + + + + + 1997 + + + + + + + 1998 + + + + + + + 1999 + + + + + + + + + year + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 2 + + + + + + + 4 + + + + + + + 6 + + + + + + + 8 + + + + + + + 10 + + + + + + + 12 + + + + + + + 14 + + + + + + + + + value + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/__tests__/integration/snapshots/static/flareElementPointMovePie.svg b/__tests__/integration/snapshots/static/flareElementPointMovePie.svg new file mode 100644 index 0000000000..1b08bc0f58 --- /dev/null +++ b/__tests__/integration/snapshots/static/flareElementPointMovePie.svg @@ -0,0 +1,642 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 事例一 + + + + + + + + + + + + + + + + + + + + 事例二 + + + + + + + + + + + + + + + + + + + + 事例三 + + + + + + + + + + + + + + + + + + + + 事例四 + + + + + + + + + + + + + + + + + + + + 事例五 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/__tests__/integration/snapshots/static/flareElementPointMoveRadar.svg b/__tests__/integration/snapshots/static/flareElementPointMoveRadar.svg new file mode 100644 index 0000000000..912beb33b2 --- /dev/null +++ b/__tests__/integration/snapshots/static/flareElementPointMoveRadar.svg @@ -0,0 +1,1273 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + a + + + + + + + + + + + + + + + + + + + + b + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Design + + + + + + + Development + + + + + + + Marketing + + + + + + + Users + + + + + + + Test + + + + + + + Language + + + + + + + Technology + + + + + + + Support + + + + + + + Sales + + + + + + + UX + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + + + + 20 + + + + + + + 40 + + + + + + + 60 + + + + + + + 80 + + + + + + + + + + + + \ No newline at end of file diff --git a/__tests__/plots/static/flare-element-point-move-area-normalizeY.ts b/__tests__/plots/static/flare-element-point-move-area-normalizeY.ts new file mode 100644 index 0000000000..35aad2f185 --- /dev/null +++ b/__tests__/plots/static/flare-element-point-move-area-normalizeY.ts @@ -0,0 +1,44 @@ +import { G2Spec } from '../../../src'; + +export function flareElementPointMoveAreaNormalizeY(): G2Spec { + return { + type: 'area', + width: 600, + height: 400, + data: [ + { year: '1991', value: 3, type: 'type1' }, + { year: '1992', value: 4, type: 'type1' }, + { year: '1993', value: 3.5, type: 'type1' }, + { year: '1994', value: 5, type: 'type1' }, + { year: '1995', value: 4.9, type: 'type1' }, + { year: '1996', value: 2, type: 'type1' }, + { year: '1997', value: 7, type: 'type1' }, + { year: '1998', value: 11, type: 'type1' }, + { year: '1999', value: 13, type: 'type1' }, + { year: '1991', value: 6, type: 'type2' }, + { year: '1992', value: 1, type: 'type2' }, + { year: '1993', value: 4, type: 'type2' }, + { year: '1994', value: 9, type: 'type2' }, + { year: '1995', value: 1.9, type: 'type2' }, + { year: '1996', value: 5, type: 'type2' }, + { year: '1997', value: 4, type: 'type2' }, + { year: '1998', value: 6, type: 'type2' }, + { year: '1999', value: 15, type: 'type2' }, + { year: '1991', value: 6, type: 'type3' }, + { year: '1992', value: 1, type: 'type3' }, + { year: '1993', value: 4, type: 'type3' }, + { year: '1994', value: 9, type: 'type3' }, + { year: '1995', value: 1.9, type: 'type3' }, + { year: '1996', value: 5, type: 'type3' }, + { year: '1997', value: 4, type: 'type3' }, + { year: '1998', value: 6, type: 'type3' }, + { year: '1999', value: 15, type: 'type3' }, + ], + encode: { x: 'year', y: 'value', key: 'type', color: 'type' }, + transform: [{ type: 'stackY' }, { type: 'normalizeY' }], + interaction: { + legendFilter: false, + elementPointMove: { selection: [1, 4] }, + }, + }; +} diff --git a/__tests__/plots/static/flare-element-point-move-area.ts b/__tests__/plots/static/flare-element-point-move-area.ts new file mode 100644 index 0000000000..85dfee6701 --- /dev/null +++ b/__tests__/plots/static/flare-element-point-move-area.ts @@ -0,0 +1,34 @@ +import { G2Spec } from '../../../src'; + +export function flareElementPointMoveArea(): G2Spec { + return { + type: 'area', + width: 600, + height: 400, + data: [ + { year: '1991', value: 3, type: 'type1' }, + { year: '1992', value: 4, type: 'type1' }, + { year: '1993', value: 3.5, type: 'type1' }, + { year: '1994', value: 5, type: 'type1' }, + { year: '1995', value: 4.9, type: 'type1' }, + { year: '1996', value: 2, type: 'type1' }, + { year: '1997', value: 7, type: 'type1' }, + { year: '1998', value: 11, type: 'type1' }, + { year: '1999', value: 13, type: 'type1' }, + { year: '1991', value: 6, type: 'type2' }, + { year: '1992', value: 1, type: 'type2' }, + { year: '1993', value: 4, type: 'type2' }, + { year: '1994', value: 9, type: 'type2' }, + { year: '1995', value: 1.9, type: 'type2' }, + { year: '1996', value: 5, type: 'type2' }, + { year: '1997', value: 4, type: 'type2' }, + { year: '1998', value: 6, type: 'type2' }, + { year: '1999', value: 15, type: 'type2' }, + ], + encode: { x: 'year', y: 'value', key: 'type', color: 'type' }, + interaction: { + legendFilter: false, + elementPointMove: { selection: [1, 4] }, + }, + }; +} diff --git a/__tests__/plots/static/flare-element-point-move-interval-normalizeY-transpose.ts b/__tests__/plots/static/flare-element-point-move-interval-normalizeY-transpose.ts new file mode 100644 index 0000000000..efc7e8934c --- /dev/null +++ b/__tests__/plots/static/flare-element-point-move-interval-normalizeY-transpose.ts @@ -0,0 +1,44 @@ +import { G2Spec } from '../../../src'; + +export function flareElementPointMoveIntervalNormalizeYTranspose(): G2Spec { + return { + type: 'interval', + width: 600, + height: 400, + coordinate: { transform: [{ type: 'transpose' }] }, + data: [ + { name: 'London', 月份: 'Jan.', 月均降雨量: 18.9 }, + { name: 'London', 月份: 'Feb.', 月均降雨量: 28.8 }, + { name: 'London', 月份: 'Mar.', 月均降雨量: 39.3 }, + { name: 'London', 月份: 'Apr.', 月均降雨量: 81.4 }, + { name: 'London', 月份: 'May', 月均降雨量: 47 }, + { name: 'London', 月份: 'Jun.', 月均降雨量: 20.3 }, + { name: 'London', 月份: 'Jul.', 月均降雨量: 24 }, + { name: 'London', 月份: 'Aug.', 月均降雨量: 35.6 }, + { name: 'Berlin', 月份: 'Jan.', 月均降雨量: 12.4 }, + { name: 'Berlin', 月份: 'Feb.', 月均降雨量: 23.2 }, + { name: 'Berlin', 月份: 'Mar.', 月均降雨量: 34.5 }, + { name: 'Berlin', 月份: 'Apr.', 月均降雨量: 99.7 }, + { name: 'Berlin', 月份: 'May', 月均降雨量: 52.6 }, + { name: 'Berlin', 月份: 'Jun.', 月均降雨量: 35.5 }, + { name: 'Berlin', 月份: 'Jul.', 月均降雨量: 37.4 }, + { name: 'Berlin', 月份: 'Aug.', 月均降雨量: 42.4 }, + ], + encode: { + x: '月份', + y: '月均降雨量', + color: 'name', + key: (d) => d['name'] + d['月份'], + }, + transform: [ + { type: 'stackY' }, + { type: 'normalizeY' }, + { type: 'sortX', by: 'y', reverse: true }, + ], + interaction: { + elementPointMove: { + selection: [6], + }, + }, + }; +} diff --git a/__tests__/plots/static/flare-element-point-move-interval-normalizeY.ts b/__tests__/plots/static/flare-element-point-move-interval-normalizeY.ts new file mode 100644 index 0000000000..3a7cf40eb0 --- /dev/null +++ b/__tests__/plots/static/flare-element-point-move-interval-normalizeY.ts @@ -0,0 +1,43 @@ +import { G2Spec } from '../../../src'; + +export function flareElementPointMoveIntervalNormalizeY(): G2Spec { + return { + type: 'interval', + width: 600, + height: 400, + data: [ + { name: 'London', 月份: 'Jan.', 月均降雨量: 18.9 }, + { name: 'London', 月份: 'Feb.', 月均降雨量: 28.8 }, + { name: 'London', 月份: 'Mar.', 月均降雨量: 39.3 }, + { name: 'London', 月份: 'Apr.', 月均降雨量: 81.4 }, + { name: 'London', 月份: 'May', 月均降雨量: 47 }, + { name: 'London', 月份: 'Jun.', 月均降雨量: 20.3 }, + { name: 'London', 月份: 'Jul.', 月均降雨量: 24 }, + { name: 'London', 月份: 'Aug.', 月均降雨量: 35.6 }, + { name: 'Berlin', 月份: 'Jan.', 月均降雨量: 12.4 }, + { name: 'Berlin', 月份: 'Feb.', 月均降雨量: 23.2 }, + { name: 'Berlin', 月份: 'Mar.', 月均降雨量: 34.5 }, + { name: 'Berlin', 月份: 'Apr.', 月均降雨量: 99.7 }, + { name: 'Berlin', 月份: 'May', 月均降雨量: 52.6 }, + { name: 'Berlin', 月份: 'Jun.', 月均降雨量: 35.5 }, + { name: 'Berlin', 月份: 'Jul.', 月均降雨量: 37.4 }, + { name: 'Berlin', 月份: 'Aug.', 月均降雨量: 42.4 }, + ], + encode: { + x: '月份', + y: '月均降雨量', + color: 'name', + key: (d) => d['name'] + d['月份'], + }, + transform: [ + { type: 'stackY' }, + { type: 'normalizeY' }, + { type: 'sortX', by: 'y', reverse: true }, + ], + interaction: { + elementPointMove: { + selection: [6], + }, + }, + }; +} diff --git a/__tests__/plots/static/flare-element-point-move-interval-transpose.ts b/__tests__/plots/static/flare-element-point-move-interval-transpose.ts new file mode 100644 index 0000000000..d34903e269 --- /dev/null +++ b/__tests__/plots/static/flare-element-point-move-interval-transpose.ts @@ -0,0 +1,40 @@ +import { G2Spec } from '../../../src'; + +export function flareElementPointMoveIntervalTranspose(): G2Spec { + return { + type: 'interval', + width: 600, + height: 400, + coordinate: { transform: [{ type: 'transpose' }] }, + data: [ + { name: 'London', 月份: 'Jan.', 月均降雨量: 18.9 }, + { name: 'London', 月份: 'Feb.', 月均降雨量: 28.8 }, + { name: 'London', 月份: 'Mar.', 月均降雨量: 39.3 }, + { name: 'London', 月份: 'Apr.', 月均降雨量: 81.4 }, + { name: 'London', 月份: 'May', 月均降雨量: 47 }, + { name: 'London', 月份: 'Jun.', 月均降雨量: 20.3 }, + { name: 'London', 月份: 'Jul.', 月均降雨量: 24 }, + { name: 'London', 月份: 'Aug.', 月均降雨量: 35.6 }, + { name: 'Berlin', 月份: 'Jan.', 月均降雨量: 12.4 }, + { name: 'Berlin', 月份: 'Feb.', 月均降雨量: 23.2 }, + { name: 'Berlin', 月份: 'Mar.', 月均降雨量: 34.5 }, + { name: 'Berlin', 月份: 'Apr.', 月均降雨量: 99.7 }, + { name: 'Berlin', 月份: 'May', 月均降雨量: 52.6 }, + { name: 'Berlin', 月份: 'Jun.', 月均降雨量: 35.5 }, + { name: 'Berlin', 月份: 'Jul.', 月均降雨量: 37.4 }, + { name: 'Berlin', 月份: 'Aug.', 月均降雨量: 42.4 }, + ], + encode: { + x: '月份', + y: '月均降雨量', + color: 'name', + key: (d) => d['name'] + d['月份'], + }, + transform: [{ type: 'stackY' }], + interaction: { + elementPointMove: { + selection: [6], + }, + }, + }; +} diff --git a/__tests__/plots/static/flare-element-point-move-interval.ts b/__tests__/plots/static/flare-element-point-move-interval.ts new file mode 100644 index 0000000000..7d3812fd5f --- /dev/null +++ b/__tests__/plots/static/flare-element-point-move-interval.ts @@ -0,0 +1,39 @@ +import { G2Spec } from '../../../src'; + +export function flareElementPointMoveInterval(): G2Spec { + return { + type: 'interval', + width: 600, + height: 400, + data: [ + { name: 'London', 月份: 'Jan.', 月均降雨量: 18.9 }, + { name: 'London', 月份: 'Feb.', 月均降雨量: 28.8 }, + { name: 'London', 月份: 'Mar.', 月均降雨量: 39.3 }, + { name: 'London', 月份: 'Apr.', 月均降雨量: 81.4 }, + { name: 'London', 月份: 'May', 月均降雨量: 47 }, + { name: 'London', 月份: 'Jun.', 月均降雨量: 20.3 }, + { name: 'London', 月份: 'Jul.', 月均降雨量: 24 }, + { name: 'London', 月份: 'Aug.', 月均降雨量: 35.6 }, + { name: 'Berlin', 月份: 'Jan.', 月均降雨量: 12.4 }, + { name: 'Berlin', 月份: 'Feb.', 月均降雨量: 23.2 }, + { name: 'Berlin', 月份: 'Mar.', 月均降雨量: 34.5 }, + { name: 'Berlin', 月份: 'Apr.', 月均降雨量: 99.7 }, + { name: 'Berlin', 月份: 'May', 月均降雨量: 52.6 }, + { name: 'Berlin', 月份: 'Jun.', 月均降雨量: 35.5 }, + { name: 'Berlin', 月份: 'Jul.', 月均降雨量: 37.4 }, + { name: 'Berlin', 月份: 'Aug.', 月均降雨量: 42.4 }, + ], + encode: { + x: '月份', + y: '月均降雨量', + color: 'name', + key: (d) => d['name'] + d['月份'], + }, + transform: [{ type: 'stackY' }], + interaction: { + elementPointMove: { + selection: [6], + }, + }, + }; +} diff --git a/__tests__/plots/static/flare-element-point-move-line.ts b/__tests__/plots/static/flare-element-point-move-line.ts new file mode 100644 index 0000000000..d9283336f2 --- /dev/null +++ b/__tests__/plots/static/flare-element-point-move-line.ts @@ -0,0 +1,41 @@ +import { G2Spec } from '../../../src'; + +export function flareElementPointMoveLine(): G2Spec { + return { + type: 'line', + width: 600, + height: 400, + data: [ + { year: '1991', value: 3, type: 'type1' }, + { year: '1992', value: 4, type: 'type1' }, + { year: '1993', value: 3.5, type: 'type1' }, + { year: '1994', value: 5, type: 'type1' }, + { year: '1995', value: 4.9, type: 'type1' }, + { year: '1996', value: 2, type: 'type1' }, + { year: '1997', value: 7, type: 'type1' }, + { year: '1998', value: 11, type: 'type1' }, + { year: '1999', value: 13, type: 'type1' }, + { year: '1991', value: 6, type: 'type2' }, + { year: '1992', value: 1, type: 'type2' }, + { year: '1993', value: 4, type: 'type2' }, + { year: '1994', value: 9, type: 'type2' }, + { year: '1995', value: 1.9, type: 'type2' }, + { year: '1996', value: 5, type: 'type2' }, + { year: '1997', value: 4, type: 'type2' }, + { year: '1998', value: 6, type: 'type2' }, + { year: '1999', value: 15, type: 'type2' }, + ], + encode: { + x: 'year', + y: 'value', + color: 'type', + key: 'type', + }, + interaction: { + elementPointMove: { + precision: 4, + selection: [1, 4], + }, + }, + }; +} diff --git a/__tests__/plots/static/flare-element-point-move-pie.ts b/__tests__/plots/static/flare-element-point-move-pie.ts new file mode 100644 index 0000000000..12e1826593 --- /dev/null +++ b/__tests__/plots/static/flare-element-point-move-pie.ts @@ -0,0 +1,36 @@ +import { G2Spec } from '../../../src'; + +export function flareElementPointMovePie(): G2Spec { + return { + type: 'interval', + width: 600, + height: 400, + data: [ + { item: '事例一', count: 40, percent: 0.4 }, + { item: '事例二', count: 21, percent: 0.21 }, + { item: '事例三', count: 17, percent: 0.17 }, + { item: '事例四', count: 13, percent: 0.13 }, + { item: '事例五', count: 9, percent: 0.09 }, + ], + encode: { y: 'count', color: 'item', key: 'item' }, + transform: [{ type: 'stackY' }], + coordinate: { type: 'theta', outerRadius: 0.8 }, + tooltip: { + items: [ + (data) => ({ + name: data.item, + value: `${data.percent * 100}%`, + }), + ], + }, + interaction: { + legendFilter: false, + elementPointMove: { + selection: [2], + pathLineDash: [2, 4], + pathStroke: '#fff', + pathLineWidth: 2, + }, + }, + }; +} diff --git a/__tests__/plots/static/flare-element-point-move-radar.ts b/__tests__/plots/static/flare-element-point-move-radar.ts new file mode 100644 index 0000000000..2330a514c9 --- /dev/null +++ b/__tests__/plots/static/flare-element-point-move-radar.ts @@ -0,0 +1,64 @@ +import { G2Spec } from '../../../src'; + +export function flareElementPointMoveRadar(): G2Spec { + return { + type: 'view', + width: 600, + height: 400, + data: [ + { item: 'Design', type: 'a', score: 70 }, + { item: 'Design', type: 'b', score: 30 }, + { item: 'Development', type: 'a', score: 60 }, + { item: 'Development', type: 'b', score: 70 }, + { item: 'Marketing', type: 'a', score: 50 }, + { item: 'Marketing', type: 'b', score: 60 }, + { item: 'Users', type: 'a', score: 40 }, + { item: 'Users', type: 'b', score: 50 }, + { item: 'Test', type: 'a', score: 60 }, + { item: 'Test', type: 'b', score: 70 }, + { item: 'Language', type: 'a', score: 70 }, + { item: 'Language', type: 'b', score: 50 }, + { item: 'Technology', type: 'a', score: 50 }, + { item: 'Technology', type: 'b', score: 40 }, + { item: 'Support', type: 'a', score: 30 }, + { item: 'Support', type: 'b', score: 40 }, + { item: 'Sales', type: 'a', score: 60 }, + { item: 'Sales', type: 'b', score: 40 }, + { item: 'UX', type: 'a', score: 50 }, + { item: 'UX', type: 'b', score: 60 }, + ], + coordinate: { type: 'polar' }, + scale: { + x: { padding: 0.5, align: 0 }, + y: { tickCount: 5, domainMax: 80 }, + }, + axis: { + x: { grid: true, gridStrokeWidth: 1, tick: false, gridLineDash: [0, 0] }, + y: { + zIndex: 1, + title: false, + gridConnect: 'line', + gridStrokeWidth: 1, + gridLineDash: [0, 0], + }, + }, + children: [ + { + type: 'area', + encode: { x: 'item', y: 'score', color: 'type', key: 'type' }, + style: { fillOpacity: 0.5 }, + }, + { + type: 'line', + encode: { x: 'item', y: 'score', color: 'type', key: 'type' }, + style: { lineWidth: 2 }, + }, + ], + interaction: { + elementPointMove: { + precision: 4, + selection: [1, 4], + }, + }, + }; +} diff --git a/__tests__/plots/static/index.ts b/__tests__/plots/static/index.ts index cb15d80681..956912b23c 100644 --- a/__tests__/plots/static/index.ts +++ b/__tests__/plots/static/index.ts @@ -322,3 +322,12 @@ export { mockComplexRadar } from './mock-complex-radar'; export { aaplLineIllegalDataSlider } from './aapl-line-illegal-data-slider'; export { mockSquareRadar } from './mock-square-radar'; export { aaplLineSliderValues } from './aapl-line-slider-values'; +export { flareElementPointMoveLine } from './flare-element-point-move-line'; +export { flareElementPointMoveInterval } from './flare-element-point-move-interval'; +export { flareElementPointMoveIntervalTranspose } from './flare-element-point-move-interval-transpose'; +export { flareElementPointMoveIntervalNormalizeY } from './flare-element-point-move-interval-normalizeY'; +export { flareElementPointMoveIntervalNormalizeYTranspose } from './flare-element-point-move-interval-normalizeY-transpose'; +export { flareElementPointMoveRadar } from './flare-element-point-move-radar'; +export { flareElementPointMovePie } from './flare-element-point-move-pie'; +export { flareElementPointMoveArea } from './flare-element-point-move-area'; +export { flareElementPointMoveAreaNormalizeY } from './flare-element-point-move-area-normalizeY'; diff --git a/__tests__/unit/lib/core.spec.ts b/__tests__/unit/lib/core.spec.ts index 6869e4b3f5..bd96bdf056 100644 --- a/__tests__/unit/lib/core.spec.ts +++ b/__tests__/unit/lib/core.spec.ts @@ -85,6 +85,7 @@ import { ElementHighlightByX, ElementSelect, ElementSelectByColor, + ElementPointMove, ElementSelectByX, Fisheye as ChartFisheye, ChartIndex, @@ -287,6 +288,7 @@ describe('corelib', () => { 'interaction.elementSelect': ElementSelect, 'interaction.elementSelectByX': ElementSelectByX, 'interaction.elementSelectByColor': ElementSelectByColor, + 'interaction.elementPointMove': ElementPointMove, 'interaction.fisheye': ChartFisheye, 'interaction.chartIndex': ChartIndex, 'interaction.tooltip': Tooltip, diff --git a/__tests__/unit/lib/std.spec.ts b/__tests__/unit/lib/std.spec.ts index b8ebf9c8da..8619f8ae0e 100644 --- a/__tests__/unit/lib/std.spec.ts +++ b/__tests__/unit/lib/std.spec.ts @@ -96,6 +96,7 @@ import { ElementSelect, ElementSelectByColor, ElementSelectByX, + ElementPointMove, Fisheye as ChartFisheye, ChartIndex, Tooltip, @@ -315,6 +316,7 @@ describe('stdlib', () => { 'interaction.elementSelect': ElementSelect, 'interaction.elementSelectByX': ElementSelectByX, 'interaction.elementSelectByColor': ElementSelectByColor, + 'interaction.elementPointMove': ElementPointMove, 'interaction.fisheye': ChartFisheye, 'interaction.chartIndex': ChartIndex, 'interaction.tooltip': Tooltip, diff --git a/site/examples/interaction/data/demo/area-element-point-move.ts b/site/examples/interaction/data/demo/area-element-point-move.ts new file mode 100644 index 0000000000..361ef47b1c --- /dev/null +++ b/site/examples/interaction/data/demo/area-element-point-move.ts @@ -0,0 +1,41 @@ +import { Chart } from '@antv/g2'; + +const chart = new Chart({ + container: 'container', + autoFit: true, +}); + +chart + .area() + .data([ + { year: '1991', value: 3, type: 'type1' }, + { year: '1992', value: 4, type: 'type1' }, + { year: '1993', value: 3.5, type: 'type1' }, + { year: '1994', value: 5, type: 'type1' }, + { year: '1995', value: 4.9, type: 'type1' }, + { year: '1996', value: 2, type: 'type1' }, + { year: '1997', value: 7, type: 'type1' }, + { year: '1998', value: 11, type: 'type1' }, + { year: '1999', value: 13, type: 'type1' }, + { year: '1991', value: 6, type: 'type2' }, + { year: '1992', value: 1, type: 'type2' }, + { year: '1993', value: 4, type: 'type2' }, + { year: '1994', value: 9, type: 'type2' }, + { year: '1995', value: 1.9, type: 'type2' }, + { year: '1996', value: 5, type: 'type2' }, + { year: '1997', value: 4, type: 'type2' }, + { year: '1998', value: 6, type: 'type2' }, + { year: '1999', value: 15, type: 'type2' }, + ]) + .interaction({ + legendFilter: false, + elementPointMove: { + selection: [1, 4], + }, + }) + .encode('x', 'year') + .encode('y', 'value') + .encode('key', 'type') + .encode('color', 'type'); + +chart.render(); diff --git a/site/examples/interaction/data/demo/area-normalizeY-element-point-move.ts b/site/examples/interaction/data/demo/area-normalizeY-element-point-move.ts new file mode 100644 index 0000000000..b1bb0e2a64 --- /dev/null +++ b/site/examples/interaction/data/demo/area-normalizeY-element-point-move.ts @@ -0,0 +1,50 @@ +import { Chart } from '@antv/g2'; + +const chart = new Chart({ + container: 'container', + autoFit: true, +}); + +chart + .area() + .data([ + { year: '1991', value: 3, type: 'type1' }, + { year: '1992', value: 4, type: 'type1' }, + { year: '1993', value: 3.5, type: 'type1' }, + { year: '1994', value: 5, type: 'type1' }, + { year: '1995', value: 4.9, type: 'type1' }, + { year: '1996', value: 2, type: 'type1' }, + { year: '1997', value: 7, type: 'type1' }, + { year: '1998', value: 11, type: 'type1' }, + { year: '1999', value: 13, type: 'type1' }, + { year: '1991', value: 6, type: 'type2' }, + { year: '1992', value: 1, type: 'type2' }, + { year: '1993', value: 4, type: 'type2' }, + { year: '1994', value: 9, type: 'type2' }, + { year: '1995', value: 1.9, type: 'type2' }, + { year: '1996', value: 5, type: 'type2' }, + { year: '1997', value: 4, type: 'type2' }, + { year: '1998', value: 6, type: 'type2' }, + { year: '1999', value: 15, type: 'type2' }, + { year: '1991', value: 6, type: 'type3' }, + { year: '1992', value: 1, type: 'type3' }, + { year: '1993', value: 4, type: 'type3' }, + { year: '1994', value: 9, type: 'type3' }, + { year: '1995', value: 1.9, type: 'type3' }, + { year: '1996', value: 5, type: 'type3' }, + { year: '1997', value: 4, type: 'type3' }, + { year: '1998', value: 6, type: 'type3' }, + { year: '1999', value: 15, type: 'type3' }, + ]) + .interaction({ + legendFilter: false, + elementPointMove: true, + }) + .transform({ type: 'stackY' }) + .transform({ type: 'normalizeY' }) + .encode('x', 'year') + .encode('y', 'value') + .encode('key', 'type') + .encode('color', 'type'); + +chart.render(); diff --git a/site/examples/interaction/data/demo/bar-element-point-move.ts b/site/examples/interaction/data/demo/bar-element-point-move.ts new file mode 100644 index 0000000000..933efb8325 --- /dev/null +++ b/site/examples/interaction/data/demo/bar-element-point-move.ts @@ -0,0 +1,43 @@ +import { Chart } from '@antv/g2'; + +const data = [ + { name: 'London', 月份: 'Jan.', 月均降雨量: 18.9 }, + { name: 'London', 月份: 'Feb.', 月均降雨量: 28.8 }, + { name: 'London', 月份: 'Mar.', 月均降雨量: 39.3 }, + { name: 'London', 月份: 'Apr.', 月均降雨量: 81.4 }, + { name: 'London', 月份: 'May', 月均降雨量: 47 }, + { name: 'London', 月份: 'Jun.', 月均降雨量: 20.3 }, + { name: 'London', 月份: 'Jul.', 月均降雨量: 24 }, + { name: 'London', 月份: 'Aug.', 月均降雨量: 35.6 }, + { name: 'Berlin', 月份: 'Jan.', 月均降雨量: 12.4 }, + { name: 'Berlin', 月份: 'Feb.', 月均降雨量: 23.2 }, + { name: 'Berlin', 月份: 'Mar.', 月均降雨量: 34.5 }, + { name: 'Berlin', 月份: 'Apr.', 月均降雨量: 99.7 }, + { name: 'Berlin', 月份: 'May', 月均降雨量: 52.6 }, + { name: 'Berlin', 月份: 'Jun.', 月均降雨量: 35.5 }, + { name: 'Berlin', 月份: 'Jul.', 月均降雨量: 37.4 }, + { name: 'Berlin', 月份: 'Aug.', 月均降雨量: 42.4 }, +]; + +const chart = new Chart({ + container: 'container', + autoFit: true, +}); + +chart + .interval() + .data(data) + .encode('x', '月份') + .encode('y', '月均降雨量') + .encode('color', 'name') + .encode('key', (d) => d['name'] + d['月份']) + .interaction({ + legendFilter: false, + elementPointMove: { + precision: 3, + }, + }) + .transform({ type: 'stackY' }) + .coordinate({ transform: [{ type: 'transpose' }] }); + +chart.render(); diff --git a/site/examples/interaction/data/demo/bar-normalizeY-element-point-move.ts b/site/examples/interaction/data/demo/bar-normalizeY-element-point-move.ts new file mode 100644 index 0000000000..d35d717cbb --- /dev/null +++ b/site/examples/interaction/data/demo/bar-normalizeY-element-point-move.ts @@ -0,0 +1,45 @@ +import { Chart } from '@antv/g2'; + +const data = [ + { name: 'London', 月份: 'Jan.', 月均降雨量: 18.9 }, + { name: 'London', 月份: 'Feb.', 月均降雨量: 28.8 }, + { name: 'London', 月份: 'Mar.', 月均降雨量: 39.3 }, + { name: 'London', 月份: 'Apr.', 月均降雨量: 81.4 }, + { name: 'London', 月份: 'May', 月均降雨量: 47 }, + { name: 'London', 月份: 'Jun.', 月均降雨量: 20.3 }, + { name: 'London', 月份: 'Jul.', 月均降雨量: 24 }, + { name: 'London', 月份: 'Aug.', 月均降雨量: 35.6 }, + { name: 'Berlin', 月份: 'Jan.', 月均降雨量: 12.4 }, + { name: 'Berlin', 月份: 'Feb.', 月均降雨量: 23.2 }, + { name: 'Berlin', 月份: 'Mar.', 月均降雨量: 34.5 }, + { name: 'Berlin', 月份: 'Apr.', 月均降雨量: 99.7 }, + { name: 'Berlin', 月份: 'May', 月均降雨量: 52.6 }, + { name: 'Berlin', 月份: 'Jun.', 月均降雨量: 35.5 }, + { name: 'Berlin', 月份: 'Jul.', 月均降雨量: 37.4 }, + { name: 'Berlin', 月份: 'Aug.', 月均降雨量: 42.4 }, +]; + +const chart = new Chart({ + container: 'container', + autoFit: true, +}); + +chart + .interval() + .data(data) + .transform({ type: 'stackY' }) + .transform({ type: 'normalizeY' }) + .transform({ type: 'sortX', by: 'y', reverse: true }) + .encode('x', '月份') + .encode('y', '月均降雨量') + .encode('color', 'name') + .encode('key', (d) => d['name'] + d['月份']) + .interaction({ + legendFilter: false, + elementPointMove: { + precision: 3, + }, + }) + .coordinate({ transform: [{ type: 'transpose' }] }); + +chart.render(); diff --git a/site/examples/interaction/data/demo/column-element-point-move.ts b/site/examples/interaction/data/demo/column-element-point-move.ts new file mode 100644 index 0000000000..855e6aa9e8 --- /dev/null +++ b/site/examples/interaction/data/demo/column-element-point-move.ts @@ -0,0 +1,43 @@ +import { Chart } from '@antv/g2'; + +const data = [ + { name: 'London', 月份: 'Jan.', 月均降雨量: 18.9 }, + { name: 'London', 月份: 'Feb.', 月均降雨量: 28.8 }, + { name: 'London', 月份: 'Mar.', 月均降雨量: 39.3 }, + { name: 'London', 月份: 'Apr.', 月均降雨量: 81.4 }, + { name: 'London', 月份: 'May', 月均降雨量: 47 }, + { name: 'London', 月份: 'Jun.', 月均降雨量: 20.3 }, + { name: 'London', 月份: 'Jul.', 月均降雨量: 24 }, + { name: 'London', 月份: 'Aug.', 月均降雨量: 35.6 }, + { name: 'Berlin', 月份: 'Jan.', 月均降雨量: 12.4 }, + { name: 'Berlin', 月份: 'Feb.', 月均降雨量: 23.2 }, + { name: 'Berlin', 月份: 'Mar.', 月均降雨量: 34.5 }, + { name: 'Berlin', 月份: 'Apr.', 月均降雨量: 99.7 }, + { name: 'Berlin', 月份: 'May', 月均降雨量: 52.6 }, + { name: 'Berlin', 月份: 'Jun.', 月均降雨量: 35.5 }, + { name: 'Berlin', 月份: 'Jul.', 月均降雨量: 37.4 }, + { name: 'Berlin', 月份: 'Aug.', 月均降雨量: 42.4 }, +]; + +const chart = new Chart({ + container: 'container', + autoFit: true, +}); + +chart + .interval() + .data(data) + .encode('x', '月份') + .encode('y', '月均降雨量') + .encode('color', 'name') + .encode('key', (d) => d['name'] + d['月份']) + .interaction({ + legendFilter: false, + elementPointMove: { + precision: 3, + selection: [5], + }, + }) + .transform({ type: 'stackY' }); + +chart.render(); diff --git a/site/examples/interaction/data/demo/column-normalizeY-element-point-move.ts b/site/examples/interaction/data/demo/column-normalizeY-element-point-move.ts new file mode 100644 index 0000000000..e90b39209e --- /dev/null +++ b/site/examples/interaction/data/demo/column-normalizeY-element-point-move.ts @@ -0,0 +1,44 @@ +import { Chart } from '@antv/g2'; + +const data = [ + { name: 'London', 月份: 'Jan.', 月均降雨量: 18.9 }, + { name: 'London', 月份: 'Feb.', 月均降雨量: 28.8 }, + { name: 'London', 月份: 'Mar.', 月均降雨量: 39.3 }, + { name: 'London', 月份: 'Apr.', 月均降雨量: 81.4 }, + { name: 'London', 月份: 'May', 月均降雨量: 47 }, + { name: 'London', 月份: 'Jun.', 月均降雨量: 20.3 }, + { name: 'London', 月份: 'Jul.', 月均降雨量: 24 }, + { name: 'London', 月份: 'Aug.', 月均降雨量: 35.6 }, + { name: 'Berlin', 月份: 'Jan.', 月均降雨量: 12.4 }, + { name: 'Berlin', 月份: 'Feb.', 月均降雨量: 23.2 }, + { name: 'Berlin', 月份: 'Mar.', 月均降雨量: 34.5 }, + { name: 'Berlin', 月份: 'Apr.', 月均降雨量: 99.7 }, + { name: 'Berlin', 月份: 'May', 月均降雨量: 52.6 }, + { name: 'Berlin', 月份: 'Jun.', 月均降雨量: 35.5 }, + { name: 'Berlin', 月份: 'Jul.', 月均降雨量: 37.4 }, + { name: 'Berlin', 月份: 'Aug.', 月均降雨量: 42.4 }, +]; + +const chart = new Chart({ + container: 'container', + autoFit: true, +}); + +chart + .interval() + .data(data) + .transform({ type: 'stackY' }) + .transform({ type: 'normalizeY' }) + .transform({ type: 'sortX', by: 'y', reverse: true }) + .encode('x', '月份') + .encode('y', '月均降雨量') + .encode('color', 'name') + .encode('key', (d) => d['name'] + d['月份']) + .interaction({ + legendFilter: false, + elementPointMove: { + precision: 3, + }, + }); + +chart.render(); diff --git a/site/examples/interaction/data/demo/line-element-point-move-polar.ts b/site/examples/interaction/data/demo/line-element-point-move-polar.ts new file mode 100644 index 0000000000..521bd0a72e --- /dev/null +++ b/site/examples/interaction/data/demo/line-element-point-move-polar.ts @@ -0,0 +1,73 @@ +import { Chart } from '@antv/g2'; + +const data = [ + { item: 'Design', type: 'a', score: 70 }, + { item: 'Design', type: 'b', score: 30 }, + { item: 'Development', type: 'a', score: 60 }, + { item: 'Development', type: 'b', score: 70 }, + { item: 'Marketing', type: 'a', score: 50 }, + { item: 'Marketing', type: 'b', score: 60 }, + { item: 'Users', type: 'a', score: 40 }, + { item: 'Users', type: 'b', score: 50 }, + { item: 'Test', type: 'a', score: 60 }, + { item: 'Test', type: 'b', score: 70 }, + { item: 'Language', type: 'a', score: 70 }, + { item: 'Language', type: 'b', score: 50 }, + { item: 'Technology', type: 'a', score: 50 }, + { item: 'Technology', type: 'b', score: 40 }, + { item: 'Support', type: 'a', score: 30 }, + { item: 'Support', type: 'b', score: 40 }, + { item: 'Sales', type: 'a', score: 60 }, + { item: 'Sales', type: 'b', score: 40 }, + { item: 'UX', type: 'a', score: 50 }, + { item: 'UX', type: 'b', score: 60 }, +]; + +const chart = new Chart({ + container: 'container', + autoFit: true, +}); + +chart.coordinate({ type: 'polar' }); + +chart + .data(data) + .scale('x', { padding: 0.5, align: 0 }) + .scale('y', { tickCount: 5, domainMax: 80 }) + .interaction({ + legendFilter: false, + elementPointMove: true, + }) + .axis('x', { + grid: true, + gridStrokeWidth: 1, + tick: false, + gridLineDash: [0, 0], + }) + .axis('y', { + zIndex: 1, + title: false, + gridConnect: 'line', + gridStrokeWidth: 1, + gridLineDash: [0, 0], + }); + +chart + .area() + .encode('x', 'item') + .encode('y', 'score') + .encode('color', 'type') + .encode('key', 'type') + .style('fillOpacity', 0.5); + +chart + .line() + .encode('x', 'item') + .encode('y', 'score') + .encode('color', 'type') + .encode('key', 'type') + .style('lineWidth', 2); + +chart.interaction('tooltip', { crosshairsLineDash: [4, 4] }); + +chart.render(); diff --git a/site/examples/interaction/data/demo/line-element-point-move.ts b/site/examples/interaction/data/demo/line-element-point-move.ts new file mode 100644 index 0000000000..bdd9da9504 --- /dev/null +++ b/site/examples/interaction/data/demo/line-element-point-move.ts @@ -0,0 +1,72 @@ +import { Chart } from '@antv/g2'; + +const chart = new Chart({ + container: 'container', + autoFit: true, +}); + +chart + .line() + .data([ + { year: '1991', value: 3, type: 'type1' }, + { year: '1992', value: 4, type: 'type1' }, + { year: '1993', value: 3.5, type: 'type1' }, + { year: '1994', value: 5, type: 'type1' }, + { year: '1995', value: 4.9, type: 'type1' }, + { year: '1996', value: 2, type: 'type1' }, + { year: '1997', value: 7, type: 'type1' }, + { year: '1998', value: 11, type: 'type1' }, + { year: '1999', value: 13, type: 'type1' }, + { year: '1991', value: 6, type: 'type2' }, + { year: '1992', value: 1, type: 'type2' }, + { year: '1993', value: 4, type: 'type2' }, + { year: '1994', value: 9, type: 'type2' }, + { year: '1995', value: 1.9, type: 'type2' }, + { year: '1996', value: 5, type: 'type2' }, + { year: '1997', value: 4, type: 'type2' }, + { year: '1998', value: 6, type: 'type2' }, + { year: '1999', value: 15, type: 'type2' }, + ]) + .interaction({ + legendFilter: false, + elementPointMove: { + pointR: 8, + pointStrokeWidth: 2, + pointActiveStroke: '#fff', + pathLineDash: [2, 4], + pathStroke: 'red', + labelFontSize: 14, + labelY: 24, + }, + }) + .encode('x', 'year') + .encode('y', 'value') + .encode('key', 'type') + .encode('color', 'type'); + +chart.render().then(() => { + chart.on('element-point:select', (v) => { + const { + data: { selection }, + } = v; + console.log(selection, 'selection'); + }); + + chart.on('element-point:moved', (v) => { + const { + data: { changeData, data }, + } = v; + console.log(changeData, 'changeData'); + console.log(data, 'data'); + }); +}); + +chart.on('afterrender', () => { + chart.emit('element-point:select', { + data: { + selection: [1, 2], + }, + }); + // Clear select. + // chart.emit('element-point:unselect'); +}); diff --git a/site/examples/interaction/data/demo/meta.json b/site/examples/interaction/data/demo/meta.json new file mode 100644 index 0000000000..d2e35b173f --- /dev/null +++ b/site/examples/interaction/data/demo/meta.json @@ -0,0 +1,80 @@ +{ + "title": { + "zh": "中文分类", + "en": "Category" + }, + "demos": [ + { + "filename": "line-element-point-move.ts", + "title": { + "zh": "折线图-数形交互", + "en": "Line Numerical Interaction" + }, + "screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*XO4yQqSSeSoAAAAAAAAAAAAADmJ7AQ/original" + }, + { + "filename": "area-element-point-move.ts", + "title": { + "zh": "面积图-数形交互", + "en": "Area Numerical Interaction" + }, + "screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*FTm7T5CfHsUAAAAAAAAAAAAADmJ7AQ/original" + }, + { + "filename": "area-normalizeY-element-point-move.ts", + "title": { + "zh": "面积图-百分比堆叠-数形交互", + "en": "Area NormalizeY Numerical Interaction" + }, + "screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*xWt3R5fzwH4AAAAAAAAAAAAADmJ7AQ/original" + }, + { + "filename": "column-element-point-move.ts", + "title": { + "zh": "柱形图-数形交互", + "en": "Column Numerical Interaction" + }, + "screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*fZn9TYPR2mcAAAAAAAAAAAAADmJ7AQ/original" + }, + { + "filename": "column-normalizeY-element-point-move.ts", + "title": { + "zh": "柱形图-百分比堆叠-数形交互", + "en": "Column NormalizeY Numerical Interaction" + }, + "screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*G5a8T7lP0ZEAAAAAAAAAAAAADmJ7AQ/original" + }, + { + "filename": "bar-element-point-move.ts", + "title": { + "zh": "条形图-数形交互", + "en": "Bar Numerical Interaction" + }, + "screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*WQnwTJ-gJq0AAAAAAAAAAAAADmJ7AQ/original" + }, + { + "filename": "bar-normalizeY-element-point-move.ts", + "title": { + "zh": "条形图-百分比堆叠-数形交互", + "en": "Bar NormalizeY Numerical Interaction" + }, + "screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*SDtmQrDx7cAAAAAAAAAAAAAADmJ7AQ/original" + }, + { + "filename": "line-element-point-move-polar.ts", + "title": { + "zh": "雷达图-数形交互", + "en": "Radar Numerical Interaction" + }, + "screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*ifgPT565BPkAAAAAAAAAAAAADmJ7AQ/original" + }, + { + "filename": "pie-element-point-move.ts", + "title": { + "zh": "饼图-数形交互", + "en": "Pie Numerical Interaction" + }, + "screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*Hm0PR5262sgAAAAAAAAAAAAADmJ7AQ/original" + } + ] +} diff --git a/site/examples/interaction/data/demo/pie-element-point-move.ts b/site/examples/interaction/data/demo/pie-element-point-move.ts new file mode 100644 index 0000000000..1dcb6cb425 --- /dev/null +++ b/site/examples/interaction/data/demo/pie-element-point-move.ts @@ -0,0 +1,38 @@ +import { Chart } from '@antv/g2'; + +const data = [ + { item: '事例一', count: 40, percent: 0.4 }, + { item: '事例二', count: 21, percent: 0.21 }, + { item: '事例三', count: 17, percent: 0.17 }, + { item: '事例四', count: 13, percent: 0.13 }, + { item: '事例五', count: 9, percent: 0.09 }, +]; + +const chart = new Chart({ + container: 'container', + autoFit: true, +}); + +chart.coordinate({ type: 'theta', outerRadius: 0.8 }); + +chart + .interval() + .data(data) + .transform({ type: 'stackY' }) + .interaction({ + legendFilter: false, + elementPointMove: { + pathLineDash: [2, 4], + pathStroke: '#fff', + pathLineWidth: 2, + }, + }) + .encode('y', 'count') + .encode('color', 'item') + .encode('key', 'item') + .tooltip((data) => ({ + name: data.item, + value: `${data.percent * 100}%`, + })); + +chart.render(); diff --git a/site/examples/interaction/data/index.en.md b/site/examples/interaction/data/index.en.md new file mode 100644 index 0000000000..ca057c69cc --- /dev/null +++ b/site/examples/interaction/data/index.en.md @@ -0,0 +1,4 @@ +--- +title: Numerical Interaction +order: 4 +--- \ No newline at end of file diff --git a/site/examples/interaction/data/index.zh.md b/site/examples/interaction/data/index.zh.md new file mode 100644 index 0000000000..2b81c30219 --- /dev/null +++ b/site/examples/interaction/data/index.zh.md @@ -0,0 +1,4 @@ +--- +title: 数形交互 +order: 4 +--- diff --git a/src/interaction/elementPointMove.ts b/src/interaction/elementPointMove.ts new file mode 100644 index 0000000000..8aa76470a5 --- /dev/null +++ b/src/interaction/elementPointMove.ts @@ -0,0 +1,658 @@ +import { Text, Group, Circle, Path } from '@antv/g'; +import { deepMix, isUndefined, find, get } from '@antv/util'; +import type { CircleStyleProps, TextStyleProps, PathStyleProps } from '@antv/g'; +import { subObject } from '../utils/helper'; + +import { + selectPlotArea, + getPointsR, + getPointsPath, + getElements, + getThetaPath, +} from './utils'; + +export type ElementPointMoveOptions = { + selection?: number[]; + precision?: number; + [key: string]: any; +}; + +const DEFAULT_STYLE = { + pointR: 6, + pointStrokeWidth: 1, + pointStroke: '#888', + pointActiveStroke: '#f5f5f5', + pathStroke: '#888', + pathLineDash: [3, 4], + labelFontSize: 12, + labelFill: '#888', + labelStroke: '#fff', + labelLineWidth: 1, + labelY: -6, + labelX: 2, +}; + +// point shape name. +const MOVE_POINT_NAME = 'movePoint'; + +// Element mouseenter change style. +const elementMouseenter = (e) => { + const element = e.target; + const { markType } = element; + // Mark line. + if (markType === 'line') { + element.attr('_lineWidth', element.attr('lineWidth') || 1); + element.attr('lineWidth', element.attr('_lineWidth') + 3); + } + // Mark interval. + if (markType === 'interval') { + element.attr('_opacity', element.attr('opacity') || 1); + element.attr('opacity', 0.7 * element.attr('_opacity')); + } +}; + +// Element mouseleave change style. +const elementMouseleave = (e) => { + const element = e.target; + const { markType } = element; + // Mark line. + if (markType === 'line') { + element.attr('lineWidth', element.attr('_lineWidth')); + } + // Mark interval. + if (markType === 'interval') { + element.attr('opacity', element.attr('_opacity')); + } +}; + +// Get the latest overall data based on the individual data changes. +const getNewData = (newChangeData, data, encode) => { + return data.map((d) => { + const isUpdate = ['x', 'color'].reduce((v, key) => { + const field = encode[key]; + if (!field) return v; + + if (d[field] !== newChangeData[field]) return false; + + return v; + }, true); + + return isUpdate ? { ...d, ...newChangeData } : d; + }); +}; + +// Find mark interval origin element data. +const getIntervalDataRatioTransformFn = (element) => { + const y = get(element, ['__data__', 'y']); + const y1 = get(element, ['__data__', 'y1']); + const v = y1 - y; + + const { + __data__: { data, encode, transform }, + childNodes, + } = element.parentNode; + const isNormalizeY = find(transform, ({ type }) => type === 'normalizeY'); + const yField = get(encode, ['y', 'field']); + const value = data[childNodes.indexOf(element)][yField]; + + return (newValue, isTheta = false) => { + if (isNormalizeY || isTheta) { + return (newValue / (1 - newValue) / (v / (1 - v))) * value; + } + + return newValue; + }; +}; + +// Find origin path data. +const getPathDataRatioTransformFn = (element, index) => { + const v = get(element, ['__data__', 'seriesItems', index, '0', 'value']); + const i = get(element, ['__data__', 'seriesIndex', index]); + + const { + __data__: { data, encode, transform }, + } = element.parentNode; + const isNormalizeY = find(transform, ({ type }) => type === 'normalizeY'); + const yField = get(encode, ['y', 'field']); + const value = data[i][yField]; + + return (newValue) => { + if (isNormalizeY) { + if (v === 1) { + return newValue; + } + return (newValue / (1 - newValue) / (v / (1 - v))) * value; + } + + return newValue; + }; +}; + +// Point shape select change style. +const selectedPointsStyle = (pointsShape, selection, defaultStyle) => { + pointsShape.forEach((shape, index) => { + shape.attr( + 'stroke', + selection[1] === index + ? defaultStyle['activeStroke'] + : defaultStyle['stroke'], + ); + }); +}; + +// Create help show message shape. +const createHelpShape = ( + group, + circle, + pathStyle, + labelStyle, +): [Path, Text] => { + const pathShape = new Path({ + style: pathStyle, + }); + + const labelShape = new Text({ + style: labelStyle, + }); + + circle.appendChild(labelShape); + group.appendChild(pathShape); + return [pathShape, labelShape]; +}; + +// Get color scale type. +const getColorType = (scaleColor, color) => { + const indexOf = get(scaleColor, ['options', 'range', 'indexOf']); + if (!indexOf) return; + const i = scaleColor.options.range.indexOf(color); + return scaleColor.sortedDomain[i]; +}; + +// Get the same direction new point. +const getSamePointPosition = (center, point, target) => { + const oldR = getPointsR(center, point); + const newR = getPointsR(center, target); + const ratio = newR / oldR; + const newX = center[0] + (point[0] - center[0]) * ratio; + const newY = center[1] + (point[1] - center[1]) * ratio; + return [newX, newY]; +}; + +/** + * ElementPointMove interaction. + */ +export function ElementPointMove( + elementPointMoveOptions: ElementPointMoveOptions = {}, +) { + const { selection = [], precision = 2, ...style } = elementPointMoveOptions; + + const defaultStyle = { ...DEFAULT_STYLE, ...(style || {}) }; + + // Shape default style. + const pathDefaultStyle = subObject(defaultStyle, 'path') as PathStyleProps; + const labelDefaultStyle = subObject(defaultStyle, 'label') as TextStyleProps; + const pointDefaultStyle = subObject( + defaultStyle, + 'point', + ) as CircleStyleProps; + + return (context, _, emitter) => { + const { + update, + setState, + container, + view, + options: { marks, coordinate: coordinateOptions }, + } = context; + const plotArea = selectPlotArea(container); + let elements = getElements(plotArea); + let newState; + let newSelection = selection; + + const { transform = [], type: coordinateType } = coordinateOptions; + const isTranspose = !!find(transform, ({ type }) => type === 'transpose'); + const isPolar = coordinateType === 'polar'; + const isTheta = coordinateType === 'theta'; + const isArea = !!find(elements, ({ markType }) => markType === 'area'); + + if (isArea) { + elements = elements.filter(({ markType }) => markType === 'area'); + } + + // Create points + const pointsGroup = new Group({ + style: { + // Tooltip point need down. + zIndex: 2, + }, + }); + plotArea.appendChild(pointsGroup); + + const selectedChange = () => { + emitter.emit('element-point:select', { + nativeEvent: true, + data: { + selection: newSelection, + }, + }); + }; + + const dataChange = (changeData, data) => { + emitter.emit('element-point:moved', { + nativeEvent: true, + data: { + changeData, + data, + }, + }); + }; + + // Element click change style. + const elementClick = (e) => { + const element = e.target; + newSelection = [element.parentNode.childNodes.indexOf(element)]; + selectedChange(); + createPoints(element); + }; + + const elementSelect = (d) => { + const { + data: { selection }, + nativeEvent, + } = d; + if (nativeEvent) return; + newSelection = selection; + const element = get(elements, [newSelection?.[0]]); + if (element) { + createPoints(element); + } + }; + + // Create select element points. + const createPoints = (element) => { + const { attributes, markType, __data__: data } = element; + const { stroke: fill } = attributes; + const { points, seriesTitle, color, title, seriesX } = data; + // Transpose Currently only do mark interval; + if (isTranspose && markType !== 'interval') return; + + const { scale, coordinate } = newState?.view || view; + const { color: scaleColor, y: scaleY } = scale; + const center = coordinate.getCenter(); + + pointsGroup.removeChildren(); + let downPoint; + + const updateView = async (x, y, color, markTypes) => { + setState('elementPointMove', (viewOptions) => { + // Update marks. + const newMarks = (newState?.options?.marks || marks).map((mark) => { + if (!markTypes.includes(mark.type)) return mark; + const { data, encode } = mark; + const encodeKeys = Object.keys(encode); + + // Get change new one element data. + const newChangeData = encodeKeys.reduce((value, key) => { + const dataKey = encode[key]; + if (key === 'x') { + value[dataKey] = x; + } + if (key === 'y') { + value[dataKey] = y; + } + if (key === 'color') { + value[dataKey] = color; + } + return value; + }, {} as any); + // Get change new all data. + const newData = getNewData(newChangeData, data, encode); + dataChange(newChangeData, newData); + + return deepMix({}, mark, { + data: newData, + // No need animate + animate: false, + }); + }); + + return { ...viewOptions, marks: newMarks }; + }); + + return await update('elementPointMove'); + }; + + if (['line', 'area'].includes(markType)) { + points.forEach((p, index) => { + const title = seriesTitle[index]; + // Area points have bottom point. + if (!title) return; + + const circle = new Circle({ + name: MOVE_POINT_NAME, + style: { + cx: p[0], + cy: p[1], + fill, + ...pointDefaultStyle, + }, + }); + + const ratioTransform = getPathDataRatioTransformFn(element, index); + + circle.addEventListener('mousedown', (e) => { + const oldPoint = coordinate.output([seriesX[index], 0]); + const pathLength = seriesTitle?.length; + + container.attr('cursor', 'move'); + + if (newSelection[1] !== index) { + newSelection[1] = index; + selectedChange(); + } + selectedPointsStyle( + pointsGroup.childNodes, + newSelection, + pointDefaultStyle, + ); + + const [pathShape, labelShape] = createHelpShape( + pointsGroup, + circle, + pathDefaultStyle, + labelDefaultStyle, + ); + + // Point move change text + const pointMousemove = (e) => { + const newCy = p[1] + e.clientY - downPoint[1]; + // Area/Radar chart. + if (isArea) { + // Radar chart. + if (isPolar) { + const newCx = p[0] + e.clientX - downPoint[0]; + + const [newX, newY] = getSamePointPosition(center, oldPoint, [ + newCx, + newCy, + ]); + + const [, initY] = coordinate.output([1, scaleY.output(0)]); + const [, y] = coordinate.invert([ + newX, + initY - (points[index + pathLength][1] - newY), + ]); + + const nextIndex = (index + 1) % pathLength; + const lastIndex = (index - 1 + pathLength) % pathLength; + const newPath = getPointsPath([ + points[lastIndex], + [newX, newY], + seriesTitle[nextIndex] && points[nextIndex], + ]); + labelShape.attr( + 'text', + ratioTransform(scaleY.invert(y)).toFixed(precision), + ); + pathShape.attr('path', newPath); + circle.attr('cx', newX); + circle.attr('cy', newY); + } else { + // Area chart. + const [, initY] = coordinate.output([1, scaleY.output(0)]); + const [, y] = coordinate.invert([ + p[0], + initY - (points[index + pathLength][1] - newCy), + ]); + const newPath = getPointsPath([ + points[index - 1], + [p[0], newCy], + seriesTitle[index + 1] && points[index + 1], + ]); + labelShape.attr( + 'text', + ratioTransform(scaleY.invert(y)).toFixed(precision), + ); + pathShape.attr('path', newPath); + circle.attr('cy', newCy); + } + } else { + // Line chart. + const [, y] = coordinate.invert([p[0], newCy]); + const newPath = getPointsPath([ + points[index - 1], + [p[0], newCy], + points[index + 1], + ]); + labelShape.attr('text', scaleY.invert(y).toFixed(precision)); + pathShape.attr('path', newPath); + circle.attr('cy', newCy); + } + }; + + downPoint = [e.clientX, e.clientY]; + window.addEventListener('mousemove', pointMousemove); + + const mouseupFn = async () => { + container.attr('cursor', 'default'); + window.removeEventListener('mousemove', pointMousemove); + container.removeEventListener('mouseup', mouseupFn); + + if (isUndefined(labelShape.attr('text'))) return; + + const y = Number(labelShape.attr('text')); + const colorType = getColorType(scaleColor, color); + newState = await updateView(title, y, colorType, [ + 'line', + 'area', + ]); + + labelShape.remove(); + pathShape.remove(); + createPoints(element); + }; + + container.addEventListener('mouseup', mouseupFn); + }); + + pointsGroup.appendChild(circle); + }); + + selectedPointsStyle( + pointsGroup.childNodes, + newSelection, + pointDefaultStyle, + ); + } else if (markType === 'interval') { + // Column chart point. + let circlePoint = [(points[0][0] + points[1][0]) / 2, points[0][1]]; + // Bar chart point. + if (isTranspose) { + circlePoint = [points[0][0], (points[0][1] + points[1][1]) / 2]; + } else if (isTheta) { + // Pie chart point. + circlePoint = points[0]; + } + + const ratioTransform = getIntervalDataRatioTransformFn(element); + + const circle = new Circle({ + name: MOVE_POINT_NAME, + style: { + cx: circlePoint[0], + cy: circlePoint[1], + fill, + ...pointDefaultStyle, + stroke: pointDefaultStyle['activeStroke'], + }, + }); + + circle.addEventListener('mousedown', (e) => { + container.attr('cursor', 'move'); + + const colorType = getColorType(scaleColor, color); + + const [pathShape, labelShape] = createHelpShape( + pointsGroup, + circle, + pathDefaultStyle, + labelDefaultStyle, + ); + + // Point move change text + const pointMousemove = (e) => { + if (isTranspose) { + // Bar chart. + const newCx = circlePoint[0] + e.clientX - downPoint[0]; + const [initX] = coordinate.output([ + scaleY.output(0), + scaleY.output(0), + ]); + + const [, x] = coordinate.invert([ + initX + (newCx - points[2][0]), + circlePoint[1], + ]); + const newPath = getPointsPath( + [ + [newCx, points[0][1]], + [newCx, points[1][1]], + points[2], + points[3], + ], + true, + ); + + labelShape.attr( + 'text', + ratioTransform(scaleY.invert(x)).toFixed(precision), + ); + pathShape.attr('path', newPath); + circle.attr('cx', newCx); + } else if (isTheta) { + // Pie chart. + const newCy = circlePoint[1] + e.clientY - downPoint[1]; + const newCx = circlePoint[0] + e.clientX - downPoint[0]; + + const [newXOut, newYOut] = getSamePointPosition( + center, + [newCx, newCy], + circlePoint, + ); + const [newXIn, newYIn] = getSamePointPosition( + center, + [newCx, newCy], + points[1], + ); + const lastPercent = coordinate.invert([newXOut, newYOut])[1]; + const nextPercent = coordinate.invert(points[3])[1]; + const percent = nextPercent - lastPercent; + if (percent < 0) return; + const newPath = getThetaPath( + center, + [[newXOut, newYOut], [newXIn, newYIn], points[2], points[3]], + percent > 0.5 ? 1 : 0, + ); + + labelShape.attr( + 'text', + ratioTransform(percent, true).toFixed(precision), + ); + pathShape.attr('path', newPath); + circle.attr('cx', newXOut); + circle.attr('cy', newYOut); + } else { + // Column chart. + const newCy = circlePoint[1] + e.clientY - downPoint[1]; + const [, initY] = coordinate.output([1, scaleY.output(0)]); + + const [, y] = coordinate.invert([ + circlePoint[0], + initY - (points[2][1] - newCy), + ]); + const newPath = getPointsPath( + [ + [points[0][0], newCy], + [points[1][0], newCy], + points[2], + points[3], + ], + true, + ); + + labelShape.attr( + 'text', + ratioTransform(scaleY.invert(y)).toFixed(precision), + ); + pathShape.attr('path', newPath); + circle.attr('cy', newCy); + } + }; + + downPoint = [e.clientX, e.clientY]; + window.addEventListener('mousemove', pointMousemove); + + // Change mosueup change data and update 、clear shape. + const mouseupFn = async () => { + container.attr('cursor', 'default'); + container.removeEventListener('mouseup', mouseupFn); + window.removeEventListener('mousemove', pointMousemove); + + if (isUndefined(labelShape.attr('text'))) return; + + const y = Number(labelShape.attr('text')); + + newState = await updateView(title, y, colorType, [markType]); + + labelShape.remove(); + pathShape.remove(); + createPoints(element); + }; + + container.addEventListener('mouseup', mouseupFn); + }); + + pointsGroup.appendChild(circle); + } + }; + + // Add EventListener. + elements.forEach((element, index) => { + if (newSelection[0] === index) { + createPoints(element); + } + element.addEventListener('click', elementClick); + element.addEventListener('mouseenter', elementMouseenter); + element.addEventListener('mouseleave', elementMouseleave); + }); + + const rootClick = (e) => { + const element = e?.target; + if ( + !element || + (element.name !== MOVE_POINT_NAME && !elements.includes(element)) + ) { + newSelection = []; + selectedChange(); + pointsGroup.removeChildren(); + } + }; + + emitter.on('element-point:select', elementSelect); + emitter.on('element-point:unselect', rootClick); + container.addEventListener('mousedown', rootClick); + + // Remove EventListener. + return () => { + pointsGroup.remove(); + emitter.off('element-point:select', elementSelect); + emitter.off('element-point:unselect', rootClick); + container.removeEventListener('mousedown', rootClick); + elements.forEach((element) => { + element.removeEventListener('click', elementClick); + element.removeEventListener('mouseenter', elementMouseenter); + element.removeEventListener('mouseleave', elementMouseleave); + }); + }; + }; +} diff --git a/src/interaction/index.ts b/src/interaction/index.ts index 43b4631cc8..f7db826c3f 100644 --- a/src/interaction/index.ts +++ b/src/interaction/index.ts @@ -21,3 +21,4 @@ export { ScrollbarFilter } from './scrollbarFilter'; export { Poptip } from './poptip'; export { Event } from './event'; export { TreemapDrillDown } from './treemapDrillDown'; +export { ElementPointMove } from './elementPointMove'; diff --git a/src/interaction/treemapDrillDown.ts b/src/interaction/treemapDrillDown.ts index 7812183a99..57a5fc630c 100644 --- a/src/interaction/treemapDrillDown.ts +++ b/src/interaction/treemapDrillDown.ts @@ -7,11 +7,7 @@ import { PLOT_CLASS_NAME } from '../runtime'; import { select } from '../utils/selection'; import { treeDataTransform } from '../utils/treeDataTransform'; import { legendClearSetState } from './legendFilter'; - -// Get element. -const getElements = (plot) => { - return plot.querySelectorAll('.element'); -}; +import { getElements } from './utils'; function selectPlotArea(root: DisplayObject): DisplayObject { return select(root).select(`.${PLOT_CLASS_NAME}`).node(); diff --git a/src/interaction/utils.ts b/src/interaction/utils.ts index 189d4c13e8..dd3428fcd1 100644 --- a/src/interaction/utils.ts +++ b/src/interaction/utils.ts @@ -2,6 +2,8 @@ import { DisplayObject, Path } from '@antv/g'; import { path as d3Path } from 'd3-path'; import { sort } from 'd3-array'; import { Vector2 } from '@antv/coord'; +import { filter } from '@antv/util'; +import type { PathArray } from '@antv/util'; import { G2Element, select } from '../utils/selection'; import { mapObject } from '../utils/array'; import { @@ -468,3 +470,53 @@ export function selectElementByData(elements, data, datum) { Object.entries(data).every(([key, value]) => datum(d)[key] === value), ); } + +export function getPointsR(point: number[], nextPoint: number[]) { + return Math.sqrt( + Math.pow(point[0] - nextPoint[0], 2) + Math.pow(point[1] - nextPoint[1], 2), + ); +} + +// Points create path. +export function getPointsPath(points: number[][], isClose = false) { + const path = filter(points, (d) => !!d).map((d, i) => { + return [i === 0 ? 'M' : 'L', ...d]; + }) as PathArray; + + if (isClose) { + path.push(['Z']); + } + return path; +} + +// Get element. +export function getElements(plot) { + return plot.querySelectorAll('.element'); +} + +// Get Theta coordinate round path. +export function getThetaPath( + center: number[], + points: number[][], + isBig = 0, +): PathArray { + const path = [['M', ...points[1]]]; + const innerRadius = getPointsR(center, points[1]); + const outerRadius = getPointsR(center, points[0]); + + if (innerRadius === 0) { + path.push( + ['L', ...points[3]], + ['A', outerRadius, outerRadius, 0, isBig, 1, ...points[0]], + ['Z'], + ); + } else { + path.push( + ['A', innerRadius, innerRadius, 0, isBig, 0, ...points[2]], + ['L', ...points[3]], + ['A', outerRadius, outerRadius, 0, isBig, 1, ...points[0]], + ['Z'], + ); + } + return path as PathArray; +} diff --git a/src/lib/core.ts b/src/lib/core.ts index afb9bba9e0..0c95f073d8 100644 --- a/src/lib/core.ts +++ b/src/lib/core.ts @@ -101,6 +101,7 @@ import { Poptip, ScrollbarFilter, TreemapDrillDown, + ElementPointMove, } from '../interaction'; import { SpaceLayer, @@ -301,6 +302,7 @@ export function corelib() { 'interaction.scrollbarFilter': ScrollbarFilter, 'interaction.poptip': Poptip, 'interaction.treemapDrillDown': TreemapDrillDown, + 'interaction.elementPointMove': ElementPointMove, 'composition.spaceLayer': SpaceLayer, 'composition.spaceFlex': SpaceFlex, 'composition.facetRect': FacetRect,