簡體   English   中英

Flow Builder(可視化編輯器)

[英]Flow Builder (visual editor)

我想為我的客戶構建一個 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代碼筆演示

不幸的是,這並沒有給我想要的,

我需要做什么才能得到我需要的東西? 或者有沒有我可以嘗試的圖書館?

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 ),它在main.js
  • 另一個難點是將 Vue 數據反映到畫布中,畫布不是 DOM,任何更改都必須重新繪制整個畫布,因此請謹慎操作
  • 不要渲染多個圖像,這是成本
  • 對於對象捕捉,您可以從 KonvaJS 的想法中學習(他們有一個例子)
  • 對於固定、平移和縮放,您可以使用 PIXI 在 Google 示例上進行搜索。
  • 單擊一個塊時,相機會通過一些動畫平滑地聚焦該塊(放大然后縮小,用相機將塊集中,...),似乎許多聊天都使用tween.js
  • 注意事件監聽器(mousemove、mousedrag...)中的計算數據,它很容易破壞性能

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM