整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          Canvas實現連線題的方案設計

          Canvas實現連線題的方案設計

          發“連線題”并沒什么難點,這里主要分享的是:實現過程中的一點點布局設計上的小心思。

          想要的效果大體長這樣(如下圖所示):

          乍一看似乎需要獲取每個元素的位置信息、計算連線的端點坐標,似乎很繁瑣,而且每次頁面尺寸變化時,都得重新計算。其實,事情可以比想象中簡單很多…

          一、方案設計

          1.1 將連線題分為上中下三個區塊,其中:

          1)上下區塊均使用flex(justify-content: space-around)

          2)中間區塊為canvas,在style屬性中將width和height設置為100%

          .row-1 { justify-content: space-around; }
          .row-2 {
              height: 0; flex-grow: 1;
              canvas { width: 100%; height: 100%; }
          }
          .row-3 { justify-content: space-around; }

          1.2 這樣設置以后,有幾點好處

          1)計算直線端點(元素中點)坐標就會變得很輕松,只需要排序百分比與canvas的內容寬度相乘即可。

          let x1=(上方元素下標 * 2 + 1) / (上方元素總數 * 2) * canvas.width
          let x2=(下方元素下標 * 2 + 1) / (下方元素總數 * 2) * canvas.width
          // 連線上方端點坐標: (x1, 0)
          // 連線下方端點坐標: (x2, canvas.height)

          2)頁面resize時無需重新計算,頁面也不會亂。當然如果resize前后差異較大,可能連線粗細程度會不美觀。

          經測,一般不重新繪制也問題不大;如果要求高的話,可以在resize時重新繪制一下。(下圖是第一張圖在網頁resize后的效果,線條經過拉伸變細了)

          3)如果你連canvas的尺寸也懶得初始化,也是可以的,只不過效果會差些(線條有點模糊,粗細不美觀),Chrome中canvas默認內容尺寸是300*100,效果如下圖所示(截圖可能視覺效果不明顯):

          二、代碼實現

          線條繪制的相關代碼如下:

          Html

          <canvas ref="canvas" :width="cvsWidth" :height="cvsHeight"></canvas>

          Js

          文由ScriptEcho平臺提供技術支持

          項目地址:傳送門

          基于Cola.js的網絡圖繪制

          應用場景

          Cola.js是一個JavaScript庫,用于繪制交互式網絡圖。它廣泛應用于社交網絡、知識圖譜、生物網絡等領域,幫助用戶可視化和探索復雜的數據關系。

          基本功能

          本代碼實現了使用Cola.js繪制網絡圖的基本功能,包括:

          • 加載外部JavaScript和CSS資源
          • 從JSON文件中讀取網絡圖數據
          • 創建節點、連線和組
          • 設置力導向布局算法的參數
          • 實時更新網絡圖布局,響應拖拽操作和力導向算法的迭代

          功能實現步驟及關鍵代碼分析

          1. 加載外部資源

          const loadStyle=(styleUrl)=> { ... }
          const loadJavascript=(jsUrl)=> { ... }
          

          使用loadStyle和loadJavascript函數異步加載必要的CSS和JavaScript文件。

          2. 讀取網絡圖數據

          d3.json(
            'webcola/website/examples/graphdata/miserables.json',
            function (error, graph) { ... }
          )
          

          使用d3.js讀取JSON格式的網絡圖數據,包括節點和連線信息。

          3. 創建節點、連線和組

          var group=svg
            .selectAll('.group')
            .data(groups)
            .enter()
            .append('rect')
            .classed('group', true)
            ...
          
          var link=svg
            .selectAll('.link')
            .data(graph.links)
            .enter()
            .append('line')
            .attr('class', 'link')
            ...
          

          var node=svg
          .selectAll('.node')
          .data(graph.nodes)
          .enter()
          .append('circle')
          .attr('class', 'node')
          ...

          分別創建節點、連線和組的DOM元素,并綁定相關數據。

          4. 設置力導向布局算法的參數

          dcola
            .nodes(graph.nodes)
            .links(graph.links)
            .groups(groups)
            .jaccardLinkLengths(40, 0.7)
            .avoidOverlaps(true)
            .start(50, 0, 50)
          

          配置力導向布局算法的參數,包括節點、連線、組信息,以及連線長度、避免重疊等約束。

          5. 實時更新布局

          dcola.on('tick', function () { ... })
          

          注冊一個回調函數,在力導向布局算法迭代時觸發,實時更新節點、連線和組的位置。

          總結與展望

          通過開發這段代碼,我們掌握了使用Cola.js繪制交互式網絡圖的技巧。

          經驗與收獲:

          • 深入理解力導向布局算法的原理和參數配置
          • 熟練運用d3.js操作DOM元素
          • 提高JavaScript異步加載和事件處理的能力

          未來拓展與優化:

          • 集成其他布局算法,如隨機布局、力導向布局的變體
          • 優化交互體驗,支持縮放、平移等操作
          • 探索更復雜的數據可視化場景,如多層網絡圖、時間序列網絡圖
          • 更多組件:

          獲取更多Echos

          本文由ScriptEcho平臺提供技術支持

          項目地址:傳送門

          微信搜索ScriptEcho了解更多

          一篇我們學習了如何使用zTree對靜態數據進行綁定并顯示。但是實際運用中數據都是數據庫或者經過計算后得到的,所以這一次我們將上次的Demo改為動態綁定。

          數據表如下,其中平pId為0的是根節點

          上一次講過zTree的回調事件中可以有許多函數,那么我們就利用點擊節點的回調函數進行動態綁定。但是問題來了,首次加載時網頁上并沒有節點,那么我們如何實現點擊節點加載數據呢?思路是這樣的,首次加載時用ajax向后臺發送一個空請求,點擊節點時ajax發送點擊節點的id。后臺收到請求,將請求參數取出,傳入數據庫查詢方法,此時進行判斷,若參數為空說明是首次加載,則執行的sql語句是select * from demo where pId='0' 即取出根節點,然后返回,而參數不為空則說明是點擊節點發出的請求,此時sql語句是select * from demo where pId='"+id+"'" 即取出父節點是我們點擊節點的節點。

          下面上代碼

          額,先看效果圖吧

          前臺

          <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

          <html>

          <head>

          <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

          <title>Insert title here</title>

          <link rel="stylesheet" href="css/metroStyle.css" type="text/css">

          <script type="text/javascript" src="js/jquery-1.4.4.min.js"></script>

          <script type="text/javascript" src="js/jquery.ztree.core.js"></script>

          </head>

          <body align="center" >

          <div id="demotree" style="margin-left:230px;margin-top:50px">

          <ul id="treeDemo" class="ztree"></ul>

          </div>

          </body>

          <script type="text/javascript">

          var treeNodes;

          var setting={

          //外觀

          view: {

          showIcon: true, //設置是否顯示節點圖標

          showLine: true, //設置是否顯示節點與節點之間的連線

          showTitle: true, //設置是否顯示節點的title提示信息

          fontCss : {color:"black",size:30}

          },

          //異步

          async:{

          enable: true,//true代表異步

          url: "/Demo/DataProcessing",//異步獲取數據的地址

          autoParam: ["id=id"]//傳遞id

          },

          //數據類型

          data: {

          simpleData: {

          enable: true, //設置是否啟用簡單數據格式(json格式)

          idKey: "id", //設置啟用簡單數據格式時id對應的屬性名稱(對應json數據中的key)

          pidKey: "pId" //設置啟用簡單數據格式時parentId對應的屬性名稱,ztree根據id及pid層級關系構建樹結構

          }

          },

          //回調事件

          callback: {

          //點擊節點事件

          onClick: function(event, treeId, treeNode, clickFlag) {

          // 判斷是否父節點 ,是父節點則ajax向后臺發送節點名稱以獲取子文件

          if(treeNode.isParent){

          //判斷節點是否折疊,若已折疊則請求子節點的數據,返回后展開

          if(treeNode.collapse==true){

          //ajax提交請求

          $.ajax({

          url: "/Demo/DataProcessing",//請求的action路徑

          type:"post", //提交方式

          async:false,//同步

          dataType:"json",//數據格式是json

          data:{'id':treeNode.id}, //傳遞被點擊節點的id

          error: function(){//請求失敗后執行的事件

          },

          //請求成功后執行事件

          success:function(data)

          {

          var jsondata=data;

          //json為空說明沒有子節點,不用執行操作

          if(jsondata==null || jsondata==""){ }

          //否則子節點添加到父節點

          else{

          var treeObj=$.fn.zTree.getZTreeObj("demotree");

          var parentZNode=treeObj.getNodeByParam("name",treeNode.name, null);//獲取指定父節點

          newNode=treeObj.addNodes(parentZNode,jsondata, false);

          }

          }

          });

          }

          }

          }

          }

          };

          //首次加載用ajax請求初始化

          $(function(){

          $.ajax({

          async : false,

          cache:false,

          type: 'POST',

          dataType : "json",

          url: "/Demo/DataProcessing",//請求的action路徑

          error: function () {//請求失敗處理函數

          alert('請求失敗');

          },

          success:function(data){ //請求成功后處理函數。

          treeNodes=data; //把后臺封裝好的簡單Json格式賦給treeNodes

          }

          });

          });

          var zTree;

          $(document).ready(function(){

          $.fn.zTree.init($("#treeDemo"), setting, treeNodes);

          });

          </script>

          </html>

          后臺servlet

          package myServlet;

          import java.io.IOException;

          import java.util.List;

          import javax.servlet.ServletException;

          import javax.servlet.annotation.WebServlet;

          import javax.servlet.http.HttpServlet;

          import javax.servlet.http.HttpServletRequest;

          import javax.servlet.http.HttpServletResponse;

          import Db.DbOperate;

          import Model.Demo;

          import com.alibaba.fastjson.JSON;

          /**

          * Servlet implementation class DataProcessing

          */

          @WebServlet("/DataProcessing")

          public class DataProcessing extends HttpServlet {

          private static final long serialVersionUID=1L;

          /**

          * @see HttpServlet#HttpServlet()

          */

          public DataProcessing() {

          super();

          // TODO Auto-generated constructor stub

          }

          /**

          * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)

          */

          protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

          // TODO Auto-generated method stub

          }

          /**

          * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)

          */

          protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

          // TODO Auto-generated method stub

          String id=request.getParameter("id");

          List<Demo> list;

          try {

          list=new DbOperate().getDemo(id);

          String jsonArr=JSON.toJSONString(list);

          System.out.println(jsonArr);

          //response.setContentType("application/json; charset=UTF-8");

          response.setCharacterEncoding("UTF-8");

          response.getWriter().print(jsonArr);

          } catch (Exception e) {

          // TODO Auto-generated catch block

          e.printStackTrace();

          }

          }

          }

          數據庫查詢

          package Db;

          import java.sql.Connection;

          import java.sql.DriverManager;

          import java.sql.ResultSet;

          import java.sql.Statement;

          import java.util.ArrayList;

          import java.util.List;

          import Model.Demo;

          public class DbOperate {

          private static String url="jdbc:mysql://localhost:3306/dir?rewriteBatchedStatements=true";

          private static String user="root";

          private static String password="";

          public List<Demo> getDemo(String id) throws Exception {

          Class.forName("com.mysql.jdbc.Driver");

          System.out.print("!!!!!!!!!");

          Connection conn=DriverManager.getConnection(url, user, password);

          List<Demo> list=new ArrayList<Demo>();

          Statement s=null;

          String sql;

          if(id==null||id==""){

          sql="select * from demo where pId='0'";

          }

          else{

          sql="select * from demo where pId='"+id+"'";

          }

          s=conn.createStatement();

          ResultSet rs=s.executeQuery(sql);

          while (rs.next()) {

          list.add(new Demo(rs.getString("id"), rs.getString("name"), rs.getString("pId"), rs.getInt("isParent")));

          }

          if (s !=null) {

          s.close();

          }

          if (conn !=null) {

          conn.close();

          }

          return list;

          }

          }

          實體類

          package Model;

          public class Demo {

          private String id;

          private String name;

          private String pId;

          private int isParent;

          public Demo(String id, String name, String pId, int isParent) {

          super();

          this.id=id;

          this.name=name;

          this.pId=pId;

          this.isParent=isParent;

          }

          public String getId() {

          return id;

          }

          public void setId(String id) {

          this.id=id;

          }

          public String getName() {

          return name;

          }

          public void setName(String name) {

          this.name=name;

          }

          public String getpId() {

          return pId;

          }

          public void setpId(String pId) {

          this.pId=pId;

          }

          public int getIsParent() {

          return isParent;

          }

          public void setIsParent(int isParent) {

          this.isParent=isParent;

          }

          }

          源碼可以私信獲取,謝謝支持


          主站蜘蛛池模板: 亚洲欧美日韩一区二区三区在线 | 蜜桃传媒一区二区亚洲AV| 国产拳头交一区二区| 日亚毛片免费乱码不卡一区| 国产伦精品一区二区免费| 亚洲日本乱码一区二区在线二产线| 亚洲一区二区三区免费在线观看| 成人一区二区三区视频在线观看 | 在线观看国产一区亚洲bd| 中文字幕一区二区三区永久| 午夜一区二区在线观看| 色老头在线一区二区三区| 国产一区在线视频观看| 国产精品久久一区二区三区| 国产色精品vr一区区三区| 国产激情一区二区三区在线观看| 国产一区视频在线| 国精无码欧精品亚洲一区| 久久久久人妻精品一区三寸蜜桃| 欧美日韩国产免费一区二区三区| 日本一区二区不卡视频| 一区二区精品在线观看| 精品3d动漫视频一区在线观看| 久久精品国内一区二区三区| 精品亚洲AV无码一区二区| bt7086福利一区国产| 国产欧美色一区二区三区| 麻豆国产在线不卡一区二区| 亚洲AV无码片一区二区三区 | 日韩爆乳一区二区无码| 一区二区不卡久久精品| 精品女同一区二区三区在线| 天天躁日日躁狠狠躁一区| 卡通动漫中文字幕第一区| 亚洲国产成人久久一区WWW| 精品一区二区在线观看| 极品尤物一区二区三区| 无码人妻精品一区二| 亚洲午夜福利AV一区二区无码| 色噜噜狠狠一区二区三区果冻 | 亚洲国产激情在线一区|