Skip to content

Commit

Permalink
feat: 添加 geometry 的点击选中 (#1383)
Browse files Browse the repository at this point in the history
  • Loading branch information
zengyue committed Mar 9, 2022
1 parent 872ae3c commit 2247ebe
Show file tree
Hide file tree
Showing 27 changed files with 511 additions and 82 deletions.
18 changes: 12 additions & 6 deletions packages/f2/src/components/geometry/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { isFunction, each, upperFirst, mix, groupToMap, isObject, flatten } from '@antv/util';
import Component from '../../base/component';
import Selection, { SelectionState } from './selection';
import * as Adjust from '../../adjust';
import { toTimeStamp } from '../../util/index';
import { GeomType, GeometryProps } from './interface';
Expand All @@ -10,7 +10,10 @@ import { AnimationCycle } from '../../canvas/animation/interface';
// 保留原始数据的字段
const FIELD_ORIGIN = 'origin';

class Geometry<T extends GeometryProps = GeometryProps> extends Component<T> {
class Geometry<
P extends GeometryProps = GeometryProps,
S extends SelectionState = SelectionState
> extends Selection<P, S> {
isGeometry = true;
geomType: GeomType;

Expand All @@ -36,7 +39,7 @@ class Geometry<T extends GeometryProps = GeometryProps> extends Component<T> {
return {};
}

constructor(props, context?) {
constructor(props: P, context?) {
super(props, context);
mix(this, this.getDefaultCfg());

Expand Down Expand Up @@ -92,6 +95,7 @@ class Geometry<T extends GeometryProps = GeometryProps> extends Component<T> {
}

didMount() {
super.didMount();
this._initEvent();
}

Expand Down Expand Up @@ -396,16 +400,18 @@ class Geometry<T extends GeometryProps = GeometryProps> extends Component<T> {
y: normalized.y,
});

// 获取shape的style
// 获取 shape 的 style
const shapeName = attrValues.shape;
const shape = this._getShapeStyle(shapeName, child.origin);
const selected = this.isSelected(child);

mix(child, attrValues, {
normalized,
x,
y,
shapeName,
shape,
selected,
});
}
}
Expand Down Expand Up @@ -493,7 +499,7 @@ class Geometry<T extends GeometryProps = GeometryProps> extends Component<T> {
}, []);
}

getSnapRecords(point) {
getSnapRecords(point): any[] {
const { props } = this;
const { coord, adjust } = props;
const invertPoint = coord.invertPoint(point);
Expand All @@ -506,7 +512,7 @@ class Geometry<T extends GeometryProps = GeometryProps> extends Component<T> {
}

const records = this.flatRecords();

// 处理饼图
if (adjust === 'stack' && coord.isPolar && coord.transposed) {
// 弧度在半径范围内
Expand Down
3 changes: 2 additions & 1 deletion packages/f2/src/components/geometry/interface.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Chart from '../../chart';
import Coord from '../../coord';
import { AnimationCycle } from '../../canvas/animation/interface';
import { SelectionProps } from './selection';

export interface AttrRange {
shape?: any[];
Expand Down Expand Up @@ -32,7 +33,7 @@ export type GeometryAdjust = {

export type AdjustConfig = GeometryAdjust | GeometryAdjustType;

export interface GeometryProps {
export interface GeometryProps extends SelectionProps {
data?: any;
adjust?: AdjustConfig;
chart?: Chart;
Expand Down
154 changes: 154 additions & 0 deletions packages/f2/src/components/geometry/selection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import { isFunction } from '@antv/util';
import Component from '../../base/component';
import { ShapeAttrs, Point } from '../../types';

function isEqual(origin1, origin2, fields: string[]) {
if (origin1 === origin2) {
return true;
}
for (let i = 0, len = fields.length; i < len; i++) {
const field = fields[i];
if (origin1[field] !== origin2[field]) {
return false;
}
}
return true;
}

type StyleType = (record: any) => ShapeAttrs;

export interface SelectionProps {
selection?: {
type?: 'single' | 'multiple';
defaultSelected?: any[];
selectedStyle: ShapeAttrs | StyleType;
unSelectedStyle: ShapeAttrs | StyleType;
cancelable: boolean;
};
[k: string]: any;
}

export interface SelectionState {
selected: any[];
}

class Selection<
P extends SelectionProps = SelectionProps,
S extends SelectionState = SelectionState
> extends Component<P, S> {
constructor(props: P, context) {
super(props, context);

const { selection } = props;
if (!selection) return;
const { defaultSelected } = selection;
this.state.selected = defaultSelected;
}

didMount() {
const { props, state, container } = this;
const canvas = container.get('canvas');
canvas.on('click', (ev) => {
const { selection, chart } = props;
if (!selection) return;
const { points } = ev;
const records = this.getSnapRecords(points[0]);
const { type = 'single', cancelable = true } = selection;
if (!records || !records.length) {
if (cancelable) {
this.setState({
selected: null,
} as S);
}
}

const { selected } = state;
const origins = records.map((record) => record.origin);
if (!selected || !selected.length) {
this.setState({
selected: origins,
} as S);
}

if (type === 'single') {
if (!cancelable) {
this.setState({
selected: origins,
} as S);
return;
}
const newSelected = [];
records.forEach((record) => {
if (!this.isSelected(record)) {
newSelected.push(record.origin);
}
});
this.setState({
selected: newSelected,
} as S);
return;
}

// 多选
const scales = chart.getScales();
const fields = Object.keys(scales);
const selectedMap = {};
selected.forEach((item) => {
const key = fields.map((field) => item[field]).join('-');
selectedMap[key] = item;
});
records.forEach((record) => {
const { origin } = record;
const key = fields.map((field) => origin[field]).join('-');
selectedMap[key] = selectedMap[key] ? null : origin;
});

const newSelected = Object.keys(selectedMap)
.map((key) => selectedMap[key])
.filter(Boolean);

this.setState({
selected: newSelected,
} as S);
});
}

getSnapRecords(_point: Point) {
return null;
}

isSelected(record) {
const { state, props } = this;
const { selected } = state;
if (!selected || !selected.length) {
return false;
}
const { chart } = props;
const scales = chart.getScales();
const fields = Object.keys(scales);
for (let i = 0, len = selected.length; i < len; i++) {
const item = selected[i];
if (isEqual(record.origin, item, fields)) {
return true;
}
}
return false;
}

getSelectionStyle(record) {
const { state, props } = this;
const { selected } = state;
if (!selected || !selected.length) {
return null;
}
const { selection } = props;
const { selectedStyle, unSelectedStyle } = selection;
const isSelected = this.isSelected(record);
if (isSelected) {
return isFunction(selectedStyle) ? selectedStyle(record) : selectedStyle;
}
return isFunction(unSelectedStyle) ? unSelectedStyle(record) : unSelectedStyle;
}
}

export default Selection;
18 changes: 5 additions & 13 deletions packages/f2/src/components/interval/view/polar.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { jsx } from '../../../jsx';
import { deepMix } from '@antv/util';

export default (props) => {
const { coord, records } = props;
const { coord, records, animation } = props;
const { center } = coord;
return (
<group>
Expand All @@ -24,25 +25,16 @@ export default (props) => {
r: yMax,
...shape,
}}
animation={{
// appear: {
// easing: 'linear',
// duration: 450,
// property: ['y', 'height'],
// start: {
// y: yMax,
// height: 0,
// },
// },
animation={deepMix({
update: {
easing: 'linear',
duration: 450,
property: ['x', 'y', 'startAngle', 'endAngle', 'r0', 'r'],
},
}}
})}
/>
);
})}
}, animation)}
</group>
);
})}
Expand Down
33 changes: 18 additions & 15 deletions packages/f2/src/components/interval/view/rect.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { deepMix } from '@antv/util';
import { jsx } from '../../../jsx';

export default props => {
export default (props) => {
const { records, animation } = props;
return (
<group>
Expand All @@ -22,22 +22,25 @@ export default props => {
fill: color,
...shape,
}}
animation={deepMix({
appear: {
easing: 'linear',
duration: 450,
property: ['y', 'height'],
start: {
y: yMax,
height: 0,
animation={deepMix(
{
appear: {
easing: 'linear',
duration: 450,
property: ['y', 'height'],
start: {
y: yMax,
height: 0,
},
},
update: {
easing: 'linear',
duration: 450,
property: ['x', 'y', 'width', 'height'],
},
},
update: {
easing: 'linear',
duration: 450,
property: ['x', 'y', 'width', 'height'],
},
}, animation)}
animation
)}
/>
);
})}
Expand Down
7 changes: 5 additions & 2 deletions packages/f2/src/components/interval/withInterval.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,16 +74,17 @@ export default (Views) => {
} else {
const { x, y } = child;
const rect = { size: mappedSize, x, y, y0 };

mix(child, coord.transformToRect(rect));
}

mix(child.shape, this.getSelectionStyle(child));
}
}
return records;
}

render() {
const { props } = this;
const { props, state } = this;
const { coord, shape = 'rect', animation, showLabel, labelCfg: customLabelCfg } = props;
const View = Views[shape];
const LabelView = LabelViews[shape];
Expand All @@ -97,12 +98,14 @@ export default (Views) => {
);

if (!View) return null;
const { selected } = state;

const records = this.mapping();
return (
<View
coord={coord}
records={records}
selected={selected}
shape={shape}
animation={animation}
showLabel={showLabel}
Expand Down

0 comments on commit 2247ebe

Please sign in to comment.