diff --git a/packages/f2/src/components/geometry/index.tsx b/packages/f2/src/components/geometry/index.tsx index 771837d94..aad379ad6 100644 --- a/packages/f2/src/components/geometry/index.tsx +++ b/packages/f2/src/components/geometry/index.tsx @@ -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'; @@ -10,7 +10,10 @@ import { AnimationCycle } from '../../canvas/animation/interface'; // 保留原始数据的字段 const FIELD_ORIGIN = 'origin'; -class Geometry extends Component { +class Geometry< + P extends GeometryProps = GeometryProps, + S extends SelectionState = SelectionState +> extends Selection { isGeometry = true; geomType: GeomType; @@ -36,7 +39,7 @@ class Geometry extends Component { return {}; } - constructor(props, context?) { + constructor(props: P, context?) { super(props, context); mix(this, this.getDefaultCfg()); @@ -92,6 +95,7 @@ class Geometry extends Component { } didMount() { + super.didMount(); this._initEvent(); } @@ -396,9 +400,10 @@ class Geometry extends Component { 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, @@ -406,6 +411,7 @@ class Geometry extends Component { y, shapeName, shape, + selected, }); } } @@ -493,7 +499,7 @@ class Geometry extends Component { }, []); } - getSnapRecords(point) { + getSnapRecords(point): any[] { const { props } = this; const { coord, adjust } = props; const invertPoint = coord.invertPoint(point); @@ -506,7 +512,7 @@ class Geometry extends Component { } const records = this.flatRecords(); - + // 处理饼图 if (adjust === 'stack' && coord.isPolar && coord.transposed) { // 弧度在半径范围内 diff --git a/packages/f2/src/components/geometry/interface.ts b/packages/f2/src/components/geometry/interface.ts index 26f180acf..524eee6c9 100644 --- a/packages/f2/src/components/geometry/interface.ts +++ b/packages/f2/src/components/geometry/interface.ts @@ -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[]; @@ -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; diff --git a/packages/f2/src/components/geometry/selection.ts b/packages/f2/src/components/geometry/selection.ts new file mode 100644 index 000000000..a6158f443 --- /dev/null +++ b/packages/f2/src/components/geometry/selection.ts @@ -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 { + 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; diff --git a/packages/f2/src/components/interval/view/polar.tsx b/packages/f2/src/components/interval/view/polar.tsx index c2f2ade5b..9434b7ce9 100644 --- a/packages/f2/src/components/interval/view/polar.tsx +++ b/packages/f2/src/components/interval/view/polar.tsx @@ -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 ( @@ -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)} ); })} diff --git a/packages/f2/src/components/interval/view/rect.tsx b/packages/f2/src/components/interval/view/rect.tsx index 188aeb466..18dbcf436 100644 --- a/packages/f2/src/components/interval/view/rect.tsx +++ b/packages/f2/src/components/interval/view/rect.tsx @@ -1,7 +1,7 @@ import { deepMix } from '@antv/util'; import { jsx } from '../../../jsx'; -export default props => { +export default (props) => { const { records, animation } = props; return ( @@ -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 + )} /> ); })} diff --git a/packages/f2/src/components/interval/withInterval.tsx b/packages/f2/src/components/interval/withInterval.tsx index d10ef1ad8..56ac90f44 100644 --- a/packages/f2/src/components/interval/withInterval.tsx +++ b/packages/f2/src/components/interval/withInterval.tsx @@ -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]; @@ -97,12 +98,14 @@ export default (Views) => { ); if (!View) return null; + const { selected } = state; const records = this.mapping(); return ( { : props.items?.length ? props.items : this.getOriginItems(); + if (!renderItems) return null; return renderItems.map((item) => { const { tickValue } = item; return { @@ -125,6 +126,7 @@ export default (View) => { position = 'top', } = props; const items = this.getItems(); + if (!items || !items.length) return; const { left, top, right, bottom, width: layoutWidth, height: layoutHeight } = parentLayout; const width = context.px2hd(customWidth) || layoutWidth; const shape = renderShape(this, this.render(), false); @@ -204,6 +206,8 @@ export default (View) => { } willMount() { + const items = this.getItems(); + if (!items || !items.length) return; this._init(); this.updateCoord(); } @@ -213,6 +217,8 @@ export default (View) => { } willUpdate(): void { + const items = this.getItems(); + if (!items || !items.length) return; this.updateCoord(); } @@ -269,6 +275,9 @@ export default (View) => { render() { const { props, state } = this; const items = this.getItems(); + if (!items || !items.length) { + return null; + } const { itemWidth, style } = state; return ( diff --git a/packages/f2/src/coord/base.ts b/packages/f2/src/coord/base.ts index 974505b92..c8eadd634 100644 --- a/packages/f2/src/coord/base.ts +++ b/packages/f2/src/coord/base.ts @@ -90,9 +90,51 @@ class Base extends Layout { return false; } - // 把归一后的值映射到对应的定义域 + _zoomVal(val, func) { + return isArray(val) ? val.map((v) => func(v)) : func(val); + } + + /** + * 把归一后的值映射到对应的定义域 + * @param point + */ + convert(point) { + const { transposed, x, y } = this; + const xDim = transposed ? 'y' : 'x'; + const yDim = transposed ? 'x' : 'y'; + return { + x: this._zoomVal(point[xDim], (v) => x[0] + (x[1] - x[0]) * v), + y: this._zoomVal(point[yDim], (v) => y[0] + (y[1] - y[0]) * v), + }; + } + + /** + * convert 的反处理,把定义域的值,反处理到归一的值 + */ + invert(point) { + const { transposed, x, y } = this; + const xDim = transposed ? 'y' : 'x'; + const yDim = transposed ? 'x' : 'y'; + return { + [xDim]: this._zoomVal(point.x, (v) => (v - x[0]) / (x[1] - x[0])), + [yDim]: this._zoomVal(point.y, (v) => (v - y[0]) / (y[1] - y[0])), + }; + } + + /** + * 把归一化的值映射到 canvas 的坐标点 + * @param point + * @returns + */ convertPoint(point) { - return point; + return this.convert(point); + } + + /** + * 把canvas坐标的点位映射回归一的值 + */ + invertPoint(point) { + return this.invert(point); } // 将标准坐标系下的矩形绘制关键点映射成实际绘制的坐标点 @@ -120,24 +162,19 @@ class Base extends Layout { // 将已经映射好的矩形绘制关键点转换成实际绘制的坐标点 transformToRect(rectPoint: RectPoint): Rect { const { x, y, y0, size } = rectPoint; - const coordOrigin = this.convertPoint({ x: 0, y: y0 }) + const coordOrigin = this.convertPoint({ x: 0, y: y0 }); const { transposed } = this; const _rectPoint = { size, x: transposed ? y : x, y: transposed ? x : y, y0: transposed ? coordOrigin.x : coordOrigin.y, - } + }; const rect = convertRect(_rectPoint); const { xMin, xMax, yMin, yMax } = transposed ? transposedRect(rect) : rect; return { xMin, xMax, yMin, yMax }; } - - // 把canvas坐标的点位映射回归一后的值 - invertPoint(point) { - return point; - } } export default Base; diff --git a/packages/f2/src/coord/polar.ts b/packages/f2/src/coord/polar.ts index cacfc973e..2f834a787 100644 --- a/packages/f2/src/coord/polar.ts +++ b/packages/f2/src/coord/polar.ts @@ -67,14 +67,15 @@ class Polar extends Base { } invertPoint(point) { - const { center, transposed, x: rangeX, y: rangeY } = this; - const x = { start: rangeX[0], end: rangeX[1] } - const y = { start: rangeY[0], end: rangeY[1] } + const { center, transposed, x, y } = this; const xDim = transposed ? 'y' : 'x'; const yDim = transposed ? 'x' : 'y'; + const [xStart, xEnd] = x; + const [yStart, yEnd] = y; + const m = [1, 0, 0, 1, 0, 0]; - Matrix.rotate(m, m, x.start); + Matrix.rotate(m, m, xStart); let startV = [1, 0]; Vector2.transformMat2d(startV, startV, m); @@ -84,24 +85,23 @@ class Polar extends Base { if (Vector2.zero(pointV)) { return { x: 0, - y: 0 + y: 0, }; } - let theta = Vector2.angleTo(startV, pointV, x.end < x.start); + let theta = Vector2.angleTo(startV, pointV, xEnd < xStart); if (Math.abs(theta - Math.PI * 2) < 0.001) { theta = 0; } const l = Vector2.length(pointV); - let percentX = theta / (x.end - x.start); - percentX = x.end - x.start > 0 ? percentX : -percentX; - const percentY = (l - y.start) / (y.end - y.start); + let percentX = theta / (xEnd - xStart); + percentX = xEnd - xStart > 0 ? percentX : -percentX; + const percentY = (l - yStart) / (yEnd - yStart); const rst = {}; rst[xDim] = percentX; rst[yDim] = percentY; return rst; } - } export default Polar; diff --git a/packages/f2/src/coord/rect.ts b/packages/f2/src/coord/rect.ts index e4c41b7c6..5821c39ad 100644 --- a/packages/f2/src/coord/rect.ts +++ b/packages/f2/src/coord/rect.ts @@ -1,4 +1,3 @@ -import { isArray } from '@antv/util'; import Base from './base'; import { Range, Option } from './types'; @@ -16,30 +15,6 @@ class Rect extends Base { this.y = y; return this; } - - _zoomVal(val, func) { - return isArray(val) ? val.map((v) => func(v)) : func(val); - } - - convertPoint(point) { - const { transposed, x, y } = this; - const xDim = transposed ? 'y' : 'x'; - const yDim = transposed ? 'x' : 'y'; - return { - x: this._zoomVal(point[xDim], (v) => x[0] + (x[1] - x[0]) * v), - y: this._zoomVal(point[yDim], (v) => y[0] + (y[1] - y[0]) * v), - }; - } - - invertPoint(point) { - const { transposed, x, y } = this; - const xDim = transposed ? 'y' : 'x'; - const yDim = transposed ? 'x' : 'y'; - return { - [xDim]: this._zoomVal(point.x, (v) => (v - x[0]) / (x[1] - x[0])), - [yDim]: this._zoomVal(point.y, (v) => (v - y[0]) / (y[1] - y[0])), - }; - } } export default Rect; diff --git a/packages/f2/src/types.ts b/packages/f2/src/types.ts index 91eb03fac..b157d2ffc 100644 --- a/packages/f2/src/types.ts +++ b/packages/f2/src/types.ts @@ -25,11 +25,17 @@ type PX_FIELD_NAME = type pxstr = `${number}px`; export type px = number | pxstr | string; -interface PxPoint { +export type Point = Types.Point; +export interface PxPoint { x: px; y: px; } +export interface Record { + origin: any; + [k: string]: any; +} + export * from '@antv/scale' export type { AxisTypes } @@ -94,6 +100,11 @@ export type ElementType = | (new (props: Props, context?: any) => any); + +export interface ShapeAttrs extends Partial>{ + [k: string]: any; +} + export type CircleAttrs = Partial>; export interface CircleProps extends IntrinsicElementsProps { attrs?: CircleAttrs; diff --git "a/packages/f2/test/components/interval/__image_snapshots__/selected-test-tsx-\346\225\260\346\215\256\351\200\211\344\270\255-\346\237\261\345\233\276-style-\344\270\272\345\207\275\346\225\260-1-snap.png" "b/packages/f2/test/components/interval/__image_snapshots__/selected-test-tsx-\346\225\260\346\215\256\351\200\211\344\270\255-\346\237\261\345\233\276-style-\344\270\272\345\207\275\346\225\260-1-snap.png" new file mode 100644 index 000000000..d882a58a0 Binary files /dev/null and "b/packages/f2/test/components/interval/__image_snapshots__/selected-test-tsx-\346\225\260\346\215\256\351\200\211\344\270\255-\346\237\261\345\233\276-style-\344\270\272\345\207\275\346\225\260-1-snap.png" differ diff --git "a/packages/f2/test/components/interval/__image_snapshots__/selected-test-tsx-\346\225\260\346\215\256\351\200\211\344\270\255-\346\237\261\345\233\276-style-\344\270\272\345\207\275\346\225\260-2-snap.png" "b/packages/f2/test/components/interval/__image_snapshots__/selected-test-tsx-\346\225\260\346\215\256\351\200\211\344\270\255-\346\237\261\345\233\276-style-\344\270\272\345\207\275\346\225\260-2-snap.png" new file mode 100644 index 000000000..1ecf368b4 Binary files /dev/null and "b/packages/f2/test/components/interval/__image_snapshots__/selected-test-tsx-\346\225\260\346\215\256\351\200\211\344\270\255-\346\237\261\345\233\276-style-\344\270\272\345\207\275\346\225\260-2-snap.png" differ diff --git "a/packages/f2/test/components/interval/__image_snapshots__/selected-test-tsx-\346\225\260\346\215\256\351\200\211\344\270\255-\346\237\261\345\233\276-style-\344\270\272\345\207\275\346\225\260-3-snap.png" "b/packages/f2/test/components/interval/__image_snapshots__/selected-test-tsx-\346\225\260\346\215\256\351\200\211\344\270\255-\346\237\261\345\233\276-style-\344\270\272\345\207\275\346\225\260-3-snap.png" new file mode 100644 index 000000000..1ecf368b4 Binary files /dev/null and "b/packages/f2/test/components/interval/__image_snapshots__/selected-test-tsx-\346\225\260\346\215\256\351\200\211\344\270\255-\346\237\261\345\233\276-style-\344\270\272\345\207\275\346\225\260-3-snap.png" differ diff --git "a/packages/f2/test/components/interval/__image_snapshots__/selected-test-tsx-\346\225\260\346\215\256\351\200\211\344\270\255-\346\237\261\345\233\276-\345\215\225\351\200\211-1-snap.png" "b/packages/f2/test/components/interval/__image_snapshots__/selected-test-tsx-\346\225\260\346\215\256\351\200\211\344\270\255-\346\237\261\345\233\276-\345\215\225\351\200\211-1-snap.png" new file mode 100644 index 000000000..aeac90c8b Binary files /dev/null and "b/packages/f2/test/components/interval/__image_snapshots__/selected-test-tsx-\346\225\260\346\215\256\351\200\211\344\270\255-\346\237\261\345\233\276-\345\215\225\351\200\211-1-snap.png" differ diff --git "a/packages/f2/test/components/interval/__image_snapshots__/selected-test-tsx-\346\225\260\346\215\256\351\200\211\344\270\255-\346\237\261\345\233\276-\345\215\225\351\200\211-2-snap.png" "b/packages/f2/test/components/interval/__image_snapshots__/selected-test-tsx-\346\225\260\346\215\256\351\200\211\344\270\255-\346\237\261\345\233\276-\345\215\225\351\200\211-2-snap.png" new file mode 100644 index 000000000..4e4d01021 Binary files /dev/null and "b/packages/f2/test/components/interval/__image_snapshots__/selected-test-tsx-\346\225\260\346\215\256\351\200\211\344\270\255-\346\237\261\345\233\276-\345\215\225\351\200\211-2-snap.png" differ diff --git "a/packages/f2/test/components/interval/__image_snapshots__/selected-test-tsx-\346\225\260\346\215\256\351\200\211\344\270\255-\346\237\261\345\233\276-\345\215\225\351\200\211-3-snap.png" "b/packages/f2/test/components/interval/__image_snapshots__/selected-test-tsx-\346\225\260\346\215\256\351\200\211\344\270\255-\346\237\261\345\233\276-\345\215\225\351\200\211-3-snap.png" new file mode 100644 index 000000000..5ce5a40bc Binary files /dev/null and "b/packages/f2/test/components/interval/__image_snapshots__/selected-test-tsx-\346\225\260\346\215\256\351\200\211\344\270\255-\346\237\261\345\233\276-\345\215\225\351\200\211-3-snap.png" differ diff --git "a/packages/f2/test/components/interval/__image_snapshots__/selected-test-tsx-\346\225\260\346\215\256\351\200\211\344\270\255-\346\237\261\345\233\276-\345\215\225\351\200\211-\344\270\215\345\217\257\345\217\226\346\266\210-1-snap.png" "b/packages/f2/test/components/interval/__image_snapshots__/selected-test-tsx-\346\225\260\346\215\256\351\200\211\344\270\255-\346\237\261\345\233\276-\345\215\225\351\200\211-\344\270\215\345\217\257\345\217\226\346\266\210-1-snap.png" new file mode 100644 index 000000000..aeac90c8b Binary files /dev/null and "b/packages/f2/test/components/interval/__image_snapshots__/selected-test-tsx-\346\225\260\346\215\256\351\200\211\344\270\255-\346\237\261\345\233\276-\345\215\225\351\200\211-\344\270\215\345\217\257\345\217\226\346\266\210-1-snap.png" differ diff --git "a/packages/f2/test/components/interval/__image_snapshots__/selected-test-tsx-\346\225\260\346\215\256\351\200\211\344\270\255-\346\237\261\345\233\276-\345\215\225\351\200\211-\344\270\215\345\217\257\345\217\226\346\266\210-2-snap.png" "b/packages/f2/test/components/interval/__image_snapshots__/selected-test-tsx-\346\225\260\346\215\256\351\200\211\344\270\255-\346\237\261\345\233\276-\345\215\225\351\200\211-\344\270\215\345\217\257\345\217\226\346\266\210-2-snap.png" new file mode 100644 index 000000000..4e4d01021 Binary files /dev/null and "b/packages/f2/test/components/interval/__image_snapshots__/selected-test-tsx-\346\225\260\346\215\256\351\200\211\344\270\255-\346\237\261\345\233\276-\345\215\225\351\200\211-\344\270\215\345\217\257\345\217\226\346\266\210-2-snap.png" differ diff --git "a/packages/f2/test/components/interval/__image_snapshots__/selected-test-tsx-\346\225\260\346\215\256\351\200\211\344\270\255-\346\237\261\345\233\276-\345\215\225\351\200\211-\344\270\215\345\217\257\345\217\226\346\266\210-3-snap.png" "b/packages/f2/test/components/interval/__image_snapshots__/selected-test-tsx-\346\225\260\346\215\256\351\200\211\344\270\255-\346\237\261\345\233\276-\345\215\225\351\200\211-\344\270\215\345\217\257\345\217\226\346\266\210-3-snap.png" new file mode 100644 index 000000000..4e4d01021 Binary files /dev/null and "b/packages/f2/test/components/interval/__image_snapshots__/selected-test-tsx-\346\225\260\346\215\256\351\200\211\344\270\255-\346\237\261\345\233\276-\345\215\225\351\200\211-\344\270\215\345\217\257\345\217\226\346\266\210-3-snap.png" differ diff --git "a/packages/f2/test/components/interval/__image_snapshots__/selected-test-tsx-\346\225\260\346\215\256\351\200\211\344\270\255-\346\237\261\345\233\276-\345\244\232\351\200\211-1-snap.png" "b/packages/f2/test/components/interval/__image_snapshots__/selected-test-tsx-\346\225\260\346\215\256\351\200\211\344\270\255-\346\237\261\345\233\276-\345\244\232\351\200\211-1-snap.png" new file mode 100644 index 000000000..aeac90c8b Binary files /dev/null and "b/packages/f2/test/components/interval/__image_snapshots__/selected-test-tsx-\346\225\260\346\215\256\351\200\211\344\270\255-\346\237\261\345\233\276-\345\244\232\351\200\211-1-snap.png" differ diff --git "a/packages/f2/test/components/interval/__image_snapshots__/selected-test-tsx-\346\225\260\346\215\256\351\200\211\344\270\255-\346\237\261\345\233\276-\345\244\232\351\200\211-2-snap.png" "b/packages/f2/test/components/interval/__image_snapshots__/selected-test-tsx-\346\225\260\346\215\256\351\200\211\344\270\255-\346\237\261\345\233\276-\345\244\232\351\200\211-2-snap.png" new file mode 100644 index 000000000..b6784ddcb Binary files /dev/null and "b/packages/f2/test/components/interval/__image_snapshots__/selected-test-tsx-\346\225\260\346\215\256\351\200\211\344\270\255-\346\237\261\345\233\276-\345\244\232\351\200\211-2-snap.png" differ diff --git "a/packages/f2/test/components/interval/__image_snapshots__/selected-test-tsx-\346\225\260\346\215\256\351\200\211\344\270\255-\346\237\261\345\233\276-\345\244\232\351\200\211-3-snap.png" "b/packages/f2/test/components/interval/__image_snapshots__/selected-test-tsx-\346\225\260\346\215\256\351\200\211\344\270\255-\346\237\261\345\233\276-\345\244\232\351\200\211-3-snap.png" new file mode 100644 index 000000000..aeac90c8b Binary files /dev/null and "b/packages/f2/test/components/interval/__image_snapshots__/selected-test-tsx-\346\225\260\346\215\256\351\200\211\344\270\255-\346\237\261\345\233\276-\345\244\232\351\200\211-3-snap.png" differ diff --git "a/packages/f2/test/components/interval/__image_snapshots__/selected-test-tsx-\346\225\260\346\215\256\351\200\211\344\270\255-\351\245\274\345\233\276-1-snap.png" "b/packages/f2/test/components/interval/__image_snapshots__/selected-test-tsx-\346\225\260\346\215\256\351\200\211\344\270\255-\351\245\274\345\233\276-1-snap.png" new file mode 100644 index 000000000..67a2d6c44 Binary files /dev/null and "b/packages/f2/test/components/interval/__image_snapshots__/selected-test-tsx-\346\225\260\346\215\256\351\200\211\344\270\255-\351\245\274\345\233\276-1-snap.png" differ diff --git "a/packages/f2/test/components/interval/__image_snapshots__/selected-test-tsx-\346\225\260\346\215\256\351\200\211\344\270\255-\351\245\274\345\233\276-2-snap.png" "b/packages/f2/test/components/interval/__image_snapshots__/selected-test-tsx-\346\225\260\346\215\256\351\200\211\344\270\255-\351\245\274\345\233\276-2-snap.png" new file mode 100644 index 000000000..0045bc966 Binary files /dev/null and "b/packages/f2/test/components/interval/__image_snapshots__/selected-test-tsx-\346\225\260\346\215\256\351\200\211\344\270\255-\351\245\274\345\233\276-2-snap.png" differ diff --git "a/packages/f2/test/components/interval/__image_snapshots__/selected-test-tsx-\346\225\260\346\215\256\351\200\211\344\270\255-\351\245\274\345\233\276-3-snap.png" "b/packages/f2/test/components/interval/__image_snapshots__/selected-test-tsx-\346\225\260\346\215\256\351\200\211\344\270\255-\351\245\274\345\233\276-3-snap.png" new file mode 100644 index 000000000..a9d92da93 Binary files /dev/null and "b/packages/f2/test/components/interval/__image_snapshots__/selected-test-tsx-\346\225\260\346\215\256\351\200\211\344\270\255-\351\245\274\345\233\276-3-snap.png" differ diff --git a/packages/f2/test/components/interval/selected.test.tsx b/packages/f2/test/components/interval/selected.test.tsx new file mode 100644 index 000000000..d62606ce9 --- /dev/null +++ b/packages/f2/test/components/interval/selected.test.tsx @@ -0,0 +1,238 @@ +import { jsx } from '../../../src/jsx'; +import { Canvas, Chart, Interval, Axis } from '../../../src'; +import { createContext, delay, gestureSimulator } from '../../util'; + +const data = [ + { a: '1', genre: 'Sports', sold: 275 }, + { a: '1', genre: 'Strategy', sold: 115 }, + { a: '1', genre: 'Action', sold: 120 }, + { a: '1', genre: 'Shooter', sold: 350 }, + { a: '1', genre: 'Other', sold: 110 }, +]; + +describe('数据选中', () => { + it('柱图-单选', async () => { + const context = createContext(); + const { props } = ( + + + + + + + + ); + + const canvas = new Canvas(props); + canvas.render(); + await delay(200); + expect(context).toMatchImageSnapshot(); + + await gestureSimulator(context.canvas, 'click', { x: 213, y: 166 }); + await delay(200); + expect(context).toMatchImageSnapshot(); + + // 反选 + await gestureSimulator(context.canvas, 'click', { x: 213, y: 166 }); + await delay(200); + expect(context).toMatchImageSnapshot(); + }); + + it('柱图-单选, 不可取消', async () => { + const context = createContext(); + const { props } = ( + + + + + + + + ); + + const canvas = new Canvas(props); + canvas.render(); + await delay(200); + expect(context).toMatchImageSnapshot(); + + await gestureSimulator(context.canvas, 'click', { x: 213, y: 166 }); + await delay(200); + expect(context).toMatchImageSnapshot(); + + // 反选 + await gestureSimulator(context.canvas, 'click', { x: 213, y: 166 }); + await delay(200); + expect(context).toMatchImageSnapshot(); + }); + + it('柱图-style 为函数', async () => { + const context = createContext(); + const { props } = ( + + + + + { + const { xMin, xMax } = record; + const width = xMax - xMin; + const offset = width * 0.1; + return { + x: xMin - offset, + width: width + offset * 2, + }; + }, + unSelectedStyle: () => { + return { + fillOpacity: 0.4, + }; + }, + cancelable: false, + }} + /> + + + ); + + const canvas = new Canvas(props); + canvas.render(); + await delay(200); + expect(context).toMatchImageSnapshot(); + + await gestureSimulator(context.canvas, 'click', { x: 213, y: 166 }); + await delay(200); + expect(context).toMatchImageSnapshot(); + + // 反选 + await gestureSimulator(context.canvas, 'click', { x: 213, y: 166 }); + await delay(200); + expect(context).toMatchImageSnapshot(); + }); + + it('柱图-多选', async () => { + const context = createContext(); + const { props } = ( + + + + + + + + ); + + const canvas = new Canvas(props); + canvas.render(); + await delay(200); + expect(context).toMatchImageSnapshot(); + + await gestureSimulator(context.canvas, 'click', { x: 213, y: 166 }); + await delay(200); + expect(context).toMatchImageSnapshot(); + + // 反选 + await gestureSimulator(context.canvas, 'click', { x: 213, y: 166 }); + await delay(200); + expect(context).toMatchImageSnapshot(); + }); + + it('饼图', async () => { + const context = createContext(); + const { props } = ( + + + { + const { yMax, yMin } = record; + return { + r: (yMax - yMin) * 1.1, + }; + }, + unSelectedStyle: {}, + cancelable: true, + }} + /> + + + ); + + const canvas = new Canvas(props); + canvas.render(); + await delay(200); + expect(context).toMatchImageSnapshot(); + + // 选中 + await gestureSimulator(context.canvas, 'click', { x: 144, y: 68 }); + await delay(200); + expect(context).toMatchImageSnapshot(); + + // 反选 + await gestureSimulator(context.canvas, 'click', { x: 144, y: 68 }); + await delay(200); + expect(context).toMatchImageSnapshot(); + }); +});