HTML5,不只是看上去很美(第四弹:可交互地铁线路图)

MarXEPE 7年前
   <h2><strong>前言</strong></h2>    <p>最近特别忙,承蒙大伙关照,3D机房的项目一个接着一个,领了一帮小弟,搞搞传帮带,乌飞兔走,转眼已经菊黄蟹肥……有个小弟很不错,勤奋好学,很快就把API都摸透了,国庆几天自己折腾着做了个HTML5的魔都的地铁线路图,能拖能拽的,还和电子地图做了交互。哥决定把小弟的成果纳入“ HTML5,不只是看上去很美 ”系列,以示鼓励(P.S. 其实还挺有压力的,后浪推前浪,新人赶旧人。我们这些老鸟也得注意,免得让00后给抢了饭碗)</p>    <h2><strong>效果图对比</strong></h2>    <p>网上的地铁图还是很多的,小弟选了这张比较新的做参考。想当年哥来魔都打拼时,图上可就一红一绿打个叉……暴露年龄的话不多说,看图:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/ce9220706dcb3366a74bb970bb246871.jpg"></p>    <p>再来看看小弟做的:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/879ce2c37c887619014808ed9531ee8d.jpg"></p>    <p>我是一眼看不出区别,但这可不是一张效果图,而是一个新手仅用了几天做出来的东西,而且里面许多美化和调整是通过程序自动完成的,这就不容易了。更重要的是,它并不是一张死图,而是纯矢量、可交互、有动态效果、无失真缩放的拓扑图!我们先简单看一下交互效果,后面可以详细说说代码的实现。</p>    <h2><strong>文本提示弹弹弹</strong></h2>    <p>首先,把鼠标移到站点、路段、图标等位置,都会有文本提示弹出,这个比较基本,百度家的就有,小弟也就放了比较简单的弹出内容。如果加上基本介绍啊、相关提示啊、周边信息啊……要是加上广告,就可以赚钱了……反正什么都可以加嘛,就是一个setToolTip命令而已。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/8b596d18e79cc1efe225e7356135e542.gif"></p>    <h2><strong>站点图标变变变</strong></h2>    <p>当鼠标移到站点上时,站点图标做了放大效果,这个效果很贴心,看了下百度家,用的是发光效果。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/429dce2e7bb4ec6f4c47c3b83582bb6f.gif"></p>    <p>实现的方法也很简便,就是在注册站点矢量图形时,加入了动态判断。以下注册普通站点矢量图形的代码:</p>    <pre>  <code class="language-javascript">twaver.Util.registerImage('station',{      w: linkWidth*1.6,      h: linkWidth*1.6,      v: function (data, view) {          var result = [];          if(data.getClient('focus')){              result.push({                  shape: 'circle',                  r: linkWidth*0.7,                  lineColor:  data.getClient('lineColor'),                  lineWidth: linkWidth*0.2,                  fill: 'white',              });              result.push({                  shape: 'circle',                  r: linkWidth*0.2,                  fill:  data.getClient('lineColor'),              });          }else{              result.push({                  shape: 'circle',                  r: linkWidth*0.6,                  lineColor: data.getClient('lineColor'),                  lineWidth: linkWidth*0.2,                  fill: 'white',              });          }          return result;      }  });</code></pre>    <h2><strong>动画效果拽拽拽</strong></h2>    <p>从上图还可以看到,在换乘站图标中,除了增加了颜色,还实现了旋转效果。这个就秒杀百度家了。</p>    <p>来看代码:</p>    <pre>  <code class="language-javascript">1.    twaver.Util.registerImage('rotateArrow', {  2.        w: 124,  3.        h: 124,  4.        v: [{  5.            shape: 'vector',  6.            name: 'doubleArrow',  7.            rotate: 360,  8.            animate: [{  9.                attr: 'rotate',  10.                to: 0,  11.                dur: 2000,  12.                reverse: false,  13.                repeat: Number.POSITIVE_INFINITY  14.            }]  15.        }]  16.    });</code></pre>    <p>当然这对于TWaver来说也很容易,只不过对rotate属性进行了动态改变而已。</p>    <p>另外,在单击和双击站点时,还实现了selected和loading的动画效果,值得点赞!</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/fd7805c263cc684c851ca7b3cd6c840d.gif"> <img src="https://simg.open-open.com/show/5ae5abd708fd6691b3fed21ad16f7487.gif"></p>    <h2>混<strong>合缩放炫炫炫</strong></h2>    <p>无失真缩放是矢量图的先天优势,小弟也掌握得炉火纯青,把TWaver的混合缩放模式用到极致,还有缩放比例控制、文字自动隐藏等小功能,方便订制。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/009a7b8fd7a3401a8a31b889bbc45104.gif"></p>    <p>代码也不复杂:</p>    <pre>  <code class="language-javascript">1.    network.setZoomManager(new twaver.vector.MixedZoomManager(network));  2.    network.setMinZoom(0.2);  3.    network.setMaxZoom(3);  4.    network.setZoomVisibilityThresholds({  5.        label : 0.6,  6.    });</code></pre>    <h2><strong>交互功能用起来</strong></h2>    <p>小弟很自豪地给我介绍这个功能:图标可以自由拖动,松开后会自动弹回。哥问小弟这有什么用,他一本正经地说:证明图是活的!</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/d3768f89af211b332b06c114f367840e.gif"></p>    <p>好吧你赢了,虽然是个没什么卵用的功能,但闲的蛋疼的时候可以随便玩上几十分钟我也是信的。</p>    <h2><strong>连续单击同一站点</strong></h2>    <p>连续单击同一站点(注意不是双击),可以将经过此站点的所有线路突出显示出来。小弟说加入这个功能纯粹因为简单易做,我……竟然表示非常理解,谁年轻时没耍过这类轻松又讨好的小招数呢?</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/b76f6f0eb0203c81d5e64e7c044aa11b.gif"></p>    <h2><strong>双击站点</strong></h2>    <p>双击站点,竟然弹出了本站周边的电子地图!知道引入他山之玉,看来小子可教啊。我发现他的定位方法,有的是用经纬度,有的是关键词查询。小弟狡黠地说,开始是人工查每个站点经纬度的,干了一段儿发现太麻烦,后来改路子了。马大大说的,懒人改变世界,我服!</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/6571f037ead54e03cd018d1af17a3dc5.gif"></p>    <p>最后来八一八程序设计的思路吧,小弟是棵好苗子,能做出那么像样的程序,必然是深思熟虑过的。不想再听我啰嗦的朋友,也可以直接发邮件给我, tw-service@servasoft.com ,来鉴赏下小弟的成果。</p>    <h2><strong>数据文件的整理</strong></h2>    <p>数据格式,选择了JavaScript原生支持的json文件,直观方便。</p>    <p>数据结构,按照站点、线路、杂项三大块来组织,结构清晰,利于遍历、查询等操作。</p>    <pre>  <code class="language-javascript">1.    {  2.        "stations":{  3.            "l01s01":{ },  4.            …………  5.        }  6.        "lines":{  7.            "l01":{……},  8.            …………  9.        }  10.        "sundrys":{  11.            "railwaystationshanghai":{……},  12.            …………  13.        }  14.    }</code></pre>    <p>命名比较规范,通过名字就可以看出基本信息(例如“l01s01”就是1号线第1个站点),甚至直接利用名字就可以进行查询和遍历。</p>    <pre>  <code class="language-javascript">1.    "l01s01":{  2.        "id":"l01s01",  3.        "name":"莘庄",  4.        "loc":{"x":419,"y":1330},  5.        "label":"bottomright.bottomright",  6.    },  7.    …………</code></pre>    <h2><strong>站点路线的创建</strong></h2>    <p>首先是读取json文件的数据。</p>    <pre>  <code class="language-javascript">1.    function loadJSON(path,callback){  2.        var xhr = new XMLHttpRequest();  3.        xhr.onreadystatechange = function(){  4.            if (xhr.readyState === 4) {  5.                if (xhr.status === 200) {  6.                   dataJson = JSON.parse(xhr.responseText);  7.                   callback && callback();  8.               }  9.           }  10.       };  11.       xhr.open("GET", path, true);  12.       xhr.send();  13.    }</code></pre>    <p>因为读取文件是一个异步的过程,所以要程序的展开都要放在文件读取函数的内部。</p>    <pre>  <code class="language-javascript">1.    function init(){  2.        loadJSON("shanghaiMetro.json", function(){  3.            initNetwork(dataJson);  4.            initNode(dataJson);  5.        });  6.    }</code></pre>    <p>只要通过对站点进行一次遍历,车站的建立就完成了。</p>    <pre>  <code class="language-javascript">1.    for(staId in json.stations){  2.        var station = json.stations[staId];  3.        staNode = new twaver.Node({  4.            id: staId,  5.            name: station.name,  6.            image:'station',  7.        });  8.        staNode.s('label.color','rgba(99,99,99,1)');  9.        staNode.s('label.font','12px 微软雅黑');  10.        staNode.s('label.position',station.label);  11.        staNode.setClient('location',station.loc);  12.        box.add(staNode);  13.    }</code></pre>    <p>再对数据文件中的各条线路下的所有站点进行遍历,在站点间依次创建Link。</p>    <pre>  <code class="language-javascript">1.    for(lineId in json.lines) {  2.        ……  3.        for(staSn in line.stations) {  4.            ……  5.            var link = new twaver.Link(linkId,prevSta,staNode);  6.            link.s('link.color', line.color);  7.            link.s('link.width', linkWidth);  8.            link.setToolTip(line.name);  9.            box.add(link);  10.        }  11.    }</code></pre>    <p>再对label位置进行调整,否则站点名称会显示的很乱。小弟是通过在原始数据中手动加入位置信息来实现的,稍显笨了一点,应该可以通过程序自动判断站点周围空间来进行智能调整。</p>    <p>最后再加入图标,一张原始的地铁图就呈现出来了。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/cbd0212c61cb72b239c9c6c135eaf485.png"></p>    <h2><strong>路线拐点的添加</strong></h2>    <p>基本的示意功能已经具备了,这里,小弟让我很欣赏的一点是没有就此停止,而是进一步做了调整,使线路只保留了横平竖直和正斜的走向,以达到整齐美观的效果。可能看起来与参考图稍稍有些不同,主要因为各路段基本只添加了一个拐点,这样做既大大简化了程序,又基本保证了图形的美观度。想远一点,做多一点,是块做产品的好料子。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/03f695bc14abf4b5a3f89c15fa669609.png"></p>    <p>当然为了提高程序的灵活性,应对必须添加两个或以上拐点的情况,也使用了人工拐点的手段。不过这里人工拐点被设成一个隐形的节点,可能利于智能拐点的判断,但也有可能在路线操作时造成混乱。如何处理更好还可以进一步推敲。</p>    <pre>  <code class="language-javascript">var createTurnSta = function(line, staSn){      staTurn = new twaver.Node(staSn);      staTurn.setImage();      staTurn.setClient('lineColor',line.color);      staTurn.setClient('lines',[line.id]);      var loc = line.stations[staSn];      staTurn.setClient('location',loc);      box.add(staTurn);      return staTurn;  }</code></pre>    <h2><strong>接点位置的调整</strong></h2>    <p>大家可以看到,并不是所有路段都直接连入站点中心,在许多情况下必须要进行偏移。</p>    <pre>  <code class="language-javascript">var createFollowSta = function(json, line, staNode, staId){      staFollow = new twaver.Follower(staId);      staFollow.setImage();      staFollow.setClient('lineColor',line.color);      staFollow.setClient('lines',[line.id]);      staFollow.setHost(staNode);      var az = azimuth[staId.substr(6,2)];      var loc0 = json.stations[staId.substr(0,6)].loc;      var loc = {x:loc0.x+az.x, y:loc0.y+az.y};      staFollow.setClient('location',loc);      box.add(staFollow);      return staFollow;  }</code></pre>    <p style="text-align:center"><img src="https://simg.open-open.com/show/4837d467faa10200b7c69d050024b445.png"></p>    <p>小弟采取了虚拟节点的办法,就是在站点的旁边,添加一个Follower(但并不显示出来),让并行的不同线路连接到不同的Follower上。通过调整Follower的位置,来实现线路与站点连接点的控制。</p>    <pre>  <code class="language-javascript">var azimuth = {      bb: {x: 0, y: linkWidth*zoom/2},      tt: {x: 0, y: -linkWidth*zoom/2},      rr: {x: linkWidth*zoom/2, y: 0},      ll: {x: -linkWidth/2, y: 0},      br: {x: linkWidth*zoom*0.7/2, y: linkWidth*zoom*0.7/2},      bl: {x: -linkWidth*zoom*0.7/2, y: linkWidth*zoom*0.7/2},      tr: {x: linkWidth*zoom*0.7/2, y: -linkWidth*zoom*0.7/2},      tl: {x: -linkWidth*zoom*0.7/2, y: -linkWidth*zoom*0.7/2},      BB: {x: 0, y: linkWidth*zoom},      TT: {x: 0, y: -linkWidth*zoom},      RR: {x: linkWidth*zoom, y: 0},      LL: {x: -linkWidth, y: 0},      BR: {x: linkWidth*zoom*0.7, y: linkWidth*zoom*0.7},      BL: {x: -linkWidth*zoom*0.7, y: linkWidth*zoom*0.7},      TR: {x: linkWidth*zoom*0.7, y: -linkWidth*zoom*0.7},      TL: {x: -linkWidth*zoom*0.7, y: -linkWidth*zoom*0.7}  };</code></pre>    <p>介绍到这里就结束了,虽然是个小例子,实在是但美观性和实用性都还过得去,小弟花了心思去做,其实稍加改造就可以做出高铁图、公交图、运行图等应用。设想一下,如果能用在轨道交通列控中心大屏监控里,是多么炫酷。说到这,又想起了前两天云栖大会上刚看到的杭州城市数据大脑,不知何时,哥也能参与一把那样的项目呢?可视化,哥的强项……</p>    <p> </p>    <p> </p>    <p>来自:https://segmentfault.com/a/1190000007186026</p>    <p> </p>