溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

怎么用HTML5 Canvas實現(xiàn)交互式地鐵線路圖

發(fā)布時間:2022-03-08 10:28:03 來源:億速云 閱讀:189 作者:小新 欄目:web開發(fā)

這篇文章主要介紹怎么用HTML5 Canvas實現(xiàn)交互式地鐵線路圖,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!

  界面生成

  底層的p是通過ht.graph.GraphView組件生成的,然后就可以利用HTforWeb提供好的方法,調(diào)用canvas畫筆隨便繪制就好,先來看看怎么生成底層p:

  vardm=newht.DataModel();//數(shù)據(jù)容器

  vargv=newht.graph.GraphView(dm);//拓撲組件

  gv.addToDOM();//將拓撲圖組件添加進body中

  addToDOM函數(shù)聲明如下:

  addToDOM=function(){

  varself=this,

  view=self.getView(),

  style=view.style;

  document.body.appendChild(view);//將組件底層p添加到body中

  style.left='0';//默認組件是絕對定位,所以要設置位置

  style.right='0';

  style.top='0';

  style.bottom='0';

  window.addEventListener('resize',function(){self.iv();},false);//窗口變化事件

  }

  現(xiàn)在我就可以在這個p上亂涂亂畫了~首先我獲取下載好的地鐵線路圖上的點,我將它們放在subway.js中,這個js文件全部都是下載的內(nèi)容,我沒有做其他的改動,主要是將這些點根據(jù)線路來分分配添加到數(shù)組中,比如:

  mark_Point13=[];//線路數(shù)組內(nèi)包含線路的起點和終點坐標以及這條線路的名稱

  t_Point13=[];//換成站點數(shù)組內(nèi)包含線路中的換乘站點坐標以及換成站點名稱

  n_Point13=[];//小站點數(shù)組內(nèi)包含線路中的小站點坐標以及小站點名稱

  mark_Point13.push({name:'十三號線',value:[113.4973,23.1095]});

  mark_Point13.push({name:'十三號線',value:[113.4155,23.1080]});

  t_Point13.push({name:'魚珠',value:[113.41548,23.10547]});

  n_Point13.push({name:'裕豐圍',value:[113.41548,23.10004]});

  接下來來描繪地鐵線路,我聲明了一個數(shù)組lineNum,用來裝js中所有的地鐵線路的編號,以及一個color數(shù)組,用來裝所有的地鐵線的顏色,這些顏色的index與lineNum中地鐵線編號的index是一一對應的:

  varlineNum=['1','2','3','30','4','5','6','7','8','9','13','14','32','18','21','22','60','68'];

  varcolor=['#f1cd44','#0060a1','#ed9b4f','#ed9b4f','#007e3a','#cb0447','#7a1a57',

  '#18472c','#008193','#83c39e','#8a8c29','#82352b','#82352b','#09a1e0','#8a8c29',

  '#82352b','#b6d300','#09a1e0'];

  接著遍歷lineNum,將lineNum中的元素和顏色傳到createLine函數(shù)中,根據(jù)這兩個參數(shù)來繪制地鐵線路以及配色,畢竟js文件中的命名方式也是有規(guī)律的,哪一條線路,則命名后面一定會加上對應的數(shù)字,所以我們只需要將字符串與這個編號結(jié)合即可獲得js中對應的數(shù)組了:

  letlineName='Line'+num;

  letline=window[lineName];

  createLine的定義也非常簡單,我的代碼設置了不少的樣式,所以看起來有點多。創(chuàng)建一個ht.Polyline管線,我們可以通過polyline.addPoint()函數(shù)向這個變量中添加具體的點,通過setSegments可以設置點的連接方式。

  functioncreateLine(num,color){//繪制地圖線

  varpolyline=newht.Polyline();//多邊形管線

  polyline.setTag(num);//設置節(jié)點tag標簽,作為唯一標示

  if(num==='68')polyline.setToolTip('APM');//設置提示信息

  elseif(num==='60')polyline.setToolTip('GF');

  elsepolyline.setToolTip('Line'+num);

  if(color){

  polyline.s({//s為setStyle的簡寫,設置樣式

  'shape.border.width':0.4,//設置多邊形的邊框?qū)挾?/p>

  'shape.border.color':color,//設置多邊形的邊框顏色

  'select.width':0.2,//設置選中節(jié)點的邊框?qū)挾?/p>

  'select.color':color//設置選中節(jié)點的邊框顏色

  });

  }

  letlineName='Line'+num;

  letline=window[lineName];

  for(leti=0;i<line.length;i++){

  for(letj=0;j<line[i].coords.length;j++){

  polyline.addPoint({x:line[i].coords[j][0]*300,y:-line[i].coords[j][1]*300});

  if(num==='68'){//APM線(有兩條,但是點是在同一個數(shù)組中的)

  if(i===0&&j===0){

  polyline.setSegments([1]);

  }

  elseif(i===1&&j===0){

  polyline.getSegments().push(1);

  }

  else{

  polyline.getSegments().push(2);

  }

  }

  }

  }

  polyline.setLayer('0');//將線設置在下層,點設置在上層“top”

  dm.add(polyline);//將管線添加進數(shù)據(jù)容器中儲存,不然這個管線屬于“游離”狀態(tài),是不會顯示在拓撲圖上的

  returnpolyline;

  }

  上面代碼中添加地鐵線上的點有分為幾種情況,是因為js中設置線的時候Line68有一個“跳躍”點的現(xiàn)象,所以我們必須“跳躍”過去,篇幅有限Line68數(shù)組具體的聲明自行看subway.js。

  這里說明一點,如果用的是addPoint函數(shù),不設置segments時,默認將添加進的點用直線連接,segments的定義如下:

  1:moveTo,占用1個點信息,代表一個新路徑的起點

  2:lineTo,占用1個點信息,代表從上次最后點連接到該點

  3:quadraticCurveTo,占用2個點信息,第一個點作為曲線控制點,第二個點作為曲線結(jié)束點

  4:bezierCurveTo,占用3個點信息,第一和第二個點作為曲線控制點,第三個點作為曲線結(jié)束點

  5:closePath,不占用點信息,代表本次路徑繪制結(jié)束,并閉合到路徑的起始點

  所以我們要做“跳躍”的行為設置segments為1即可。

  最后繪制這些地鐵線上的點,這個部分subway.js中也分離出來了,命名以“mark_Point”、“t_Point”以及“n_Point”開頭,我在前面js的展示部分有對這些數(shù)組進行解釋,大家動動中指劃上去看看。

  我們在這些點的位置添加ht.Node節(jié)點,當節(jié)點一添加進dm數(shù)據(jù)容器中時,就會在拓撲圖上顯示,當然,前提是這個拓撲圖組件gv設置的數(shù)據(jù)容器是這個dm。篇幅有限,添加地鐵線上的點的代碼部分我只展示添加“換乘站點”的點:

  vartName='t_Point'+num;

  vartP=window[tName];//大站點

  if(tP){//有些線路沒有“換乘站點”

  for(leti=0;i<tP.length;i++){

  letnode=createNode(tP[i].name,tP[i].value,color[index]);//在獲取的線路上的點的坐標位置添加節(jié)點

  node.s({//設置節(jié)點的樣式style

  'label.scale':0.05,//文本縮放,可以避免瀏覽器限制的最小字號問題

  'label.font':'bold12pxarial,sans-serif'//設置文本的font

  });

  node.setSize(0.6,0.6);//設置節(jié)點大小。由于js中每個點之間的偏移量太小,所以我不得不把節(jié)點設置小一些

  node.setImage('images/旋轉(zhuǎn)箭頭.json');//設置節(jié)點的圖片

  node.a('alarmColor1','rgb(150,150,150)');//attr屬性,可以在這里面設置任何的東西,alarmColor1是在上面設置的image的json中綁定的屬性,具體參看HTforWeb矢量手冊(http://www.hightopo.com/guide/guide/core/vector/ht-vector-guide.html#ref_binding)

  node.a('alarmColor2','rgb(150,150,150)');//同上

  node.a('tpNode',true);//這個屬性設置只是為了用來區(qū)分“換乘站點”和“小站點”的,后面會用上

  }

  }

  所有的地鐵線路以及站點都添加完畢。但是!你可能會看不見自己繪制的圖,因為他們太小了,這個時候可以設置graphView拓撲組件上的fitContent函數(shù),我們順便將拓撲圖上的所有東西不可移動也設置一下:

  gv.fitContent(false,0.00001);//自適應大小,參數(shù)1為是否動畫,參數(shù)2為gv與邊框的padding值

  gv.setMovableFunc(function(){

  returnfalse;//設置gv上的節(jié)點不可移動

  });

  這下你的地鐵線路圖就可以顯示啦~接下來看看交互。

  交互

  首先是鼠標移動事件,鼠標滑過具體線路時,線路會變粗,懸停一會兒還能看到這條線路的編號;當鼠標移動到“換乘站點”或“小站點”,站點對應的圖標都會變大并且變色,字體也會變大,鼠標移開圖標變回原來的顏色并且字體變小。不同點在于鼠標移動到“換乘站點”時,“換乘站點”會旋轉(zhuǎn)。

  鼠標滑動事件,我直接基于gv的底層p進行的mousemove事件,通過ht封裝的getDataAt函數(shù)傳入事件event參數(shù),獲取事件下對應的節(jié)點,然后就可以隨意操作節(jié)點了:

  gv.getView().addEventListener('mousemove',function(e){

  vardata=gv.getDataAt(e);//傳入邏輯坐標點或者交互event事件參數(shù),返回當前點下的圖元

  if(name){

  originNode(name);//不管什么時候都要讓節(jié)點保持原來的大小

  }

  if(datainstanceofht.Polyline){//判斷事件節(jié)點的類型

  dm.sm().ss(data);//選中“管道”

  name='';

  clearInterval(interval);

  }

  elseif(datainstanceofht.Node){

  if(data.getTag()!==name&&data.a('tpNode')){//若不是同一個節(jié)點,并且mousemove的事件對象為ht.Node類型,那么設置節(jié)點的旋轉(zhuǎn)

  interval=setInterval(function(){

  data.setRotation(data.getRotation()-Math.PI/16);//在自身旋轉(zhuǎn)的基礎上再旋轉(zhuǎn)

  },100);

  }

  if(data.a('npNode')){//如果鼠標移到“小站點”也要停止動畫

  clearInterval(interval);

  }

  expandNode(data,name);////自定義的放大節(jié)點函數(shù),比較容易,我不粘代碼了,可以去http://hightopo.com/查看

  dm.sm().ss(data);//設置選中節(jié)點

  name=data.getTag();//作為“上一個節(jié)點”的存儲變量,可以通過這個值來獲取節(jié)點

  }

  else{//其他任何情況則不選中任何內(nèi)容并且清除“換乘站點”上的動畫

  dm.sm().ss(null);

  name='';

  clearInterval(interval);

  }

  });

  鼠標懸停在地鐵線路上時顯示“具體線路信息”,我是通過設置tooltip來完成的(注意:要打開gv的tooltip開關(guān)):

  gv.enableToolTip();//打開tooltip的開關(guān)

  if(num==='68')polyline.setToolTip('APM');//設置提示信息

  elseif(num==='60')polyline.setToolTip('GF');

  elsepolyline.setToolTip('Line'+num);

  然后我利用右下角的form表單,單擊表單上的具體線路,或者雙擊拓撲圖上任意一個“站點”或者線路,則拓撲圖會自適應到對應的部分,將被雙擊的部分展現(xiàn)到拓撲圖的中央。

  form表單的聲明部分我好像還沒有解釋。。。就是通過new一個ht.widget.FomePane類創(chuàng)建一個form表單組件,通過form.getView()獲取表單組件的底層p,將這個p擺放在body右下角,然后通過addRow函數(shù)向form表單中添加一行的表單項,可以在這行中添加任意多個項,通過addRow函數(shù)的第二個參數(shù)(一個數(shù)組),對添加進的表單項進行寬度的設置,通過第三個參數(shù)設置這行的高度:

  functioncreateForm(){//創(chuàng)建右下角的form表單

  varform=newht.widget.FormPane();

  form.setWidth(200);//設置表單寬度

  form.setHeight(416);//設置表單高度

  letview=form.getView();

  document.body.appendChild(view);//將表單添加進body中

  view.style.zIndex=1000;

  view.style.bottom='10px';//ht組件幾乎都設置絕對路徑

  view.style.right='10px';

  view.style.background='rgba(211,211,211,0.8)';

  names.forEach(function(nameString){

  form.addRow([//向表單中添加行

  {//這一行中的第一個表單項

  button:{//向表單中添加button按鈕

  icon:'images/Line'+nameString.value+'.json',//設置按鈕的圖標

  background:'',//設置按鈕的背景

  borderColor:'',//設置按鈕的邊框顏色

  clickable:false//設置按鈕不可點擊

  }

  },

  {//第二個表單項

  button:{

  label:nameString.name,

  labelFont:'bold14pxarial,sans-serif',

  labelColor:'#fff',

  background:'',

  borderColor:'',

  onClicked:function(){//按鈕點擊回調(diào)事件

  gv.sm().ss(dm.getDataByTag(nameString.value));//設置選中按下的按鈕對應的線路

  gv.fitData(gv.sm().ld(),true,5);//將選中的地鐵線路顯示在拓撲圖的中央

  }

  }

  }

  ],[0.1,0.2],23);//第二個參數(shù)是設置第一參數(shù)中的數(shù)組的寬度,小于1是比例,大于1是實際寬度。第三個參數(shù)是該行的高度

  });

  }

  單擊“站點”顯示紅色標注,雙擊節(jié)點自適應放置到拓撲圖中央以及雙擊空白處將紅色標注隱藏的內(nèi)容都是通過對拓撲組件gv的事件監(jiān)聽來控制的,非常清晰易懂,代碼如下:

  varnode=createRedLight();//創(chuàng)建一個新的節(jié)點,顯示為“紅燈”的樣式

  gv.mi(function(e){//ht中拓撲組件中的事件監(jiān)聽

  if(e.kind==='clickData'&&(e.data.a('tpNode')||e.data.a('npNode'))){//e.kind獲取當前事件類型,e.data獲取當前事件下的節(jié)點

  node.s('2d.visible',true);//設置node節(jié)點可見

  node.setPosition(e.data.getPosition().x,e.data.getPosition().y);//設置node的坐標為當前事件下節(jié)點的位置

  }

  elseif(e.kind==='doubleClickData'){//雙擊節(jié)點

  gv.fitData(e.data,false,10);//將事件下的節(jié)點自適應到拓撲圖的中央,參數(shù)1為自適應的節(jié)點,參數(shù)2為是否動畫,參數(shù)3為gv與邊框的padding

  }

  elseif(e.kind==='doubleClickBackground'){//雙擊空白處

  node.s('2d.visible',false);//設置node節(jié)點不可見查看HTforWeb樣式手冊(http://www.hightopo.com/guide/guide/core/theme/ht-theme-guide.html#ref_style)

  }

  });

  注意s(style)和a(attr)定義是這樣的,s是ht預定義的一些樣式屬性,而a是我們用戶來自定義的屬性,一般是通過調(diào)用字符串來調(diào)用結(jié)果的,這個字符串對應的可以是常量也可以是函數(shù),還是很靈活的。

  最后還做了一個小小的部分,選中“站點”,則該“站點”的上方會顯示一個紅色的會“呼吸”的用來注明當前選中的“站點”。

  “呼吸”的部分是利用ht的setAnimation函數(shù)來完成的,在用這個函數(shù)之前要先打開數(shù)據(jù)容器的動畫開關(guān),然后設置動畫:

  dm.enableAnimation();//打開數(shù)據(jù)容器的動畫開關(guān)

  functioncreateRedLight(){

  varnode=newht.Node();

  node.setImage('images/紅燈.json');//設置節(jié)點的圖片

  node.setSize(1,1);//設置節(jié)點的大小

  node.setLayer('firstTop');//設置節(jié)點顯示在gv的最上層

  node.s('2d.visible',false);//節(jié)點不可見

  node.s('select.width',0);//節(jié)點選中時的邊框為0,不可見

  node.s('2d.selectable',false);//設置這個屬性,則節(jié)點不可選中

  node.setAnimation({//設置動畫具體參見HTforWeb動畫手冊(http://www.hightopo.com/guide/guide/plugin/animation/ht-animation-guide.html)

  expandWidth:{

  property:"width",//設置這個屬性,并且未設置accessType,則默認通過setWidth/getWidth來設置和獲取屬性。這里的width和下面的height都是通過前面設置的size得到的

  from:0.5,//動畫開始時的屬性值

  to:1,//動畫結(jié)束時的屬性值

  next:"collapseWidth"//字符串類型,指定當前動畫完成之后,要執(zhí)行的下個動畫,可將多個動畫融合

  },

  collapseWidth:{

  property:"width",

  from:1,

  to:0.5,

  next:"expandWidth"

  },

  expandHeight:{

  property:"height",

  from:0.5,

  to:1,

  next:"collapseHeight"

  },

  collapseHeight:{

  property:"height",

  from:1,

  to:0.5,

  next:"expandHeight"

  },

  start:["expandWidth","expandHeight"]//數(shù)組,用于指定要啟動的一個或多個動畫

  });

  dm.add(node);

  returnnode;

  }

以上是“怎么用HTML5 Canvas實現(xiàn)交互式地鐵線路圖”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對大家有幫助,更多相關(guān)知識,歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI