• 
       
       
       
       
       
       
       
       



  •     

        狗万官网酒店 > 狗万官网下载 > 运用d3.js制造连线动画图与编辑器的主意实例_javascript艺术

        运用d3.js制造连线动画图与编辑器的主意实例_javascript艺术

        来源: 2019-09-09 19:45 我来投稿 参与评论
        这篇文章主要给大家介绍了关于如何运用d3.js制造连线动画图与编辑器的相关资料,文中通过示范代码介绍的独特详细,对大家学习或者使用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 accp = ap[acIndex].ap;
          var curLine = data.element[i].line.concat();
          // 联网节点与开始点的距离
          var disAp = Math.pow((accp[0] - curLine[0][0]),2) +
          Math.pow((accp[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,accp);
          // 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();
         });
         });
         }

        在此只贴出一些代码,如果大家有其他建议和题材,还请留言,谢谢。

        总结

        上述就是这篇文章的方方面面内容了,瞩望本文的情节对大家的上学或者工作具有原则性的参考学习价值,谢谢大家对脚本的师之支持。

        义务编辑:狗万官网酒店
         
         
        0% (0)
         
         
        0% (0)
        机长评论( ) 请自觉遵守互联网相关的富民政策法规,不准发布色情、暴力、反动的议论。
        地名: 匿名?