Flow Builder(可视化编辑器)

时间:2019-06-12 11:45:52

标签: javascript jquery html jquery-ui visualdesigner

我想为我的客户构建一个Flow Builder,以给他们一种在manychat flow builder启发下在我的仪表板编辑器中构造数据的完整方法

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

我受到多聊编辑器的启发,您可以在这里https://manychat.com/

看到它

enter image description here

更多在多聊中的外观 enter image description here

正在寻找有关入门的建议...好奇是否有框架/库,有人会建议使其更简单,或者只是确认我应该开始使用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 code pen demo

不幸的是,这不能满足我的要求,

我需要做什么才能获得所需的东西?还是有什么我可以尝试的图书馆?

1 个答案:

答案 0 :(得分: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 等)中的计算数据,它很容易破坏性能