Skip to content

Commit 48efa51

Browse files
committed
feat: 增加组件包围盒计算
1 parent a2505be commit 48efa51

4 files changed

Lines changed: 203 additions & 3 deletions

File tree

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { JSX } from '../../jsx/jsx-namespace';
2+
import Component from '../../component';
3+
import { renderJSXElement, computeCSSLayout } from './computeLayout';
4+
import { createShape } from './createShape';
5+
import { VNode } from '../vnode';
6+
import { Group } from '@antv/g-lite';
7+
import Children from '../../children';
8+
9+
function computeComponent(component: Component | VNode, newChildren?: JSX.Element) {
10+
const { context, updater } = component;
11+
const { canvas } = context;
12+
const nodeTree = renderJSXElement(newChildren, context, updater);
13+
14+
computeCSSLayout(nodeTree);
15+
16+
const rootShape = new Group();
17+
traverseNodeTreeAndCreateShapes(nodeTree, rootShape);
18+
19+
canvas.getRoot().appendChild(rootShape);
20+
const bbox = rootShape.getBBox();
21+
rootShape.remove();
22+
return bbox;
23+
}
24+
25+
function traverseNodeTreeAndCreateShapes(node, parentShape) {
26+
if (!node) return;
27+
28+
const { type, style, children } = node;
29+
30+
// Create the shape for this node
31+
const shape = createShape(type, { style });
32+
33+
// Add to parent
34+
if (parentShape) {
35+
parentShape.appendChild(shape);
36+
}
37+
38+
Children.map(children, (child) => traverseNodeTreeAndCreateShapes(child, shape));
39+
40+
return shape;
41+
}
42+
43+
export { computeComponent };

packages/f-engine/src/canvas/render/computeLayout.ts

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import Children from '../../children';
44
import { isNumber, isArray } from '@antv/util';
55
import getShapeAttrs from '../shape';
66
import { VNode } from '../vnode';
7-
import { Shape, FunctionComponent, getWorkTag } from '../workTags';
7+
import { Shape, FunctionComponent, getWorkTag, ClassComponent } from '../workTags';
88
import computeCSSLayout from './css-layout';
99

1010
export interface INode {
@@ -151,6 +151,20 @@ function renderJSXElement(element: JSX.Element, context, updater) {
151151
return renderJSXElement(newElement, context, updater);
152152
}
153153

154+
if (tag === ClassComponent) {
155+
// 创建组件实例
156+
const instance = new (type as any)(element.props, context, updater);
157+
158+
// 初始化组件
159+
if (instance.componentWillMount) {
160+
instance.componentWillMount();
161+
}
162+
163+
const newElement = instance.render();
164+
165+
return renderJSXElement(newElement, context, updater);
166+
}
167+
154168
const { className, style: customStyle = {}, attrs, children: newChildren } = props;
155169

156170
const style = px2hd({
@@ -270,4 +284,11 @@ function fillComponentLayout(vNode: VNode) {
270284
});
271285
}
272286

273-
export { computeLayout, createNodeTree, computeCSSLayout, fillElementLayout, fillComponentLayout };
287+
export {
288+
computeLayout,
289+
renderJSXElement,
290+
createNodeTree,
291+
computeCSSLayout,
292+
fillElementLayout,
293+
fillComponentLayout,
294+
};
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import { jsx, Canvas, Component, computeLayout } from '../../../src';
2+
import { computeComponent } from '../../../src/canvas/render/computeComponent';
3+
import { createContext, delay } from '../../util';
4+
5+
class View extends Component {
6+
willMount(): void {}
7+
render() {
8+
const { position } = this.props;
9+
return (
10+
<group
11+
style={{
12+
fill: 'red',
13+
}}
14+
>
15+
<circle
16+
style={{
17+
cx: position[0],
18+
cy: position[1],
19+
r: 20,
20+
fill: 'red',
21+
}}
22+
/>
23+
</group>
24+
);
25+
}
26+
}
27+
28+
class ViewText extends Component {
29+
willMount(): void {}
30+
render() {
31+
const { position } = this.props;
32+
return (
33+
<group
34+
style={{
35+
fill: 'red',
36+
}}
37+
>
38+
<text
39+
style={{
40+
x: position[0],
41+
y: position[1],
42+
text: 'hello',
43+
}}
44+
/>
45+
</group>
46+
);
47+
}
48+
}
49+
50+
class GuideGroup extends Component {
51+
bbox = [];
52+
53+
willMount(): void {
54+
const { children } = this.props;
55+
const { updater, context } = this;
56+
const childArray = Array.isArray(children) ? children : [children];
57+
childArray.map((child) => {
58+
const component = new child.type(child.props, context, updater);
59+
component.context = context;
60+
61+
const bbox = computeComponent(this, component.render());
62+
this.bbox.push(bbox);
63+
});
64+
}
65+
render() {
66+
const { children } = this.props;
67+
return <group>{children}</group>;
68+
}
69+
}
70+
71+
describe('computeComponent', () => {
72+
it('组件bbox计算', async () => {
73+
const context = createContext();
74+
const ref = { current: null };
75+
const guideGroupRef = { current: null };
76+
const { props } = (
77+
<Canvas context={context}>
78+
<GuideGroup ref={guideGroupRef}>
79+
<ViewText position={[40, 40]} ref={ref} />
80+
<View position={[70, 70]} />
81+
</GuideGroup>
82+
</Canvas>
83+
);
84+
85+
const canvas = new Canvas(props);
86+
await canvas.render();
87+
88+
await delay(100);
89+
90+
const bbox = guideGroupRef.current.bbox;
91+
92+
expect(bbox[0].x).toEqual(40);
93+
expect(bbox[0].y).toEqual(28);
94+
expect(bbox[0].width).toBeGreaterThan(0);
95+
expect(bbox[0].height).toBeGreaterThan(0);
96+
97+
expect(bbox[1].x).toEqual(50);
98+
expect(bbox[1].y).toEqual(50);
99+
expect(bbox[1].width).toEqual(40);
100+
expect(bbox[1].height).toEqual(40);
101+
});
102+
});

packages/f-engine/test/g.test.tsx

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Canvas, CanvasEvent, Circle, convertToPath, Path, Rect } from '@antv/g-lite';
1+
import { Canvas, CanvasEvent, Circle, Text, convertToPath, Path, Rect, Group } from '@antv/g-lite';
22
import { Renderer as CanvasRenderer } from '@antv/g-mobile-canvas';
33
import '@antv/g-web-animations-api';
44
import { createContext, delay } from './util';
@@ -70,4 +70,38 @@ describe('G 的测试使用', () => {
7070

7171
await delay(1000);
7272
});
73+
74+
it('getBoxx', async () => {
75+
const circle = new Circle({
76+
style: {
77+
cx: 50,
78+
cy: 50,
79+
r: 20,
80+
fill: 'red',
81+
},
82+
});
83+
const circleBBox = circle.getBBox();
84+
85+
const group = new Group();
86+
const text = new Text({
87+
style: {
88+
x: 50,
89+
y: 50,
90+
text: 'test',
91+
},
92+
});
93+
canvas.appendChild(group);
94+
group.appendChild(text);
95+
const textBBox = text.getBBox();
96+
97+
expect(circleBBox).toBeDefined();
98+
expect(circleBBox.x).toBeCloseTo(30, 1);
99+
expect(circleBBox.y).toBeCloseTo(30, 1);
100+
expect(circleBBox.width).toBeCloseTo(40, 1);
101+
expect(circleBBox.height).toBeCloseTo(40, 1);
102+
103+
expect(textBBox).toBeDefined();
104+
expect(textBBox.x).toBeCloseTo(50, 1);
105+
expect(textBBox.y).toBeCloseTo(35, 1);
106+
});
73107
});

0 commit comments

Comments
 (0)