zoukankan      html  css  js  c++  java
  • jsplumb.js小地图和框选实现策略

    背景:

    jsplumb.js官方开源免费版不支持内置小部件功能,比如:小地图、框选、编辑等快捷功能,因此参考收费版功能自定义设置。

    一、小地图:

    选用vue编辑画布流程图+自定义点击节点弹框设置内容:以下是生成后的结构

      <div class="jtk-demo-main">
        <!-- this is the main drawing area -->
        <div class="jtk-demo-canvas canvas-wide jtk-surface">
          <!-- miniview -->
          <div class="miniview jtk-miniview"  style="display: block;">
          </div>
          <!--miniview surface -->
          <div class="jtk-surface-canvas"></div>
        </div>
        <div class="jtk-demo-rhs">
          <!-- the current dataset -->
          <div class="jtk-demo-dataset"></div>
        </div>
      </div>

    工具版生成模板demo:

    <template>
      <div class="jtk-demo-main">
        <!-- this is the main drawing area -->
        <div class="jtk-demo-canvas">
          <!-- miniview -->
          <div class="miniview"></div>
        </div>
      </div>
    </template>
    
    <script>
    import "./index.css";
    import "./jsplumb.js";// 局部引入
    export default {
      data() {
        return {
          jsPlumb: null,
        jsplumToolkit:null
    }; }, mounted() {
      this.jsplumb = this.$jsplumb || jsplumb //全局注册或局部引入
       this.jsplumbToolkit = this.$jsplumbToolkit || jsplumbToolkit //全局注册或局部引入
      if(this.jsplumb){
       this.info()
      }
    }, methods: { init() { this.jsPlumb.ready(function() { // prepare some data var data = { nodes: [ { id: "1", label: "jsPlumb" }, { id: "2", label: "Toolkit" }, { id: "3", label: "Hello" }, { id: "4", label: "World" } ], edges: [ { source: "1", target: "2" }, { source: "2", target: "3" }, { source: "3", target: "4" }, { source: "4", target: "1" } ] }; // get a new instance of the Toolkit var toolkit = this.jsPlumbToolkit.newInstance(); var mainElement = document.querySelector(".jtk-demo-main"), canvasElement = mainElement.querySelector(".jtk-demo-canvas"), miniviewElement = mainElement.querySelector(".miniview"); toolkit.render({ // 实际是js动态创建div插入绑定生成画布 container: canvasElement, miniview: { container: miniviewElement }, layout: { type: "Spring" } }); // load the data. toolkit.load({ data: data }); }); } } }; </script>

    了解原理后,自己设置一个小地图:

    1..jquery操作(只是编写时简化,编译后可能更复杂,不推荐);

    2.原生js操作;

        <!-- 2.画布内容:左侧1.拖拽节点菜单 -->
        <div class="jtk-demo-main">
          <!-- 2.1顶栏工具栏插口 -->
          <div style="padding: 5px">
            <slot name="header" />
          </div>
          <!-- 2.2画布编辑框 -->
          <div
            style="border:1px solid #e1e1e1;position:relative;"
            id="flow-chart-container"
          >
            <!-- 2.3右上角图标 -->
             <span>款选图标</span>  
            <!-- 2.4.miniview:小地图 -->
            <div
              id="miniview"
              @mousewheel="miniViewRoll(this)"
            >
              <div
                class="miniview jtk-miniview"
                :class="collapsed?'jtk-miniview-collapsed':''"
                id="jtk-miniview"
                style="display: block;"
              >
                <div
                  class="jtk-miniview-canvas"
                  id="jtk-miniview-canvas"
                />
                <div class="jtk-miniview-panner" />
                <div
                  class="jtk-miniview-collapse"
                  @click="collapsed=!collapsed"
                />
              </div>
            </div>
            <!-- 2.5自定义节点 -->
            <div
              :connections="connections"
              :elements="elements"
              :key="id"
              ref="chart"
              source-key="source"
              target-key="target"
              @element-click="handleELementClick"
              @container-click="onContainerClcik"
              @on-element-new="onElementNew"
              @on-element-delete="onElementDelete"
              @on-connection-new="onConnectionNew"
              @on-connection-delete="onConnectionDelete"
              @on-connection-drag-start="onConnectionDragStart"
              @on-connection-drag-end="onConnectionDragEnd"
              @on-connection-moved="onConnectionMoved"
              :left="x"
              :top="y"
            >
              <template slot-scope="scope">
                <slot :elData="scope.elData">
                  <ElementSlot :el-data="scope.elData" />
                </slot>
              </template>
            </div>
          </div>
        </div>

    这里只分析小地图实现逻辑,画布绘制分:节点元素+节点连线,小地图视图只需要遍历生成dom节点即可,问题是如何与实际生成的画布绑定变动后的方位和缩放比例。

          this.elements.forEach((v,i)=>{// 起始点设置为圆点
            const canvasBox = document.getElementById('jtk-miniview-canvas')
            const nodeBox = document.createElement('div')
            nodeBox.className ="jtk-miniview-element"
            nodeBox.style =i===0?"border-radius: 50%; 40px; height: 40px; position: absolute;":" 80px; height: 30px; position: absolute;"
            nodeBox.style.left = v.x+'px'
            nodeBox.style.top = v.y+'px'
            canvasBox.appendChild(nodeBox)
          })

    1.小地图移动和缩放:

    .jtk-miniview-panner {
        border: 5px dotted WhiteSmoke;
        opacity: 0.4;
        background-color: rgb(79, 111, 126);
        cursor: move;
        cursor: -webkit-grab;// 上面是静态样式,下面是动态绑定
        width: 1903px;
        height: 937px;
        position: absolute;
        transform-origin: 0px 0px;
        transform: scale(0.1);
        left: -81.0315px;
        top: 8.70836px;
    }

    解析:实际的画布大小是1903*937,小地图缩放倍数是0.1,定位偏差left、top是实际相反方向的取值:比如画布向左移动,小地图的遮罩层应该向右移动,这样就能看清全局画布占位。

    而缩放比例改变transform的同时(也是取反),也会改变定位偏差的大小。

    如果小地图只是模仿官网节点效果,可以直接绘制节点(实际需求需要绘制线条):

      mounted() {
        this.instance = this.$refs.chart;// 实例化流程图对象
        this.miniviewNode();// 绘制默认小地图(只有开始节点)
        window.addEventListener('keyup',this.handleKeyup)// 监听键盘事件
      },
      destroyed () {// 销毁粘贴板内容+键盘监听事件
        localStorage.removeItem("kp-canvas-copy"); 
        window.removeEventListener('keyup',this.handleKeyup)
        // window.removeEventListener('scroll',this.handleScroll)
      },
      methods: {
        // 键盘事件
        handleKeyup(event){
          let self = this;
          document.onkeydown = function (e) {
            let evn = e || event;
            let key = evn.keyCode || evn.which || evn.charCode;
            // ctrl + v   
            if (evn.keyCode === 86 && evn.ctrlKey) {
            //  console.log(666)
            }
            // delete
            if (key === 46) {
            //  console.log(7777)
            }
          }
        },
        miniViewRoll() { // 小地图滚动
          if (event.wheelDelta === 120) {
            //±120
            this.handleZoomIn();// 放大
          } else {
            this.handleZoomOut();// 缩小
          }
        },
       miniviewNode(){// 绘制静态节点:先移除已有节点
          let childs = [1,2,3,4];
          for (var i = childs.length - 1; i >= 0; i--) {
            canvasBox.removeChild(childs[i]);
          }
          elements.forEach((v, i) => {
            const nodeBox = document.createElement("div");
            nodeBox.className = "jtk-miniview-element";
            nodeBox.style =i === 0 ? "border-radius: 50%; 40px; height: 40px; position: absolute;": " 80px; height: 30px; position: absolute;";
            nodeBox.style.left = v.x + "px";
            nodeBox.style.top = v.y + "px";
            canvasBox.appendChild(nodeBox);
            // 根据画布大小设置0.1倍的蒙版大小
            const canvasBox = document.querySelector("#flow-chart-container");
            this.widthPanner = canvasBox.offsetWidth;
            this.heightPanner = canvasBox.offsetHeight;
          });
        },
        // 框选节点
        CheckBoxNodes() {
          const self = this;
          var stateBar = document.getElementById("flow-chart-container");
          stateBar.style["cursor"] = "auto";
          // stateBar.style['pointer-events'] = 'none'
          stateBar.onmousedown = function(e) {
            var posx = e.clientX;
            var posy = e.clientY;
            var div = document.createElement("div");
            div.id = "selectDiv";
            div.className = "tempDiv";
            div.style.left = e.clientX + "px";
            div.style.top = e.clientY + "px";
            document.body.appendChild(div);
            document.onmousemove = function(ev) {
              div.style.left = Math.min(ev.clientX, posx) + "px";
              div.style.top = Math.min(ev.clientY, posy) + "px";
              div.style.width = Math.abs(posx - ev.clientX) + "px";
              div.style.height = Math.abs(posy - ev.clientY) + "px";
              document.onmouseup = function() {
                var selDiv = document.getElementById("selectDiv");
                var fileDivs = document.getElementsByClassName("node");
                var selectedEls = [];
                var l = selDiv.offsetLeft; // 减去容器位置
                var t = selDiv.offsetTop; // 减去容器位置
                var w = selDiv.offsetWidth;
                var h = selDiv.offsetHeight;
                for (var i = 0; i < fileDivs.length; i++) {
                  //所有节点
                  var sl = fileDivs[i].getBoundingClientRect().left;
                  var st = fileDivs[i].getBoundingClientRect().top;
                  if (sl > l && st > t && sl < l + w && st < t + h) {
                    // 区域内节点
                    fileDivs[i].className += " " + "is-active";
                    selectedEls.push(fileDivs[i].id);
                  }
                }
                self.selectedEls = selectedEls;
                if(selectedEls.length>0){
                  self.$message.success("已批量选中,通过ctrl+V粘贴(允许跨画布)或delete删除")
                }
                //********************************** */
                stateBar.style.cursor = "grab";
                div.parentNode.removeChild(div);
                stateBar.onmousedown = null;
                document.onmousemove = null;
                document.onmouseup = null;
                setTimeout(() => {// 设置延迟禁止移动画布
                  self.checked = false;
                }, 200);
              };
            };
          };
        },
      }

    页面设置:点击图标、切换样式、禁用移动画布、添加图标效果等

            <div
              id="miniview"
              @mousewheel="miniViewRoll(this)"
            >
              <div
                class="miniview jtk-miniview"
                :class="collapsed ? 'jtk-miniview-collapsed' : ''"
                id="jtk-miniview"
                style="display: block;"
              >
              </div>
              <div style="position: absolute;right: 10px;bottom: 5px;z-index: 5;cursor: pointer;color: #63656E;">
                <i
                  :class="collapsed?'fa fa-eye-slash':'fa fa-eye'"
                  @click.stop="collapsed = !collapsed"
                />
              </div>
            </div>

    -大概思路就是这些,细节调整根据自己的情况来定-

  • 相关阅读:
    js数组条件筛选——map()
    (转)适用微信小程序的table表格(带隔行变色)
    nodejs学习笔记<七> 路由
    酷我音乐(在线试听)下载方法
    nodejs学习笔记<二> 使用node创建基础服务器
    JS BOM 窗口中的使用
    JS DOM
    JS 寄生 继承
    JS字面量创建方式的优缺点
    JS 构造函数
  • 原文地址:https://www.cnblogs.com/wheatCatcher/p/13066053.html
Copyright © 2011-2022 走看看