Skip to content

Commit 3254d1f

Browse files
author
xuying.xu
committed
feat: 增加Pictorial组件
1 parent d715549 commit 3254d1f

File tree

6 files changed

+248
-0
lines changed

6 files changed

+248
-0
lines changed

packages/f2/src/components/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,4 @@ export {
3232
withCandlestick,
3333
CandlestickView,
3434
} from './candlestick';
35+
export { default as Pictorial, PictorialProps } from './pictorial';
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import Pictorial, { PictorialProps } from './pictorial';
2+
3+
export { PictorialProps, Pictorial };
4+
export default Pictorial;
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import { jsx } from '@antv/f-engine';
2+
import { GeometryProps } from '../geometry';
3+
import { withInterval } from '../interval';
4+
import { DataRecord } from '../../chart/Data';
5+
import { deepMix } from '@antv/util';
6+
7+
const parsePercent = (value: number | string, total: number) => {
8+
if (typeof value === 'number') {
9+
return value;
10+
} else if (typeof value === 'string') {
11+
if (value.endsWith('%')) {
12+
return (parseFloat(value) / 100) * total;
13+
} else {
14+
return 0;
15+
}
16+
}
17+
};
18+
19+
export interface PictorialProps<TRecord extends DataRecord = DataRecord>
20+
extends GeometryProps<TRecord> {
21+
symbol?: any;
22+
symbolSize?: (number | string)[];
23+
symbolOffset?: (number | string)[];
24+
}
25+
26+
export default class Pictorial extends withInterval({}) {
27+
props: PictorialProps;
28+
// 默认初始大小
29+
symbolDefaultSize: number = 400;
30+
31+
defalutBound(type) {
32+
switch (type) {
33+
case 'ellipse':
34+
return {
35+
rx: this.symbolDefaultSize / 2,
36+
ry: this.symbolDefaultSize / 2,
37+
};
38+
39+
default:
40+
return {
41+
width: this.symbolDefaultSize,
42+
height: this.symbolDefaultSize,
43+
};
44+
}
45+
}
46+
47+
preSymbolBound(symbol) {
48+
symbol.props = deepMix(
49+
{
50+
style: this.defalutBound(symbol?.type),
51+
},
52+
symbol.props
53+
);
54+
55+
return {
56+
width: this.symbolDefaultSize,
57+
height: this.symbolDefaultSize,
58+
};
59+
}
60+
61+
parsePercentArray(array, [w, h]) {
62+
const x = parsePercent(array[0], w);
63+
const y = parsePercent(array[1], h);
64+
return [x, y];
65+
}
66+
67+
render() {
68+
const { props, context } = this;
69+
const { px2hd } = context;
70+
const { symbol, symbolSize = ['100%', '100%'], symbolOffset = [0, 0] } = px2hd(props);
71+
const records = this.mapping();
72+
73+
const { width: symbolW, height: symbolH } = this.preSymbolBound(symbol);
74+
75+
return (
76+
<group>
77+
{records.map((record) => {
78+
const { key, children } = record;
79+
return (
80+
<group key={key}>
81+
{children.map((item) => {
82+
const [width, height] = this.parsePercentArray(symbolSize, [
83+
item.xMax - item.xMin,
84+
item.yMax - item.yMin,
85+
]);
86+
const [offsetX, offsetY] = this.parsePercentArray(symbolOffset, [width, height]);
87+
const position = [item.xMin + offsetX, item.yMin + offsetY];
88+
89+
const symbolScale = [width / symbolW, height / symbolH];
90+
const transform = `translate(${position[0]},${position[1]}) scale(${symbolScale[0]},${symbolScale[1]})`;
91+
92+
return deepMix(
93+
{
94+
props: {
95+
style: {
96+
transform,
97+
},
98+
},
99+
},
100+
symbol
101+
);
102+
})}
103+
</group>
104+
);
105+
})}
106+
</group>
107+
);
108+
}
109+
}
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
import { jsx } from '../../../src';
2+
import { Canvas, Chart, Pictorial, Axis, Tooltip } from '../../../src';
3+
import { createContext, delay, gestureSimulator } from '../../util';
4+
const context = createContext();
5+
6+
const symbol = (
7+
<image
8+
style={{
9+
src:
10+
'https://gw.alipayobjects.com/zos/finxbff/compress-tinypng/mNyB6MXFwnxLMwzfEWHYt/juxingbeifen_6.png',
11+
}}
12+
/>
13+
);
14+
15+
const symbolTop = (
16+
<image
17+
style={{
18+
src:
19+
'https://gw.alipayobjects.com/zos/finxbff/compress-tinypng/VV9WVNGexcXLVYpQxjBFH/tuoyuanxingbeifen_13.png',
20+
}}
21+
/>
22+
);
23+
24+
const symbolBottom = (
25+
<image
26+
style={{
27+
src:
28+
'https://gw.alipayobjects.com/zos/finxbff/compress-tinypng/76LdyFixxEmUqrGG6rmCG/tuoyuanxingbeifen_32.png',
29+
}}
30+
/>
31+
);
32+
const data = [
33+
{
34+
x: '产品1',
35+
value: 4927,
36+
bValue: 0,
37+
},
38+
{
39+
x: '产品2',
40+
value: 11607,
41+
bValue: 0,
42+
},
43+
];
44+
45+
describe('pictorial', () => {
46+
it('image symbol', async () => {
47+
const { props } = (
48+
<Canvas context={context} pixelRatio={1}>
49+
<Chart data={data}>
50+
<Axis field="value" min={0}></Axis>
51+
52+
<Pictorial
53+
x="x"
54+
y="bValue"
55+
symbol={symbolBottom}
56+
symbolOffset={[0, '-50%']}
57+
symbolSize={['100%', '20px']}
58+
/>
59+
60+
<Pictorial x="x" y="value" symbol={symbol} />
61+
<Pictorial
62+
x="x"
63+
y="value"
64+
symbol={symbolTop}
65+
symbolSize={['100%', '20px']}
66+
symbolOffset={[0, '-50%']}
67+
/>
68+
</Chart>
69+
</Canvas>
70+
);
71+
72+
const canvas = new Canvas(props);
73+
await canvas.render();
74+
75+
await delay(1000);
76+
expect(context).toMatchImageSnapshot();
77+
});
78+
79+
it('ellipse symbol', async () => {
80+
const { props } = (
81+
<Canvas context={context} pixelRatio={1}>
82+
<Chart data={data}>
83+
<Axis field="value" min={0}></Axis>
84+
85+
<Pictorial
86+
x="x"
87+
y="bValue"
88+
symbol={
89+
<ellipse
90+
style={{
91+
fill: 'l(90) 0:#1f7eff 1:#64adff',
92+
}}
93+
/>
94+
}
95+
symbolOffset={['50%', 0]}
96+
symbolSize={['100%', '40px']}
97+
/>
98+
99+
<Pictorial
100+
x="x"
101+
y="value"
102+
symbol={
103+
<rect
104+
style={{
105+
fill: 'l(90) 0:#9cc1ff 1:#ecf5ff',
106+
fillOpacity: 0.9,
107+
}}
108+
/>
109+
}
110+
/>
111+
<Pictorial
112+
x="x"
113+
y="value"
114+
symbol={
115+
<ellipse
116+
style={{
117+
fill: 'l(90) 0:#1f7eff 1:#64adff',
118+
}}
119+
/>
120+
}
121+
symbolSize={['100%', '40px']}
122+
symbolOffset={['50%', 0]}
123+
/>
124+
</Chart>
125+
</Canvas>
126+
);
127+
128+
const canvas = new Canvas(props);
129+
await canvas.render();
130+
131+
await delay(1000);
132+
expect(context).toMatchImageSnapshot();
133+
});
134+
});

0 commit comments

Comments
 (0)