UNPKG

@antv/f2

Version:

Charts for mobile visualization.

319 lines (280 loc) 9.04 kB
import { isArray, isBoolean, isUndefined, pick } from '@antv/util'; import Children from '../children'; import { compareRenderTree, renderJSXElement } from '../jsx'; import { JSX } from '../jsx/jsx-namespace'; import { render } from '../jsx/render'; import Component from './component'; import equal from './equal'; function pickElement(element) { if (!element) return element; return Children.map(element, (item) => { if (!item) return item; // 只需要这几个元素就可以了 return pick(item, ['key', 'ref', 'type', 'props']); }); } function renderShape(component: Component, children: JSX.Element, animate?: boolean) { const { container, context, updater, // @ts-ignore __lastElement, // @ts-ignore transformFrom, animate: componentAnimate, } = component; // 先清空绘制内容 container.clear(); animate = isBoolean(animate) ? animate : componentAnimate; const { px2hd } = context; const lastElement = __lastElement || (transformFrom && transformFrom.__lastElement); // children 是 shape 的 jsx 结构, component.render() 返回的结构 const shapeElement = renderJSXElement(children, context, updater); // @ts-ignore component.__lastElement = shapeElement; const renderElement = animate !== false ? compareRenderTree(shapeElement, lastElement) : shapeElement; if (!renderElement) return null; // 生成G的节点树, 存在数组的情况是根节点有变化,之前的树删除,新的树创建 if (isArray(renderElement)) { return renderElement.map((element) => { return render(element, container, animate, px2hd); }); } else { return render(renderElement, container, animate, px2hd); } } function setComponentAnimate(child: Component, parent: Component) { const { animate: parentAnimate } = parent; // 如果父组件不需要动画,子组件全不不执行动画 if (parentAnimate === false) { child.animate = false; return; } const { props: childProps } = child; const { animate: childAnimate } = childProps; child.animate = isBoolean(childAnimate) ? childAnimate : parentAnimate; } function getTransformComponent(component: Component) { if (!component) return null; // @ts-ignore const { __lastElement, children } = component; if (__lastElement) { return component; } if (!children) { return null; } let componentFromChildren = null; Children.map(children, (item) => { if (componentFromChildren) return; if (!item) return; const component = getTransformComponent(item.component); if (component) { componentFromChildren = component; } }); return componentFromChildren; } function getTransformFromComponentRef(transformFromRef) { if (!transformFromRef || !transformFromRef.current) { return null; } const transformFromComponent = transformFromRef.current; return getTransformComponent(transformFromComponent); } function createComponent(parent: Component, element: JSX.Element): Component { const { type, props, ref } = element; const { container, context, updater, //@ts-ignore transformFrom, } = parent; const { transformFrom: transformFromRef, ...receiveProps } = props; let component: Component; // @ts-ignore if (type.prototype && type.prototype.isF2Component) { // @ts-ignore component = new type(receiveProps, context, updater); } else { component = new Component(receiveProps, context, updater); component.render = function () { // @ts-ignore return type(this.props, context, updater); }; } // 设置ref if (ref) { ref.current = component; } // 因为view 可能在子组件,所以这里要透传到子组件 if (transformFrom) { // @ts-ignore component.transformFrom = transformFrom; } if (transformFromRef) { const transformFromComponent = transformFromRef ? getTransformFromComponentRef(transformFromRef) : null; // @ts-ignore component.transformFrom = transformFromComponent; } const { zIndex } = props; // 每个组件都新建一个独立容器 component.container = container.addGroup({ zIndex }); component.context = context; component.updater = updater; return component; } function renderComponent(component: Component | Component[]) { Children.map(component, (item: Component) => { const { children: lastChildren } = item; const mount = isUndefined(lastChildren); if (mount) { if (item.willMount) item.willMount(); } else if (item.willUpdate) { item.willUpdate(); } }); Children.map(component, (item: Component) => { const { children: lastChildren } = item; const mount = isUndefined(lastChildren); const newChildren = item.render(); renderChildren(item, newChildren, lastChildren); if (mount) { if (item.didMount) item.didMount(); } else if (item.didUpdate) { item.didUpdate(); } }); } function destroyElement(elements: JSX.Element) { Children.map(elements, (element) => { if (!element) return; const { component } = element; if (!component) { return; } if (component.willUnmount) { component.willUnmount(); } destroyElement(component.children); const { container } = component; container.remove(true); if (component.didUnmount) { component.didUnmount(); } component.destroy(); }); } function diffElement(parent: Component, nextElement: JSX.Element, lastElement: JSX.Element) { if (!nextElement && !lastElement) { return null; } // 删除 if (!nextElement && lastElement) { destroyElement(lastElement); return null; } // 新建 if (nextElement && !lastElement) { return nextElement; } // diff const { type: nextType, props: nextProps } = nextElement; const { type: lastType, props: lastProps, component: lastComponent } = lastElement; if (nextType !== lastType) { destroyElement(lastElement); return nextElement; } // 保留component, 等下一阶段处理 nextElement.component = lastComponent; if (equal(nextProps, lastProps) && lastComponent.context === parent.context) { return null; } return nextElement; } function diff(parent: Component, nextChildren, lastChildren) { // destroy // 生命周期的几个阶段 // should create / update // create / Receive props // willMount / willUpdate // render // didMount / didUpdate let childrenArray = []; // 1. 第一轮比较, 直接destroy的元素处理掉,destroy 的元素不需要进入下一阶段 Children.compare(nextChildren, lastChildren, (next, last) => { const element = diffElement(parent, next, last); if (element) { childrenArray = childrenArray.concat(Children.toArray(element).filter(Boolean)); } }); // 2. 处理 shouldCreate 和 shouldUpdate const shouldProcessChildren = childrenArray.filter((element: JSX.Element) => { const { component, props } = element; // 说明是新增的元素,需要新建 if (!component) return true; // 不需要更新 if (component.shouldUpdate && component.shouldUpdate(props) === false) { return false; } return true; }); // 3. 处理 create 和 Receive props const shouldRenderComponent = shouldProcessChildren.map((element: JSX.Element) => { let { component } = element; if (!component) { component = createComponent(parent, element); } else { const { props } = element; if (component.willReceiveProps) { component.willReceiveProps(props, parent.context); } const { zIndex } = props; component.container.set('zIndex', zIndex); component.props = props; component.context = parent.context; } element.component = component; setComponentAnimate(component, parent); return component; }); // 4. 处理 render renderComponent(shouldRenderComponent); // 按子组件顺序渲染内容 childrenArray.forEach((element: JSX.Element) => { const { component } = element; const { container: parentGroup } = parent; parentGroup.add(component.container); }); return nextChildren; } function isContainer(children: JSX.Element) { if (!children) return false; if (!isArray(children)) { const { type } = children; return typeof type === 'function'; } for (let i = 0, len = children.length; i < len; i++) { if (isContainer(children[i])) { return true; } } return false; } function renderChildren(parent: Component, nextChildren, lastChildren) { // react 生成的 element 是 not extensible 的,这里新建一个新对象,并把需要的内容pick 出来 nextChildren = pickElement(nextChildren); parent.children = nextChildren; if (isContainer(nextChildren)) { nextChildren = diff(parent, nextChildren, lastChildren); } else { renderShape(parent, nextChildren); } return nextChildren; } export { renderChildren, diff, renderComponent, renderShape, destroyElement };