简体   繁体   English

Flow Builder(可视化编辑器)

[英]Flow Builder (visual editor)

I want to build a Flow Builder for my clients to give them an entirely way to construct data inside my dashboard editor, inspired with manychat flow builder我想为我的客户构建一个 Flow Builder,为他们提供一种在我的仪表板编辑器中构建数据的完整方式,灵感来自于manychat flow builder

I have a simple app which has UI editor I have a need to build a workflow tool, allowing people to drag nodes onto a canvas, connect outlets of nodes to inputs of other nodes... Zoom.etc.我有一个简单的应用程序,它有 UI 编辑器我需要构建一个工作流工具,允许人们将节点拖到画布上,将节点的出口连接到其他节点的输入...... Zoom.etc。

I am inspired with a manychat editor which u can see it here https://manychat.com/我受到了一个多聊天编辑器的启发,你可以在这里看到它https://manychat.com/

在此处输入图片说明

More how it look in manychat更多它在多聊天中的样子在此处输入图片说明

Looking for suggestions on how to get started... Curious if there are frameworks/libraries anyone would recommend making this easier or just confirmation that I should just start whipping out Javascript to handle the drags/drops/line drawing/etc.寻找关于如何开始的建议......好奇是否有框架/库,有人会建议让这更容易,或者只是确认我应该开始使用 Javascript 来处理拖放/画线/等。

I have found this library named rete.js我找到了这个名为 rete.js 的库

So far this is what I have.到目前为止,这就是我所拥有的。

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');
})();

Here is HTML这是 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>

Here is codepen code pen demo这是codepen代码笔演示

Unfortunately, this does not give what I want,不幸的是,这并没有给我想要的,

What do I need to do to get what I need?我需要做什么才能得到我需要的东西? or is there any library which I can try?或者有没有我可以尝试的图书馆?

ManyChat uses PIXI.js to build the flow builder. ManyChat 使用 PIXI.js 来构建流构建器。 I found out myself when reading through their bundle codes (who the hell read compiled codes, yes I did :))我在阅读他们的捆绑代码时发现了自己(谁他妈的阅读了编译代码,是的,我做到了:))

Seems like they use React-PIXI - a react wrapper of PIXI.js (I'm not sure about this)似乎他们使用 React-PIXI - PIXI.js 的反应包装器(我不确定这一点)

From that observation I can build my own one using VueJS (in a project of my previous company)根据这一观察,我可以使用 VueJS 构建自己的一个(在我以前公司的一个项目中)

You should take a look at PIXI.你应该看看 PIXI。 Some important notices for you (all I collect from Manychat's idea and during my implementation):给您的一些重要通知(我从Manychat 的想法和实施过程中收集的所有信息):

  • Remember to double your canvas width and height property, then use CSS to style your canvas with half size of the width and height.请记住将画布宽度和高度属性加倍,然后使用 CSS 将画布设置为宽度和高度的一半大小。 Doing this will make your canvas has high quality.这样做将使您的画布具有高质量。 Example:例子:
<canvas width="400" height="600" style="width: 200px; height: 300px;"></canvas>
  • One of the hard part is making the arrow to connect between 2 blocks, I read compiled code of Manychat and found out, not remember exactly what it is but basically they split the arrow to 2 symmetric parts and start drawing them to their center, you can read it and find out yourself (if you lucky, LOL), it's in main.js困难的部分之一是让箭头连接 2 个块,我阅读了 Manychat 的编译代码并发现,不记得它是什么,但基本上他们将箭头分成 2 个对称部分并开始将它们绘制到它们的中心,你可以阅读它并找出自己(如果你幸运的话, main.js ),它在main.js
  • Another hard part is reflecting Vue data into canvas, canvas is not DOM, any change you'll have to re-draw the whole canvas, so do this carefully另一个难点是将 Vue 数据反映到画布中,画布不是 DOM,任何更改都必须重新绘制整个画布,因此请谨慎操作
  • don't render multiple images, it's cost不要渲染多个图像,这是成本
  • for Object snapping, you can learn from idea of KonvaJS (they have an example for that)对于对象捕捉,您可以从 KonvaJS 的想法中学习(他们有一个例子)
  • For pin, pan and Zoom you can search on Google examples using PIXI.对于固定、平移和缩放,您可以使用 PIXI 在 Google 示例上进行搜索。
  • When clicking an block, the camera will smoothly focus that block with some animations (zoom in then out, centralize the block with the camera,...), seem Many chat uses tween.js单击一个块时,相机会通过一些动画平滑地聚焦该块(放大然后缩小,用相机将块集中,...),似乎许多聊天都使用tween.js
  • Beware of computing data in event listeners (mousemove, mousedrag,...), it can easily destroy performance注意事件监听器(mousemove、mousedrag...)中的计算数据,它很容易破坏性能

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM