jerosoler / Drawflow

Simple flow library 🖥️🖱️
https://jerosoler.github.io/Drawflow/
MIT License
4.42k stars 714 forks source link

Ability to have "fixed" items but still translate canvas? #799

Open chirstius opened 6 months ago

chirstius commented 6 months ago

@jerosoler first off - THANK YOU for sharing such an amazing library!

I am trying to achieve a layout (badly) represented by this google slide: drawflow-fixed-nodes

Basically - input/output "parent" containers fixed to the edges with nodes inside them and then the ability to drag/translate the "interior" nodes between them. I've done a little playing around with capturing drag events and "un-translating" the input/output containers to keep them "fixed" which sorta works, but it's a sloppy workaround and feels hacky. I haven't found a simple CSS way to do this, and I'm not sure I can have nodes outside the main "editor" element in the DOM (at least I don't know how without breaking things) which I think would be ideal.

Do you have any thoughts on how to implement such a solution?

Thank you!

jerosoler commented 6 months ago

Hello,

It is not possible to have nodes outside the editor.

I've played around with this idea a bit. With the new :has css element we can do something.

I have limited the movement of the background.

Here the html code:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Document</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/jerosoler/Drawflow/dist/drawflow.min.css" />
  <script src="https://cdn.jsdelivr.net/gh/jerosoler/Drawflow/dist/drawflow.min.js"></script>
  <style>
    #drawflow {
      position: relative;
      width: 100%;
      height: 800px;
      border: 1px solid red;
    }

    #drawflow .parent-node:has(.drawflow-node.right) {
        position: fixed;
        left: 0px;
        top: 0px;
        width: 100%;
        height: 100%;
        display: block;
        pointer-events: none;
        z-index: 2;
    }

    #drawflow .drawflow-node.right {
        right: 0px !important;
        top: 0px !important;
        left: auto !important;
        height: 100%;
        pointer-events: all;

    }

    #drawflow .parent-node:has(.drawflow-node.left) {
        position: fixed;
        left: 0px;
        top: 0px;
        width: 100%;
        height: 100%;
        display: block;
        pointer-events: none;
        z-index: 2;
    }

    #drawflow .drawflow-node.left {
        left: 0px !important;
        top: 0px !important;
        height: 100%;
        pointer-events: all;

    }

    .drawflow, .drawflow .parent-node  {
        z-index: 1;
    }

  </style>
</head>
<body>
  <div>
    <div id="drawflow"></div>
  </div>
  <script>
    var id = document.getElementById("drawflow");
    const editor = new Drawflow(id);
    editor.start();

    editor.on("clickEnd", (e) => {
        editor.editor_selected = false;
    })

    editor.addNode("left", 0, 8, 150, 300, "left", {}, '');
    editor.addNode("right", 8, 0, 150, 300, "right", {}, '');

    editor.addNode("item", 1, 1, 150, 200, "item", {}, '');
    editor.addNode("item", 1, 1, 300, 300, "item", {}, '');
    editor.addNode("item", 1, 1, 700, 400, "item", {}, '');
    editor.addNode("item", 1, 1, 900, 700, "item", {}, '');
    editor.addNode("item", 1, 1, 500, 550, "item", {}, '');
  </script>
</body>
</html>

image

Try and tell me.

jerosoler commented 6 months ago

View for link items to a simulate nodes in panels:

chirstius commented 6 months ago

Thanks for the quick response. This does get close but it's the same issue I had before, and maybe poorly represented above so here's a new image: drawflow-fixed-nodes2

While this does allow individual nodes to be dragged "between" the outer nodes. I'd like to be able to move the "canvas" under everything but keep those outer nodes fixed in place. Maybe I've altered something else in my CSS if that is working for you, but maybe this gif better illustrates what I'm seeing. So at least initially this looks perfect - visually. But the two outer nodes get dragged along with the div.drawflow as it is translated, and ideally I'd like them to "stay" fixed always where they start.

Another sample of playing around "actively" moving a node (payload) back to (0,0) - translated as needed "opposite" to div.drawflow - to show more of what I am looking for. In this case I'm just hooking the drag end, and making a single move back to (0,0) but of course it works with the active drag event(s) as well, I just feel like that's so heavy-handed I'd prefer not to do it that way.

drawflow-fixed-nodes3

Am I missing/overriding something in my current CSS to not enable the "fixed" behavior (meaning are YOU seeing those outer nodes stay fixed at all times)? Or is there something else required to get the "sticky" behavior in the last gif within the editor?

Thanks again!

jerosoler commented 6 months ago

You could listen to the "translate" event and get the "x" and "y" values. And do a translate of the left and right nodes.

chirstius commented 6 months ago

I'm using Vue - with nodes as their own components, and the editor in a "main" mapping canvas Component. I'm somewhat new to Vue - is there an easy way to access the editor instance in the "parent" to hook the translate event from child components? Should I add it as a property to be passed in to Nodes that need to remain "fixed"? or is there a more generic/simpler way I am missing? Apologies, I know this isn't a drawflow specific question

chirstius commented 6 months ago

Ok, this is where things are at - ignoring formatting within the INPUTS panel. I'm just passing the editor through via props and "undoing" any translation of the overall canvas to hold it in place. It's not ideal (I'd love to have it out of the translation flow entirely), but it does work.

I am also calling editor.updateConnectionNodes() to keep the connections all sorted. I do wish I could get rid of the jittering, but for now I think I can move forward with other things. Thank you for the help!

drawflow-fixed-nodes4

jerosoler commented 6 months ago

Hi!

View new example with custom variables for positions.

https://github.com/jerosoler/Drawflow/assets/30957047/888f6a0d-8d28-4624-ba61-2ea8a5747d58

Not flickering.

And Zoom panels fixed on left and right.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Document</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/jerosoler/Drawflow/dist/drawflow.min.css" />
  <script src="https://cdn.jsdelivr.net/gh/jerosoler/Drawflow/dist/drawflow.min.js"></script>
  <style>
    :root {
      --x: 0;
      --y: 0;
      --zoomRev: 1;
    }
    #drawflow {
      position: relative;
      width: 100%;
      height: 800px;
      border: 1px solid red;
    }

    #drawflow .parent-node:has(.drawflow-node.right) {
        position: fixed;
        left: 0px;
        top: 0px;
        width: 100%;
        height: 100%;
        display: block;
        pointer-events: none;
        z-index: 2;
        transform: translate(calc(var(--x) * var(--zoomRev)), calc(var(--y) * var(--zoomRev))) scale(var(--zoomRev));
    }
    #drawflow .drawflow-node.right {
        right: 0px !important;
        top: 0px !important;
        left: auto !important;
        height: 100%;
        pointer-events: all;

    }
    #drawflow .parent-node:has(.drawflow-node.left) {
        position: fixed;
        left: 0px;
        top: 0px;
        width: 100%;
        height: 100%;
        display: block;
        pointer-events: none;
        z-index: 2;
        transform: translate(calc(var(--x) * var(--zoomRev)), calc(var(--y) * var(--zoomRev))) scale(var(--zoomRev));
    }

    #drawflow .drawflow-node.left {
        left: 0px !important;
        top: 0px !important;
        height: 100%;
        pointer-events: all;

    }

    .drawflow, .drawflow .parent-node  {
        z-index: 1;
    }
  </style>
</head>
<body>
  <div>
    <div id="drawflow"></div>
  </div>
  <script>
    var id = document.getElementById("drawflow");
    const editor = new Drawflow(id);

    editor.zoom_refresh = function(){
      this.canvas_x = (this.canvas_x / this.zoom_last_value) * this.zoom;
      this.canvas_y = (this.canvas_y / this.zoom_last_value) * this.zoom;
      this.zoom_last_value = this.zoom;
      this.precanvas.style.transform = "translate("+this.canvas_x+"px, "+this.canvas_y+"px) scale("+this.zoom+")";
      this.dispatch('zoom', this.zoom);
    }

    editor.start();  
    editor.on("translate", (pos) => {
      document.documentElement.style.setProperty('--x', pos.x*-1 + "px");
      document.documentElement.style.setProperty('--y', pos.y*-1 + "px");
      editor.updateConnectionNodes(`node-${NodeLeft}`)
      editor.updateConnectionNodes(`node-${NodeRight}`)
    })

    editor.on("zoom", (zoom) => {
      document.documentElement.style.setProperty('--zoomRev', 1/zoom);
      document.documentElement.style.setProperty('--x', editor.canvas_x*-1 + "px");
      document.documentElement.style.setProperty('--y', editor.canvas_y*-1 + "px");
      editor.updateConnectionNodes(`node-${NodeLeft}`)
      editor.updateConnectionNodes(`node-${NodeRight}`)
    });

    const NodeLeft = editor.addNode("left", 0, 8, 150, 300, "left", {}, '');
    const NodeRight = editor.addNode("right", 8, 0, 150, 300, "right", {}, '');

    editor.addNode("item", 1, 1, 150, 200, "item", {}, '');
    editor.addNode("item", 1, 1, 300, 300, "item", {}, '');
    editor.addNode("item", 1, 1, 700, 400, "item", {}, '');
    editor.addNode("item", 1, 1, 900, 700, "item", {}, '');
    editor.addNode("item", 1, 1, 500, 550, "item", {}, '');
  </script>
</body>
</html>
chirstius commented 6 months ago

You are a legend! I will look to incorporate this update once the holidays settle down. Thank you!

chirstius commented 5 months ago

Finally got to adding this all in and it works brilliantly. Thank you. Feel free to close this one out unless you have any other thoughts on it.