1

我想为我的客户构建一个 Flow Builder,为他们提供一种在我的仪表板编辑器中构建数据的完整方式,灵感来自于manychat flow builder

我有一个带有 UI 编辑器的简单应用程序我需要构建一个工作流工具,允许人们将节点拖到画布上,将节点的出口连接到其他节点的输入...... Zoom.etc。

我受到了一个多聊天编辑器的启发,你可以在这里看到它https://manychat.com/

在此处输入图像描述

更多它在许多聊天中的样子 在此处输入图像描述

寻找关于如何开始的建议......好奇是否有框架/库有人会建议让这更容易,或者只是确认我应该开始使用 Javascript 来处理拖放/画线/等。

我找到了这个名为 rete.js 的库

到目前为止,这就是我所拥有的。

js

var numSocket = new Rete.Socket('Number value');

var VueNumControl = {
  props: ['readonly', 'emitter', 'ikey', 'getData', 'putData'],
  template: '<input type="number" :readonly="readonly" :value="value" @input="change($event)" @dblclick.stop="" @pointermove.stop=""/>',
  data() {
    return {
      value: 0,
    }
  },
  methods: {
    change(e){
      this.value = +e.target.value;
      this.update();
    },
    update() {
      if (this.ikey)
        this.putData(this.ikey, this.value)
      this.emitter.trigger('process');
    }
  },
  mounted() {
    this.value = this.getData(this.ikey);
  }
}

class NumControl extends Rete.Control {

  constructor(emitter, key, readonly) {
    super(key);
    this.component = VueNumControl;
    this.props = { emitter, ikey: key, readonly };
  }

  setValue(val) {
    this.vueContext.value = val;
  }
}

class NumComponent extends Rete.Component {

    constructor(){
        super("Number");
    }

    builder(node) {
        var out1 = new Rete.Output('num', "Number", numSocket);

        return node.addControl(new NumControl(this.editor, 'num')).addOutput(out1);
    }

    worker(node, inputs, outputs) {
        outputs['num'] = node.data.num;
    }
}

class AddComponent extends Rete.Component {
    constructor(){
        super("Add");
    }

    builder(node) {
        var inp1 = new Rete.Input('num1',"Number", numSocket);
        var inp2 = new Rete.Input('num2', "Number2", numSocket);
        var out = new Rete.Output('num', "Number", numSocket);

        inp1.addControl(new NumControl(this.editor, 'num1'))
        inp2.addControl(new NumControl(this.editor, 'num2'))

        return node
            .addInput(inp1)
            .addInput(inp2)
            .addControl(new NumControl(this.editor, 'preview', true))
            .addOutput(out);
    }

    worker(node, inputs, outputs) {
        var n1 = inputs['num1'].length?inputs['num1'][0]:node.data.num1;
        var n2 = inputs['num2'].length?inputs['num2'][0]:node.data.num2;
        var sum = n1 + n2;

        this.editor.nodes.find(n => n.id == node.id).controls.get('preview').setValue(sum);
        outputs['num'] = sum;
    }
}

(async () => {
    var container = document.querySelector('#rete');
    var components = [new NumComponent(), new AddComponent()];

    var editor = new Rete.NodeEditor('demo@0.1.0', container);
    editor.use(ConnectionPlugin.default);
    editor.use(VueRenderPlugin.default);    
    editor.use(ContextMenuPlugin.default);
    editor.use(AreaPlugin);
    editor.use(CommentPlugin.default);
    editor.use(HistoryPlugin);
    editor.use(ConnectionMasteryPlugin.default);

    var engine = new Rete.Engine('demo@0.1.0');

    components.map(c => {
        editor.register(c);
        engine.register(c);
    });

    var n1 = await components[0].createNode({num: 2});
    var n2 = await components[0].createNode({num: 0});
    var add = await components[1].createNode();

    n1.position = [80, 200];
    n2.position = [80, 400];
    add.position = [500, 240];


    editor.addNode(n1);
    editor.addNode(n2);
    editor.addNode(add);

    editor.connect(n1.outputs.get('num'), add.inputs.get('num1'));
    editor.connect(n2.outputs.get('num'), add.inputs.get('num2'));


    editor.on('process nodecreated noderemoved connectioncreated connectionremoved', async () => {
      console.log('process');
        await engine.abort();
        await engine.process(editor.toJSON());
    });

    editor.view.resize();
    AreaPlugin.zoomAt(editor);
    editor.trigger('process');
})();

这是HTML

<div id="rete"></div>

<a target="_blank" href="https://github.com/retejs/rete"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://camo.githubusercontent.com/652c5b9acfaddf3a9c326fa6bde407b87f7be0f4/68747470733a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f6f72616e67655f6666373630302e706e67" alt="Fork me on GitHub" data-canonical-src="https://s3.amazonaws.com/github/ribbons/forkme_right_orange_ff7600.png"></a>

这是codepen 代码笔演示

不幸的是,这并没有给出我想要的,

我需要做什么才能得到我需要的东西?或者有没有我可以尝试的图书馆?

4

1 回答 1

0

ManyChat 使用 PIXI.js 构建流程构建器。我在阅读他们的捆绑代码时发现了自己(谁读了编译代码,是的,我做到了:))

好像他们使用 React-PIXI - PIXI.js 的反应包装器(我不确定)

从那次观察中,我可以使用 VueJS 构建自己的一个(在我以前公司的一个项目中)

你应该看看 PIXI。给您的一些重要通知(我从 Manychat 的想法和实施过程中收集的所有信息):

  • 请记住将您的画布宽度和高度属性加倍,然后使用 CSS 将您的画布设置为宽度和高度的一半。这样做将使您的画布具有高质量。例子:
<canvas width="400" height="600" style="width: 200px; height: 300px;"></canvas>
  • 困难的部分之一是使箭头连接 2 个块,我阅读了 Manychat 的编译代码并发现,不记得到底是什么但基本上他们将箭头分成 2 个对称部分并开始将它们绘制到它们的中心,你可以阅读它并找出自己(如果你幸运的话,哈哈),它在main.js
  • 另一个困难的部分是将 Vue 数据反映到画布中,画布不是 DOM,任何更改都必须重新绘制整个画布,所以要小心
  • 不要渲染多个图像,这是成本
  • 对于对象捕捉,你可以从 KonvaJS 的想法中学习(他们有一个例子)
  • 对于固定、平移和缩放,您可以使用 PIXI 在 Google 示例中进行搜索。
  • 单击一个块时,相机将通过一些动画平滑地聚焦该块(放大然后缩小,用相机集中块,...),似乎许多聊天使用tween.js
  • 小心在事件监听器(mousemove、mousedrag...)中计算数据,它很容易破坏性能
于 2020-12-29T06:12:01.553 回答