画布引擎

Playground

画布引擎底层会提供一套自己的坐标系, 主要由 Playground 驱动

interface Playground {
   node: HTMLDivElement // 画布挂载的dom节点
   toReactComponent() // 渲染为react 节点
   readonly: boolean // 只读模式
   config: PlaygroundConfigEntity // 包含 zoom,scroll 等画布数据
}
// hook 快速获取
const { playground } = useClientContext()

Layer

P.S.
  • 渲染层在底层建立了一套自己的坐标系,基于这个坐标系实现模拟滚动、缩放等逻辑,在算viewport时候节点也需要转换到该坐标系上
  • 渲染按画布被拆分成多个层 (Layer),分层设计是基于ECS的数据切割思想,不同 Layer 只监听自己想要的数据,独立渲染不干扰,Layer 可以理解为ECS的 System,即最终Entity数据消费的地方
  • Layer 实现了类mobx的observer响应式动态依赖收集,数据更新会触发 autorun或render

切面编程

  • Layer 生命周期
interface Layer {
    /**
     * 初始化时候触发
     */
    onReady?(): void;

    /**
     * playground 大小变化时候会触发
     */
    onResize?(size: PipelineDimension): void;

    /**
     * playground focus 时候触发
     */
    onFocus?(): void;

    /**
     * playground blur 时候触发
     */
    onBlur?(): void;

    /**
     * 监听缩放
     */
    onZoom?(scale: number): void;

    /**
     * 监听滚动
     */
    onScroll?(scroll: { scrollX: number; scrollY: number }): void;

    /**
     * viewport 更新触发
     */
    onViewportChange?(): void;

    /**
     * readonly 或 disable 状态变化
     * @param state
     */
    onReadonlyOrDisabledChange?(state: { disabled: boolean; readonly: boolean }): void;

    /**
   * 数据更新自动触发react render,如果不提供则不会调用react渲染
   */
    render?(): JSX.Element
 }

Layer的定位其实和 Unity 游戏引擎 提供的 MonoBehaviour 类似, Unity 游戏引擎的脚本扩展都是基于这个,可以认为是最核心的设计,底层也是基于 C# 提供的反射 (Reflection) 能力的依赖注入

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MyMonoBehavior : MonoBehaviour
{
    void Awake()
    {
        Debug.Log("Awake method is always called before application starts.");
    }
    void Start()
    {
        Debug.Log("Start method is always called after Awake().");
    }
    void Update()
    {
        Debug.Log("Update method is called in every frame.");
    }
}
  • Layer 的响应式更新
export class DemoLayer extends Layer {
    // 任意的inversify模块 的注入
    @inject(FlowDocument) document: FlowDocument
    // 监听单个Entity
    @observeEntity(SomeEntity) entity: SomeEntity
    // 监听多个Entity
    @observeEntities(SomeEntity) entities: SomeEntity[]
    // 监听 Entity的数据块(ECS - Component)变化
    @observeEntityDatas(SomeEntity, SomeEntityData) transforms: SomeEntityData[]
    autorun() {}
    render() {
      return <div></div>
    }
}

FlowNodeEntity

  • 节点是一颗树, 包含子节点 (blocks) 和父亲节点, 节点采用 ECS 架构
inteface FlowNodeEntity {
    id: string
    blocks: FlowNodeEntity[]
    pre?: FlowNodeEntity
    next?: FlowNodeEntity
    parent?: FlowNodeEntity
    collapsed: boolean // 是否展开
    getData(dataRegistry): NodeEntityData
    addData(dataRegistry)
}

FlowNodeTransformData 节点的位置及大小数据

class FlowNodeTransformData {
    localTransform: Matrix, // 相对偏移, 只相对于同一个Block的上一个Sibling节点的偏移
    worldTransform: Matrix, // 绝对偏移, 相对于Parent和Sibling节点叠加后的偏移
    delta:Point // 居中居左偏移, 和Matrix独立,每个节点自己控制
    getSize(): Size, // 由自己(独立节点) 或者 子分支节点宽高间距计算得出
    getBounds(): Rectangle // 由worldMatix及 size 计算得出, 用于最终渲染,该范围也可用于确定高亮选中区域
    inputPoint(): Point // 输入点位置,一般是Block的第一个节点的中上位置(居中布局)
    outputPoint(): Point // 输出点位置,默认是节点中下位置,但条件分支,是由内置结束节点等具体逻辑判断得出
   // ...others
}

FlowNodeRenderData 节点内容渲染数据

class FlowNodeRenderData {
  node: HTMLDivElement // 当前节点的dom
  expanded:boolean // 是否展开
  activated: boolean // 是否激活
  hidden: boolean // 是否隐藏
  // ...others
}

FlowDocument

interface FLowDocument {
    root: FlowNodeEntity // 画布的根节点
    fromJSON(data): void // 导入数据
    toJSON(): FlowDocumentJSON // 导出数据
    addNode(type: string, meta: any): FlowNodeEntity // 添加节点
    travese(fn: (node: flowNodeEntity) => void, startNode = this.root) // 遍历
}