<span id="mktg5"></span>

<i id="mktg5"><meter id="mktg5"></meter></i>

        <label id="mktg5"><meter id="mktg5"></meter></label>
        最新文章專題視頻專題問答1問答10問答100問答1000問答2000關鍵字專題1關鍵字專題50關鍵字專題500關鍵字專題1500TAG最新視頻文章推薦1 推薦3 推薦5 推薦7 推薦9 推薦11 推薦13 推薦15 推薦17 推薦19 推薦21 推薦23 推薦25 推薦27 推薦29 推薦31 推薦33 推薦35 推薦37視頻文章20視頻文章30視頻文章40視頻文章50視頻文章60 視頻文章70視頻文章80視頻文章90視頻文章100視頻文章120視頻文章140 視頻2關鍵字專題關鍵字專題tag2tag3文章專題文章專題2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章專題3
        問答文章1 問答文章501 問答文章1001 問答文章1501 問答文章2001 問答文章2501 問答文章3001 問答文章3501 問答文章4001 問答文章4501 問答文章5001 問答文章5501 問答文章6001 問答文章6501 問答文章7001 問答文章7501 問答文章8001 問答文章8501 問答文章9001 問答文章9501
        當前位置: 首頁 - 科技 - 知識百科 - 正文

        利用d3.js制作連線動畫圖與編輯器的方法實例

        來源:懂視網 責編:小采 時間:2020-11-27 21:51:08
        文檔

        利用d3.js制作連線動畫圖與編輯器的方法實例

        利用d3.js制作連線動畫圖與編輯器的方法實例: 連線動畫圖 編輯器 效果如上圖所示。 本項目使用主要d3.jsv4制作,分兩部分,一個是實際展示的連線動畫圖,另一個是管理人員使用鼠標編輯連線的頁面。對于d3.js如何引入圖片,如何畫線等基礎功能,這里就不再介紹了,大家可以找一些入門文章看一下。這里
        推薦度:
        導讀利用d3.js制作連線動畫圖與編輯器的方法實例: 連線動畫圖 編輯器 效果如上圖所示。 本項目使用主要d3.jsv4制作,分兩部分,一個是實際展示的連線動畫圖,另一個是管理人員使用鼠標編輯連線的頁面。對于d3.js如何引入圖片,如何畫線等基礎功能,這里就不再介紹了,大家可以找一些入門文章看一下。這里

        連線動畫圖

        編輯器

        效果如上圖所示。

        本項目使用主要d3.jsv4制作,分兩部分,一個是實際展示的連線動畫圖,另一個是管理人員使用鼠標編輯連線的頁面。對于d3.js如何引入圖片,如何畫線等基礎功能,這里就不再介紹了,大家可以找一些入門文章看一下。這里主要介紹一下重點問題。

        1.連線動畫圖

        此圖的主要功能是每隔給定時間,通過ajax請求后臺數據,并根據返回的數據動態改變每個圖片下方的數值,動態改變連線上的動畫流動方向和是否流動。

        首先,確定圖表中需要配置的內容,如各圖片存儲位置,連線和動畫顏色,圖片和連線的坐標等。這些數據需要在html中進行配置,最好寫成object對象,賦值給我們自己的圖表類的函數。比如:

        var data = {
         element:[{
         image: 'img/work.png',
         pos:[1,1], // 圖片位置
         linePoint:[], // 圖片發出線段坐標數組
         lineDir:0, // 線段動畫方向
         title: '工作'
         }],
         lineColor:'black', // 連線顏色
         animateColor: 'red', // 動畫顏色
        };
        var chart = new Myd3chart('#chart');
        chart.lineChart(data);

        其中圖片發出的線段坐標數組,使用外部文件提供,此文件由之后介紹的編輯器生成。

        在設計我們自己的圖表函數時,最好把每個功能劃分成的函數,這樣方便以后的維護和擴展。

        動畫線段采用css的方式,有動畫的線段添加此css即可:

        .animate-line{
         fill: none;
         stroke-width: 1;
         stroke-dasharray: 50 100;
         stroke-dashoffset: 0;
         animation: stroke 6s infinite linear;
        }
        @keyframes stroke {
         100% {
         stroke-dashoffset: 500; /* 如果反向移動改為-500 */
         }
        }

        這個圖表的難點在于動態改變連線上的流動動畫,因為A線段的終點會連接到B線段上,如果B線段動畫停止,則A線段上的動畫仍然要從B上經過,而不能簡單停止B線段上的動畫。而且如果B線段上的接入點不止一個,還要判斷接入點之間的順序,只顯示最靠近B起始點的接入點的動畫。另外還要判斷接入線段上是否有接入線段,層級關系里面如果有1個線段有動畫,則此接入點就有動畫流出。(這里說起來有點繞)

        我的方法是:

        1)統計每個線段上的所有接入點,這里就是圖片名稱,用于判斷此線段是否有動畫流出。

        2)接收后臺傳來的數據時,判斷每個線段是否有動畫,如果有動畫,則直接恢復其動畫線段的起始點坐標;如果沒有動畫,則判斷最靠近起始點的接入點是否有動畫,如果有動畫則將動畫線段的起始點改為此接入點坐標。

        // 統計接入點
         function findAccessPoint() {
         var accessPoints = [];
         // 記錄每個線段上的接入點,data為配置數據
         data.eles.forEach(function(d, i){
         if(d.line.length == 0){
         return;
         }
         var acsp = {
         name: d.title.text,
         ap: [], // 接入點,按順序排列,頭部離開始點近
         };
         // 本線段上,每兩相鄰的點作為一個元素存入數組
         var linePair = [];
         // 本線段起始點
         var startPos = d.line[0];
         d.line.forEach(function(dd, di){
         if(d.line[di+1]){
         var pair = {
         start: dd,
         end: d.line[di+1]
         };
         linePair.push(pair);
         } 
         });
         // 對每兩相鄰的點,查找接入點
         linePair.forEach(function(dd, di){
         chartData.eles.forEach(function(ddd, ddi){
         // 排除自己,查找自己線段上的接入點
         if(i != ddi && ddd.line.length > 1){
         // 得到此線段終點
         var pos = ddd.line[ddd.line.length - 1];
         // dd.start開始點,dd.end結束點
         // 用x坐標計算在本線段上的y坐標,再和實際的y坐標比較
         var computeY = dd.start[1] + 
         (pos[0] - dd.start[0])*(dd.end[1] - dd.start[1])/(dd.end[0] - dd.start[0]);
         var dif = Math.abs(computeY - pos[1]);
         // 如果誤差在2以內,并且此線終點在當前線起點和終點之間
         // 認為此點為接入點
         if(dif < 2 && (
         (
         ((pos[0] > dd.start[0]) && (pos[0] < dd.end[0])) ||
         ((pos[0] < dd.start[0]) && (pos[0] > dd.end[0]))
         ) && (
         ((pos[1] > dd.start[1]) && (pos[1] < dd.end[1])) ||
         ((pos[1] < dd.start[1]) && (pos[1] > dd.end[1]))
         )
         )) {
         var dis = Math.pow((pos[0] - startPos[0]),2) + Math.pow((pos[1] - startPos[1]),2);
         var ap = {
         name: ddd.title.text,
         ap: pos,
         distance: dis, // 距離起始點的距離
         allNames: [], // 所有通過此接入點的站點名稱
         }
         acsp.ap.push(ap); 
         }
         }
         });
         })
         accessPoints.push(acsp);
         });
        
         //對所有的接入點,按與起始點的距離排序,并查找此接入點的上層站點
         accessPoints.forEach(function(d, i){
         // 按distance由小到大排序
         d.ap.sort(function(a, b){
         return a.distance - b.distance;
         });
         // 查找每個接入點的上層站點
         d.ap.forEach(function(dd, di){
         findPoint(dd.name, dd.allNames);
         });
         });
         // name是接入點名稱,arr是該接入點的allNames
         function findPoint(name, arr){
         accessPoints.forEach(function(d, i){
         // 在數組中找到指定名稱的項
         if(d.name === name){
         if(d.ap.length>0){
         // 把該項下面的ap中的名稱加入給定arr
         d.ap.forEach(function(dd, di){
         arr.push(dd.name);
         // 如果該點內的allNames已經有值則直接加入
         if(dd.allNames.length>0){
         dd.allNames.forEach(function(d, i){
         arr.push(d);
         });
         } else{
         // 遞歸查找子接入點
         findPoint(dd.name, arr);
         }
         });
         } else {
         return;
         }
         }else{
         return;
         }
         });
         }
         }

        以上函數的運行結果會產生一個對象,存儲每個接入線段上‘掛載'的接入點,目的就是改變動畫時方便判斷。

        // 更新線條動畫
         aniLine.each(function(d, i){
         var curLine = d3.select(this);
         // 找到對應的動畫line
         if (dd.name === curLine.attr('tag')) {
         // 處理動畫是否運行
         if (dd.ani) {
         // 此線條動畫運行
         curLine.style('animation-play-state', 'running');
         curLine.style('display', 'inline');
         // 如果動畫運行,則恢復原始動畫路徑
         curLine.attr('d', function(d){
         return line(chartData.eles[i].line);
         });
         } else {
         // 此線條動畫停止
         // 先查找離本線段開始點最近的接入點
         var acp = accessPoints;
         // 從accessPoints中找到本節點的接入點集合
         var ap = [];
         acp.forEach(function(acd, aci){
         if(acd.name === dd.name){
         ap = acd.ap;
         }
         }); 
         // 最近有動畫接入點序號
         var acIndex = -1;
         // 找到最近的有動畫接入點,遠近按數組序號遞增
         for(var j=0;j<ap.length;j++){
         // 復制所有子接入點數組
         var allNames = ap[j].allNames.concat();
         // 將接入點名稱也加入
         allNames.push(ap[j].name);
         // 判斷此接入點樹中是否有動畫,如果1個有就可以
         allNames.forEach(function(name,ani){
         data.forEach(function(datad, datai){
         if(datad.name === name){
         if(datad.ani){
         acIndex = j;
         return;
         }
         }
         });
         });
         if(acIndex != -1) {
         break;
         }
         }
         // 如果存在有動畫接入點
         if(acIndex != -1){
         curLine.style('animation-play-state', 'running');
         curLine.style('display', 'inline');
         curLine.attr('d', function(d){
         var a = ap[acIndex].ap;
         var curLine = data.element[i].line.concat();
         // 接入節點與開始點的距離
         var disAp = Math.pow((a[0] - curLine[0][0]),2) +
         Math.pow((a[1] - curLine[0][1]),2);
         // 如果當前線段中有離開始節點比接入點近的節點
         // 則刪除此節點
         curLine.forEach(function(curld, curli){
         if(curli > 0){
         var dis = Math.pow((curld[0] - curLine[0][0]),2) +
         Math.pow((curld[1] - curLine[0][1]),2);
         if(dis < disAp){
         // 刪除此點
         curLine.splice(curli,1);
         }
         }
         });
         // 從此接入點處開始動畫
         curLine.splice(0,1,a);
         // debugger;
         return line(curLine);
         });
         }else{
         // 此線條動畫停止
         curLine.style('animation-play-state', 'paused');
         curLine.style('display', 'none');
         }
         }
         }

        2.編輯器

        由于本圖表需要配置大量坐標,如果手動填寫的話效率十分低下,所以需要開發一個編輯器用來修改圖表。

        編輯器的主要使用方法為,使用鼠標拖動圖標,雙擊確定起始位置并開始實時畫線狀態,隨著鼠標移動動態畫出線段,單擊確定臨時終點,再單擊確定下一個終點,右擊結束動態畫線狀態。如果鼠標單擊其他圖標,則終點為該圖標的起始坐標。本程序的實時畫線部分進行了傾斜的約束,即左傾或右傾30度角。

        編輯器比展示圖要簡單一些,復雜部分在事件處理。

        // 拖動圖標
         var draging = d3.drag()
         .on('drag', function () {
         // 當長寬相同時,iconSize是圖標大小[寬,高]
         var move = iconSize[0] / 2,
         moveSubBg = [25, 53.5], moveTitle = [25, 50];
         var g = d3.select(this),
         eventX = d3.event.x - move,
         eventY = d3.event.y - move;
         // 設定圖標位置
         g.select('.image')
         .attr('x', eventX)
         .attr('y', eventY);
         })
         // 拖拽結束
         .on('end', function () {
         var g = d3.select(this);
         g.select('.subBg')
         .attr('transform', function (d, i) {
         // 對子標簽的處理,自動符合字符串長度
         var x = parseFloat(d3.select(this).attr('x')) + parseFloat(d3.select(this).attr('width')) / 2,
         // y沒被縮放,所以不用處理
         y = d3.select(this).attr('y'),
         dsl = (d.title.subTitle.text + '').length;
         var scaleX = dsl * 5.5;
         return 'translate(' + x + ' ' + y + ') scale(' + scaleX + ', 1) translate(' + -x + ' ' + -y + ')';
         });
         });
         // 圖標組增加拖動事件
         imageGs.call(draging);

        以上拖動事件,只是調用基本方法。

        實時畫線功能需要提前定義臨時存儲對象,用來存儲鼠標移動時線段的終點坐標。

        // 鼠標移動時,實時畫線到鼠標當前位置,_bodyRect為主區域
         _bodyRect.on('mousemove', function(){
         // 如果不處于實時畫線狀態
         if(!_chartData.drawing){
         return;
         }
         // 如果沒有端點名稱
         if (!_chartData.linePrePare.name) {
         return;
         }
         /* 實時畫線 */
         // 判斷線段傾斜方向,linePrePare為線段臨時存儲
         var preLines = linePrePare.lines;
         var mousePos = d3.mouse(_bodyRect.node()),
         beforePos = preLines[preLines.length - 1], newy,
         newPos = [];
         if((mousePos[0]>beforePos[0] && mousePos[1]>beforePos[1]) || (mousePos[0]<beforePos[0] && mousePos[1]<beforePos[1])){
         // 向左傾斜\ 左上到右下:y = cy + 0.7*(x-cx)
         newy = beforePos[1] + 0.7 * (mousePos[0] - beforePos[0]);
         } else {
         // 向右傾斜/ 左下到右上:y = cy - 0.7*(cx-x)
         newy = beforePos[1] - 0.7 * (mousePos[0] - beforePos[0]);
         }
         newPos = [mousePos[0], newy];
         // 移除舊線
         if(_chartData.tempLine.line){
         _chartData.tempLine.pos = [];
         _chartData.tempLine.line.remove();
         }
         // 畫新線,tempLine為實時畫線的臨時存儲
         _chartData.tempLine.line = _chartData.lineRootG.append('path')
         .attr('class', 'line-path')
         .attr('stroke', chartData.line.color)
         .attr('stroke-width', chartData.line.width)
         .attr('fill', 'none')
         .attr('d', function () {
         var newLine = [
         preLines[preLines.length - 1],
         newPos
         ];
         _chartData.tempLine.pos = newPos;
         return line(newLine);
         });
        
         // 當鼠標移入某個建筑圖標范圍時
         _chartData.imageGs.on('mouseenter', function(d, i){
         // 移除舊線
         if(_chartData.tempLine.line){
         _chartData.tempLine.pos = [];
         _chartData.tempLine.line.remove();
         }
         // 得到圖標中心點坐標
         var posX = parseFloat(d3.select(this).select('.image').attr('x')) + _chartConf.baseSize[0] / 2;
         var posY = parseFloat(d3.select(this).select('.image').attr('y')) + _chartConf.baseSize[1] / 2;
         // 將此建筑圖標的中心點坐標作為終點坐標畫線
         _chartData.tempLine.line = _chartData.lineRootG.append('path')
         .attr('class', 'line-path')
         .attr('stroke', chartData.line.color)
         .attr('stroke-width', chartData.line.width)
         .attr('fill', 'none')
         .attr('d', function () {
         var newLine = [
         preLines[preLines.length - 1],
         [posX,posY]
         ];
         _chartData.tempLine.pos = [posX,posY];
         return line(newLine);
         });
         });
         // 當鼠標移出圖標區域
         _chartData.imageGs.on('mouseleave', function(d, i){
         // 移除舊線
         if(_chartData.tempLine.line){
         _chartData.tempLine.pos = [];
         _chartData.tempLine.line.remove();
         }
         });
         // 對圖標單擊鼠標,保存線
         _chartData.imageGs.on('click', function (d, i) {
         // 保存臨時線
         drawLine();
         // 停止實時畫線
         exitDrawing();
         });
         });
         // 點擊鼠標右鍵,停止實時畫線
         _bodyRect.on('contextmenu', function(){
         // 停止實時畫線
         exitDrawing();
         d3.event.preventDefault();
         });
         });
         }

        在此只貼出部分代碼,如果大家有任何建議和問題,還請留言,謝謝。

        總結

        聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

        文檔

        利用d3.js制作連線動畫圖與編輯器的方法實例

        利用d3.js制作連線動畫圖與編輯器的方法實例: 連線動畫圖 編輯器 效果如上圖所示。 本項目使用主要d3.jsv4制作,分兩部分,一個是實際展示的連線動畫圖,另一個是管理人員使用鼠標編輯連線的頁面。對于d3.js如何引入圖片,如何畫線等基礎功能,這里就不再介紹了,大家可以找一些入門文章看一下。這里
        推薦度:
        • 熱門焦點

        最新推薦

        猜你喜歡

        熱門推薦

        專題
        Top
        主站蜘蛛池模板: 一级黄色毛片免费看| 国产成人免费爽爽爽视频| 久久精品成人免费国产片小草| 精品无码无人网站免费视频| 老司机永久免费网站在线观看| 亚洲日本va在线视频观看| 亚洲国产精华液2020| 99re免费99re在线视频手机版| 国产成人免费片在线视频观看| 亚洲国产精品一区| yy一级毛片免费视频| 久久久久亚洲精品无码网址| 亚洲午夜理论片在线观看| 日韩插啊免费视频在线观看| 亚洲国产香蕉碰碰人人| 亚洲一区二区三区免费视频| 国产AV无码专区亚洲AV漫画 | 免费国产不卡午夜福在线| 久久夜色精品国产噜噜亚洲AV| 一级黄色免费毛片| 亚洲中文字幕无码久久2017| 国产精品免费看久久久| 中文字幕一精品亚洲无线一区| 人妻在线日韩免费视频| 亚洲人成国产精品无码| 亚洲AV噜噜一区二区三区| 91久久成人免费| 亚洲AV无码AV日韩AV网站| 久久影视综合亚洲| 成年人网站免费视频| 亚洲国产中文在线二区三区免| 免费人成网站在线观看不卡| 亚洲伊人色欲综合网| 91热成人精品国产免费| 美美女高清毛片视频黄的一免费| 免费看片A级毛片免费看| 中文字幕无码免费久久9一区9 | 女人18毛片水真多免费播放| 亚洲国语在线视频手机在线| xxxxx免费视频| 免费一级全黄少妇性色生活片|