手机版

一个用d3.js制作连接动画图和编辑器的例子

时间:2021-08-20 来源:互联网 编辑:宝哥软件园 浏览:

连接动画

编者编辑

效果如上图。

这个项目是用d3.jsv4制作的,分为两部分,一部分是实际连接的动画图,另一部分是管理人员用鼠标编辑连接的页面。对于d3.js的基本功能,比如如何介绍图片,如何画线,这里就不介绍了。可以找一些介绍性的文章看看。本文主要介绍其中的关键问题。

1.连接动画图

这张图片的主要功能是通过ajax在给定的时间间隔内请求后台数据,并根据返回的数据动态改变每张图片下方的值,动态改变动画流向以及是否在连接线上流动。

首先确定图表中需要配置的内容,比如每张图片的存储位置,连接和动画的颜色,图片和连接的坐标。这些数据需要用html配置,最好把它们写成对象对象,分配给我们自己的图表类函数。例如:

vardata={ element :[{ image : ' img/work . png ',pos3360 [1,1],//图片位置linePoint:[],//图片发射线段坐标数组lineDir:0,//线段标题的动画方向:' work' }],lineColor:'black ',//连接颜色animateColor: 'red ',//动画颜色};var chart=new myd3 chart(' # chart ');图表.折线图(数据);其中图片发送的线段坐标数组由外部文件提供,由后面介绍的编辑器生成。

在设计我们自己的图表功能时,最好将每个功能划分为独立的功能,方便以后的维护和扩展。

动画线段采用css,动画线段可以用这个css添加:动画线{ fill: none笔画宽度: 1;冲程-缓冲器: 50 100;stroke-dash offset : 0;animation:笔画6s无限线性;} @关键帧stroke { 100% { stroke-dash offset : 500;/*如果将反向移动改为-500 */}}这个图表的难点在于动态改变连接线上的流动动画,因为A线的端点会连接到B线,如果B线的动画停止,A线的动画仍然会经过B线,而不是简单地停止B线的动画.而且,如果B线段上有多个接入点,就要判断接入点之间的顺序,只显示最接近B起点的接入点的动画。此外,还需要判断接入线段上是否有接入线段。如果层次关系中的一条线段有动画,则接入点将有动画。(这里有点绕弯)

我的方法是:

1)统计每条线段上的所有访问点,这里是图片名称,用来判断是否有动画流出这条线段。

2)从后台接收数据时,判断每条线段是否有动画,如果有动画,直接还原其动画线段的起点坐标;如果没有动画,则判断最靠近起点的接入点是否有动画,如果有动画,则将动画线段的起点改为该接入点的坐标。

//统计接入点函数find accesspoint(){ var accesspoints=[];//记录每个线段上的访问点,其中数据为配置数据data.eles.foreach(函数(d,I){ if(d . line . length==0){ return;} varacsp={ name :d . title . text,ap: [],//接入点,按顺序排列,头部靠近起点};//该线段上的每两个相邻点作为数组var linePair=[]中的一个元素存储;//var startPos=d.line[0],此线段的起点;d.line.forEach(函数(dd,di){ if(d . line[di 1]){ var pair={ start : DD,end :d . line[di 1]};linePair.push(对);} });//对于每两个相邻的点,找到接入点linepair.foreach (function (DD,di){ chart data . eles . foreach(function(DDD,ddi){//排除自己,找到接入点if(i!=ddi ddd.line.length 1){ //获取此线段的终点var pos=DDD . line[DDD . line . length-1];//dd .起点,dd .终点//用x坐标计算该线段上的y坐标,并与实际y坐标进行比较。var computey=DD。开始[1](位置[0]-DD。start [0]) * (DD。end [1]-DD。start [1])/(。var dif=Math . ABS(Computey-pos[1]);//如果误差在2以内,并且该行的终点在当前行的起点和终点之间//则认为该点为访问点if (dif2 (((pos [0] DD。开始[0])(位置[0] DD。end[0])| |((pos[0]DD。start[0])(pos[0]DD)DD . start[1])(pos[1]DD . end[1]))| |((pos[1]DD . start[1])(pos[1]DD . end[1])))))){ var dis=math . pow((pos[0]-start pos[0]),2) Math.pow((pos[1] - startPos[1]),2);var AP={ name : DD . title . text,ap:pos,distance3360dis,//到起点的距离all names 3360[],//经过此接入点的所有站点的名称} acsp . AP . push(AP);} } });})accesspoints . push(acsp);});//根据与起点的距离对所有接入点进行排序,找到上层站点接入点。foreach(函数(d,I) {//sort d.ap.sort(函数(a,b){ return a . distance-b . distance;});//查找上层站点d.ap.foreach (function (DD,di) {findpoint (DD。名字,DD。所有名称);});});//name是接入点名称,arr是allnames函数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);//添加if (DD。allNames.length0) {DD。如果该点的allnames已经有值,则直接调用allnames.foreach(函数(d,I){ arr . push(d));});} else{ //递归搜索子接入点findPoint(dd.name,arr);} });} else { return} } else { return} });}}上述功能的运行结果会产生一个对象,该对象存储了每个访问线段上‘挂载’的访问点,便于在更改动画时进行判断。

//更新直线动画苯胺. each(函数(d,I){ var curve=D3 . select(this);//找到对应的动画线if (DD。name==curve . attr(' tag '){//处理动画是否运行if (dd.ani) {//此线条动画运行curve . style(' animation-play-state ',' running ');curLine.style('display ',' inline ');//如果动画运行,恢复原始动画路径curl.attr ('d ',函数(d){ return line(chart data . eles[I])。行);});} else {//这条线的动画停止//首先找到最接近这条线段起点的接入点var acp=accessPoints//从accessPoints中找到该节点的接入点集var AP=[];acp.forEach(function(acd,ACI){ if(ACD . name===DD . name){ AP=ACD . AP;} });//最近有动画接入点序列号var acidex=-1;//用动画找到最近的接入点,按数组序号递增为(var j=0;日本长度;J ){ //复制所有子接入点阵列var all name=AP[j]。all name . concat();//将接入点名称添加到allNames.push(ap[j])。姓名);//判断这个接入点树中是否有动画,如果有,可以allnames。foreach(函数(名称,ani) {data。foreach(函数(datad,datai) {if (datad。ani) {AC指数=j;返回;} } });});if(acIndex!=-1){ break;} }//如果有动画访问点if(acIndex!=-1){ curLine.style('动画-播放-状态','正在运行');curLine.style('display ',' inline ');curLine.attr('d ',function(d){ var accp=AP[ACinDEX]。AP;var curLine=data . element[I]. line . concat();//接入节点与起始点的距离vardisap=math . pow((accp[0]-曲线[0] [0]),2)math . pow((accp[1]-曲线[0] [1]),2);//如果当前线段中有一个节点比接入点更靠近起始节点//,则删除该节点Curline。foreach(函数(curl,curl){ If(curl 0){ vardis=math . pow((curl[0]-curl[0][0])),2) math.pow (() If(dis disAp){ //删除此点curl。拼接(卷曲,1);} } });//开始动画曲线。从此接入点分割(0,1,accp);//调试器;返回线(curLine);});}else{ //此行动画停止curl.style('动画-播放-状态','暂停');curLine.style('display ',' none ');}} }2.编者编辑

因为这个图表需要配置大量的坐标,手工填写效率不高,所以需要开发一个编辑器来修改图表。

编辑器的主要使用方法是用鼠标拖动图标,双击确定起始位置,开始实时画线状态,随着鼠标移动动态绘制线段,点击确定临时终点,再点击确定下一个终点,右键结束动态画线状态。如果鼠标单击另一个图标,终点是图标的起始坐标。本程序的实时画线部分受倾斜度的限制,即向左或向右30度。

编辑器比显示图简单,复杂的部分是事件处理。

//拖动图标vardraging=D3 . draging()。on ('drag ',function(){//当长度和宽度相同时,iconSize为图标大小[宽度,高度] varmove=iconsize [0]/2,movesubbg=[25,53.5],move title=[22]var g=D3 . select(this),eventX=d3.event.x - move,eventY=D3 . event . y-move;//设置图标位置g .选择('。图像')。attr ('x ',eventx)。attr ('y ',eventy);})//拖放结束。on ('end ',function () {var g=d3。选择(此);g .选择('。subbg’)。attr ('transform ',函数(d,I){//处理子标签,自动匹配字符串长度var x=parsefloat (d3.select (this))。attr(' x '))parse float(D3 . select(this)。attr(' width ')/2,//y没有缩放,所以y=d3.select (this)。attr('。var scaleX=dsl * 5.5返回“translate(' x ' ' y ') scale(' scaleX ',1)translate('-x ' '-y ')”;});});//将拖动事件imageGs.call(拖动)添加到图标组;上面的拖拽事件只是调用了基本方法。

实时画线功能需要预先定义一个临时存储对象,用于存储鼠标移动时线段端点的坐标。

//鼠标移动时,实时画线到鼠标当前位置,_bodyRect为主区域_bodyRect.on('mousemove ',function(){ //如果不处于实时画线状态if(!_图表数据。绘图){ return} //如果没有端点名称if(!_图表数据。LinePrepare。name){ return;} /* 实时画线*///判断线段倾斜方向,linePrePare为线段临时存储var preline=line prepare . line var鼠标位置=D3。鼠标(_体直。node()),在pos=preline[preline]之前。length-1],newy,new pos=[];if((位置[0]之前的鼠标位置[0])位置[1]之前的鼠标位置[1])| |(位置[0]之前的鼠标位置[0]位置[1]之前的鼠标位置[1]){//向左倾斜\左上到右下:y=cy 0.7 *(x-CX)新y=PoS[1]之前0.7 *(MousePoS[0]-PoS[0]之前);} else { //向右倾斜/左下到右上:y=cy-0.7 *(CX-x)新y=在位置[1]之前-0.7 *(鼠标位置[0]-在位置[0]之前);} newPos=[mousePos[0],Newy];//移除旧线if(_图表数据。临时工。线条){ _图表数据。临时工。pos=[];_图表数据。临时工。排队。移除();} //画新线,tempLine为实时画线的临时存储_图表数据。临时工。line=_ chart数据。线根。追加('路径').attr('class ','线路路径').attr('stroke ',chartData.line.color).attr('笔画宽度,chartData.line.width).attr('填充','无')。attr('d ',function(){ var NewLine=[preLines][preLines。length-1],NewPoS];_图表数据。临时工。pos=新pos;返回行(NewLine);});//当鼠标移入某个建筑图标范围时_图表数据。图像。打开('鼠标输入',函数(d,i){ //移除旧线if(_图表数据。临时工。线条){ _图表数据。临时工。pos=[];_图表数据。临时工。排队。移除();} //得到图标中心点坐标var POSx=ParseFloat(D3。选择(此选项).选择('。图像')。attr(' x ')_ ChartConf。baseSize[0]/2;var POSy=ParseFloat(D3。选择(此选项).选择('。图像')。attr(' y ')_ ChartConf。baseSize[1]/2;//将此建筑图标的中心点坐标作为终点坐标画线_图表数据。临时工。line=_ chart数据。线根。追加('路径').attr('class ','线路路径').attr('stroke ',chartData.line.color).attr('笔画宽度,chartData.line.width).attr('填充','无')。attr('d ',function(){ var newLine=[preLines][preLines。length-1],[posX,POSy]];_chartData.tempLine.pos=[posX,POSy];返回行(NewLine);});});//当鼠标移出图标区域_图表数据。图像。on('鼠标离开',函数(d,i){ //移除旧线if(_图表数据。临时工。线条){ _图表数据。临时工。pos=[];_图表数据。临时工。排队。移除();} });//对图标单击鼠标,保存线_chartData.imageGs.on('单击',函数(d,i) { //保存临时线画线();//停止实时画线退出绘图();});});//点击鼠标右键,停止实时画线_bodyRect.on('contextmenu ',function(){ //停止实时画线退出绘图();D3。事件。prevent default();});});}在此只贴出部分代码,如果大家有任何建议和问题,还请留言,谢谢。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对我们的支持。

版权声明:一个用d3.js制作连接动画图和编辑器的例子是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。