這篇文章主要介紹了HTML5 Canvas的交互式地鐵線路圖怎么弄,具有一定借鑒價(jià)值,需要的朋友可以參考下。希望大家閱讀完這篇文章后大有收獲。下面讓小編帶著大家一起了解一下。
創(chuàng)新互聯(lián)是一家專業(yè)從事網(wǎng)站建設(shè)、成都網(wǎng)站建設(shè)、網(wǎng)頁設(shè)計(jì)的品牌網(wǎng)絡(luò)公司。如今是成都地區(qū)具影響力的網(wǎng)站設(shè)計(jì)公司,作為專業(yè)的成都網(wǎng)站建設(shè)公司,創(chuàng)新互聯(lián)依托強(qiáng)大的技術(shù)實(shí)力、以及多年的網(wǎng)站運(yùn)營經(jīng)驗(yàn),為您提供專業(yè)的成都網(wǎng)站建設(shè)、營銷型網(wǎng)站建設(shè)及網(wǎng)站設(shè)計(jì)開發(fā)服務(wù)!
底層的 p 是通過 ht.graph.GraphView 組件生成的,然后就可以利用 HT for Web 提供好的方法,調(diào)用 canvas 畫筆隨便繪制就好,先來看看怎么生成底層 p:
var dm = new ht.DataModel();//數(shù)據(jù)容器 var gv = new ht.graph.GraphView(dm);//拓?fù)浣M件 gv.addToDOM();//將拓?fù)鋱D組件添加進(jìn)body中
addToDOM 函數(shù)聲明如下:
addToDOM = function(){ var self = this, view = self.getView(), style = view.style; document.body.appendChild(view); //將組件底層p添加到body中 style.left = '0';//默認(rèn)組件是絕對定位,所以要設(shè)置位置 style.right = '0'; style.top = '0'; style.bottom = '0'; window.addEventListener('resize', function () { self.iv(); }, false); //窗口變化事件 }
現(xiàn)在我就可以在這個(gè) p 上亂涂亂畫了~首先我獲取下載好的地鐵線路圖上的點(diǎn),我將它們放在 subway.js 中,這個(gè) js 文件全部都是下載的內(nèi)容,我沒有做其他的改動,主要是將這些點(diǎn)根據(jù)線路來分分配添加到數(shù)組中,比如:
mark_Point13 = [];//線路 數(shù)組內(nèi)包含線路的起點(diǎn)和終點(diǎn)坐標(biāo)以及這條線路的名稱 t_Point13 = [];//換成站點(diǎn) 數(shù)組內(nèi)包含線路中的換乘站點(diǎn)坐標(biāo)以及換成站點(diǎn)名稱 n_Point13 = [];//小站點(diǎn) 數(shù)組內(nèi)包含線路中的小站點(diǎn)坐標(biāo)以及小站點(diǎn)名稱 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]});
接下來來描繪地鐵線路,我聲明了一個(gè)數(shù)組 lineNum,用來裝 js 中所有的地鐵線路的編號,以及一個(gè) color 數(shù)組,用來裝所有的地鐵線的顏色,這些顏色的 index 與 lineNum 中地鐵線編號的 index 是一一對應(yīng)的:
var lineNum = ['1', '2', '3', '30', '4', '5', '6', '7', '8', '9', '13', '14', '32', '18', '21', '22', '60', '68']; var color = ['#f1cd44', '#0060a1', '#ed9b4f', '#ed9b4f', '#007e3a', '#cb0447', '#7a1a57', '#18472c', '#008193', '#83c39e', '#8a8c29', '#82352b', '#82352b', '#09a1e0', '#8a8c29', '#82352b', '#b6d300', '#09a1e0'];
接著遍歷 lineNum,將 lineNum 中的元素和顏色傳到 createLine 函數(shù)中,根據(jù)這兩個(gè)參數(shù)來繪制地鐵線路以及配色,畢竟 js 文件中的命名方式也是有規(guī)律的,哪一條線路,則命名后面一定會加上對應(yīng)的數(shù)字,所以我們只需要將字符串與這個(gè)編號結(jié)合即可獲得 js 中對應(yīng)的數(shù)組了:
let lineName = 'Line' + num; let line = window[lineName];
createLine 的定義也非常簡單,我的代碼設(shè)置了不少的樣式,所以看起來有點(diǎn)多。創(chuàng)建一個(gè) ht.Polyline 管線,我們可以通過 polyline.addPoint() 函數(shù)向這個(gè)變量中添加具體的點(diǎn),通過 setSegments 可以設(shè)置點(diǎn)的連接方式。
function createLine(num, color) {//繪制地圖線 var polyline = new ht.Polyline();//多邊形 管線 polyline.setTag(num);//設(shè)置節(jié)點(diǎn)tag標(biāo)簽,作為唯一標(biāo)示 if(num === '68') polyline.setToolTip('A P M');//設(shè)置提示信息 else if(num === '60') polyline.setToolTip('G F'); else polyline.setToolTip('Line' + num); if(color) { polyline.s({//s 為 setStyle 的簡寫,設(shè)置樣式 'shape.border.width': 0.4,//設(shè)置多邊形的邊框?qū)挾? 'shape.border.color': color,//設(shè)置多邊形的邊框顏色 'select.width': 0.2,//設(shè)置選中節(jié)點(diǎn)的邊框?qū)挾? 'select.color': color//設(shè)置選中節(jié)點(diǎn)的邊框顏色 }); } let lineName = 'Line' + num; let line = window[lineName]; for(let i = 0; i < line.length; i++) { for(let j = 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線(有兩條,但是點(diǎn)是在同一個(gè)數(shù)組中的) if(i === 0 && j === 0) { polyline.setSegments([1]); } else if(i === 1 && j === 0) { polyline.getSegments().push(1); } else { polyline.getSegments().push(2); } } } } polyline.setLayer('0');//將線設(shè)置在下層,點(diǎn)設(shè)置在上層“top” dm.add(polyline);//將管線添加進(jìn)數(shù)據(jù)容器中儲存,不然這個(gè)管線屬于“游離”狀態(tài),是不會顯示在拓?fù)鋱D上的 return polyline; }
上面代碼中添加地鐵線上的點(diǎn)有分為幾種情況,是因?yàn)?js 中設(shè)置線的時(shí)候 Line68 有一個(gè)“跳躍”點(diǎn)的現(xiàn)象,所以我們必須“跳躍”過去,篇幅有限 Line68 數(shù)組具體的聲明自行看 subway.js。
這里說明一點(diǎn),如果用的是 addPoint 函數(shù),不設(shè)置 segments 時(shí),默認(rèn)將添加進(jìn)的點(diǎn)用直線連接,segments 的定義如下:
1: moveTo,占用 1 個(gè)點(diǎn)信息,代表一個(gè)新路徑的起點(diǎn)
2: lineTo,占用 1 個(gè)點(diǎn)信息,代表從上次最后點(diǎn)連接到該點(diǎn)
3: quadraticCurveTo,占用 2 個(gè)點(diǎn)信息,第一個(gè)點(diǎn)作為曲線控制點(diǎn),第二個(gè)點(diǎn)作為曲線結(jié)束點(diǎn)
4: bezierCurveTo,占用 3 個(gè)點(diǎn)信息,第一和第二個(gè)點(diǎn)作為曲線控制點(diǎn),第三個(gè)點(diǎn)作為曲線結(jié)束點(diǎn)
5: closePath,不占用點(diǎn)信息,代表本次路徑繪制結(jié)束,并閉合到路徑的起始點(diǎn)
所以我們要做“跳躍”的行為設(shè)置 segments 為 1 即可。
最后繪制這些地鐵線上的點(diǎn),這個(gè)部分 subway.js 中也分離出來了,命名以“mark_Point”、“t_Point”以及“n_Point”開頭,我在前面 js 的展示部分有對這些數(shù)組進(jìn)行解釋,大家動動中指劃上去看看。
我們在這些點(diǎn)的位置添加 ht.Node 節(jié)點(diǎn),當(dāng)節(jié)點(diǎn)一添加進(jìn) dm 數(shù)據(jù)容器中時(shí),就會在拓?fù)鋱D上顯示,當(dāng)然,前提是這個(gè)拓?fù)鋱D組件 gv 設(shè)置的數(shù)據(jù)容器是這個(gè) dm。篇幅有限,添加地鐵線上的點(diǎn)的代碼部分我只展示添加“換乘站點(diǎn)”的點(diǎn):
var tName = 't_Point' + num; var tP = window[tName];//大站點(diǎn) if(tP) {//有些線路沒有“換乘站點(diǎn)” for(let i = 0; i < tP.length; i++) { let node = createNode(tP[i].name, tP[i].value, color[index]);//在獲取的線路上的點(diǎn)的坐標(biāo)位置添加節(jié)點(diǎn) node.s({//設(shè)置節(jié)點(diǎn)的樣式style 'label.scale': 0.05,//文本縮放,可以避免瀏覽器限制的最小字號問題 'label.font': 'bold 12px arial, sans-serif'//設(shè)置文本的font }); node.setSize(0.6, 0.6);//設(shè)置節(jié)點(diǎn)大小。由于js中每個(gè)點(diǎn)之間的偏移量太小,所以我不得不把節(jié)點(diǎn)設(shè)置小一些 node.setImage('images/旋轉(zhuǎn)箭頭.json');//設(shè)置節(jié)點(diǎn)的圖片 node.a('alarmColor1', 'rgb(150, 150, 150)');//attr屬性,可以在這里面設(shè)置任何的東西,alarmColor1是在上面設(shè)置的image的json中綁定的屬性,具體參看 HT for Web 矢量手冊(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);//這個(gè)屬性設(shè)置只是為了用來區(qū)分“換乘站點(diǎn)”和“小站點(diǎn)”的,后面會用上 } }
所有的地鐵線路以及站點(diǎn)都添加完畢。但是!你可能會看不見自己繪制的圖,因?yàn)樗麄兲×?,這個(gè)時(shí)候可以設(shè)置 graphView 拓?fù)浣M件上的 fitContent 函數(shù),我們順便將拓?fù)鋱D上的所有東西不可移動也設(shè)置一下:
gv.fitContent(false, 0.00001);//自適應(yīng)大小,參數(shù)1為是否動畫,參數(shù)2為gv與邊框的padding值 gv.setMovableFunc(function(){ return false;//設(shè)置gv上的節(jié)點(diǎn)不可移動 });
這下你的地鐵線路圖就可以顯示啦~接下來看看交互。
首先是鼠標(biāo)移動事件,鼠標(biāo)滑過具體線路時(shí),線路會變粗,懸停一會兒還能看到這條線路的編號;當(dāng)鼠標(biāo)移動到“換乘站點(diǎn)”或“小站點(diǎn)”,站點(diǎn)對應(yīng)的圖標(biāo)都會變大并且變色,字體也會變大,鼠標(biāo)移開圖標(biāo)變回原來的顏色并且字體變小。不同點(diǎn)在于鼠標(biāo)移動到“換乘站點(diǎn)”時(shí),“換乘站點(diǎn)”會旋轉(zhuǎn)。
鼠標(biāo)滑動事件,我直接基于 gv 的底層 p 進(jìn)行的 mousemove 事件,通過 ht 封裝的 getDataAt 函數(shù)傳入事件 event 參數(shù),獲取事件下對應(yīng)的節(jié)點(diǎn),然后就可以隨意操作節(jié)點(diǎn)了:
gv.getView().addEventListener('mousemove', function(e) { var data = gv.getDataAt(e);//傳入邏輯坐標(biāo)點(diǎn)或者交互event事件參數(shù),返回當(dāng)前點(diǎn)下的圖元 if(name) { originNode(name);//不管什么時(shí)候都要讓節(jié)點(diǎn)保持原來的大小 } if (data instanceof ht.Polyline) {//判斷事件節(jié)點(diǎn)的類型 dm.sm().ss(data);//選中“管道” name = ''; clearInterval(interval); } else if (data instanceof ht.Node) { if(data.getTag() !== name && data.a('tpNode')) {//若不是同一個(gè)節(jié)點(diǎn),并且mousemove的事件對象為ht.Node類型,那么設(shè)置節(jié)點(diǎn)的旋轉(zhuǎn) interval = setInterval(function() { data.setRotation(data.getRotation() - Math.PI/16); //在自身旋轉(zhuǎn)的基礎(chǔ)上再旋轉(zhuǎn) }, 100); } if(data.a('npNode')) {//如果鼠標(biāo)移到“小站點(diǎn)”也要停止動畫 clearInterval(interval); } expandNode(data, name);////自定義的放大節(jié)點(diǎn)函數(shù),比較容易,我不粘代碼了,可以去http://hightopo.com/ 查看 dm.sm().ss(data);//設(shè)置選中節(jié)點(diǎn) name = data.getTag();//作為“上一個(gè)節(jié)點(diǎn)”的存儲變量,可以通過這個(gè)值來獲取節(jié)點(diǎn) } else {//其他任何情況則不選中任何內(nèi)容并且清除“換乘站點(diǎn)”上的動畫 dm.sm().ss(null); name = ''; clearInterval(interval); } });
鼠標(biāo)懸停在地鐵線路上時(shí)顯示“具體線路信息”,我是通過設(shè)置 tooltip 來完成的(注意:要打開 gv 的 tooltip 開關(guān)):
gv.enableToolTip();//打開 tooltip 的開關(guān) if(num === '68') polyline.setToolTip('A P M');//設(shè)置提示信息 else if(num === '60') polyline.setToolTip('G F'); else polyline.setToolTip('Line' + num);
然后我利用右下角的 form 表單,單擊表單上的具體線路,或者雙擊拓?fù)鋱D上任意一個(gè)“站點(diǎn)”或者線路,則拓?fù)鋱D會自適應(yīng)到對應(yīng)的部分,將被雙擊的部分展現(xiàn)到拓?fù)鋱D的中央。
form 表單的聲明部分我好像還沒有解釋。。。就是通過 new 一個(gè) ht.widget.FomePane 類創(chuàng)建一個(gè) form 表單組件,通過 form.getView() 獲取表單組件的底層 p,將這個(gè) p 擺放在 body 右下角,然后通過 addRow 函數(shù)向 form 表單中添加一行的表單項(xiàng),可以在這行中添加任意多個(gè)項(xiàng),通過 addRow 函數(shù)的第二個(gè)參數(shù)(一個(gè)數(shù)組),對添加進(jìn)的表單項(xiàng)進(jìn)行寬度的設(shè)置,通過第三個(gè)參數(shù)設(shè)置這行的高度:
function createForm() {//創(chuàng)建右下角的form表單 var form = new ht.widget.FormPane(); form.setWidth(200);//設(shè)置表單寬度 form.setHeight(416);//設(shè)置表單高度 let view = form.getView(); document.body.appendChild(view);//將表單添加進(jìn)body中 view.style.zIndex = 1000; view.style.bottom = '10px';//ht組件幾乎都設(shè)置絕對路徑 view.style.right = '10px'; view.style.background = 'rgba(211, 211, 211, 0.8)'; names.forEach(function(nameString) { form.addRow([//向表單中添加行 {//這一行中的第一個(gè)表單項(xiàng) button: {//向表單中添加button按鈕 icon: 'images/Line'+nameString.value+'.json',//設(shè)置按鈕的圖標(biāo) background: '',//設(shè)置按鈕的背景 borderColor: '',//設(shè)置按鈕的邊框顏色 clickable: false//設(shè)置按鈕不可點(diǎn)擊 } }, {//第二個(gè)表單項(xiàng) button: { label: nameString.name, labelFont: 'bold 14px arial, sans-serif', labelColor: '#fff', background: '', borderColor: '', onClicked: function() {//按鈕點(diǎn)擊回調(diào)事件 gv.sm().ss(dm.getDataByTag(nameString.value));//設(shè)置選中按下的按鈕對應(yīng)的線路 gv.fitData(gv.sm().ld(), true, 5);//將選中的地鐵線路顯示在拓?fù)鋱D的中央 } } } ], [0.1, 0.2], 23);//第二個(gè)參數(shù)是設(shè)置第一參數(shù)中的數(shù)組的寬度,小于1是比例,大于1是實(shí)際寬度。第三個(gè)參數(shù)是該行的高度 }); }
單擊“站點(diǎn)”顯示紅色標(biāo)注,雙擊節(jié)點(diǎn)自適應(yīng)放置到拓?fù)鋱D中央以及雙擊空白處將紅色標(biāo)注隱藏的內(nèi)容都是通過對拓?fù)浣M件 gv 的事件監(jiān)聽來控制的,非常清晰易懂,代碼如下:
var node = createRedLight();//創(chuàng)建一個(gè)新的節(jié)點(diǎn),顯示為“紅燈”的樣式 gv.mi(function(e) {//ht 中拓?fù)浣M件中的事件監(jiān)聽 if(e.kind === 'clickData' && (e.data.a('tpNode') || e.data.a('npNode'))) {//e.kind獲取當(dāng)前事件類型,e.data獲取當(dāng)前事件下的節(jié)點(diǎn) node.s('2d.visible', true);//設(shè)置node節(jié)點(diǎn)可見 node.setPosition(e.data.getPosition().x, e.data.getPosition().y);//設(shè)置node的坐標(biāo)為當(dāng)前事件下節(jié)點(diǎn)的位置 } else if(e.kind === 'doubleClickData') {//雙擊節(jié)點(diǎn) gv.fitData(e.data, false, 10);//將事件下的節(jié)點(diǎn)自適應(yīng)到拓?fù)鋱D的中央,參數(shù)1為自適應(yīng)的節(jié)點(diǎn),參數(shù)2為是否動畫,參數(shù)3為gv與邊框的padding } else if(e.kind === 'doubleClickBackground') {//雙擊空白處 node.s('2d.visible', false);//設(shè)置node節(jié)點(diǎn)不可見 查看 HT for Web 樣式手冊(http://www.hightopo.com/guide/guide/core/theme/ht-theme-guide.html#ref_style) } });
注意 s(style) 和 a(attr) 定義是這樣的,s 是 ht 預(yù)定義的一些樣式屬性,而 a 是我們用戶來自定義的屬性,一般是通過調(diào)用字符串來調(diào)用結(jié)果的,這個(gè)字符串對應(yīng)的可以是常量也可以是函數(shù),還是很靈活的。
最后還做了一個(gè)小小的部分,選中“站點(diǎn)”,則該“站點(diǎn)”的上方會顯示一個(gè)紅色的會“呼吸”的用來注明當(dāng)前選中的“站點(diǎn)”。
“呼吸”的部分是利用 ht 的 setAnimation 函數(shù)來完成的,在用這個(gè)函數(shù)之前要先打開數(shù)據(jù)容器的動畫開關(guān),然后設(shè)置動畫:
dm.enableAnimation();//打開數(shù)據(jù)容器的動畫開關(guān) function createRedLight() { var node = new ht.Node(); node.setImage('images/紅燈.json');//設(shè)置節(jié)點(diǎn)的圖片 node.setSize(1, 1);//設(shè)置節(jié)點(diǎn)的大小 node.setLayer('firstTop');//設(shè)置節(jié)點(diǎn)顯示在gv的最上層 node.s('2d.visible', false);//節(jié)點(diǎn)不可見 node.s('select.width', 0);//節(jié)點(diǎn)選中時(shí)的邊框?yàn)?,不可見 node.s('2d.selectable', false);//設(shè)置這個(gè)屬性,則節(jié)點(diǎn)不可選中 node.setAnimation({//設(shè)置動畫 具體參見 HT for Web 動畫手冊(http://www.hightopo.com/guide/guide/plugin/animation/ht-animation-guide.html) expandWidth: { property: "width",//設(shè)置這個(gè)屬性,并且未設(shè)置 accessType,則默認(rèn)通過 setWidth/getWidth 來設(shè)置和獲取屬性。這里的 width 和下面的 height 都是通過前面設(shè)置的 size 得到的 from: 0.5, //動畫開始時(shí)的屬性值 to: 1,//動畫結(jié)束時(shí)的屬性值 next: "collapseWidth"http://字符串類型,指定當(dāng)前動畫完成之后,要執(zhí)行的下個(gè)動畫,可將多個(gè)動畫融合 }, 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ù)組,用于指定要啟動的一個(gè)或多個(gè)動畫 }); dm.add(node); return node; }
全部代碼結(jié)束!
這個(gè) Demo 花了我兩天時(shí)間完成,總覺得有點(diǎn)不甘心啊,但是有時(shí)候思維又轉(zhuǎn)不過彎來,花費(fèi)了不少的時(shí)間,但是總的來說收獲還是很多的,我以前一直以為只要通過 getPoints().push 來向多邊形中添加點(diǎn)就可以了,求助了大神之后,發(fā)現(xiàn)原來這個(gè)方法不僅繞彎路而且還會出現(xiàn)各種各樣的問題,比如 getPoints 之前,一定要在多邊形中已經(jīng)有 points 才可以,但是在很多情況下,初始化的 points 并不好設(shè)置,而且會造成代碼很繁瑣,直接通過 addPoint 方法,直接將點(diǎn)添加進(jìn)多邊形變量中,并且還會默認(rèn)將點(diǎn)通過直線的方式連接,也不用設(shè)置 segments,多可愛的一個(gè)函數(shù)。
還有就是因?yàn)?ht 默認(rèn)縮放大小是 20,而我這個(gè) Demo 的間距又很小,導(dǎo)致縮放到最大地鐵線路圖顯示也很小,所以我在 htconfig 中更改了 ht 的默認(rèn) zoomMax 屬性,記住,更改這個(gè)值一定要在所有的 ht 調(diào)用之前,因?yàn)樵?htconfig 中設(shè)置的值在后面定義都是不可更改的。
感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享HTML5 Canvas的交互式地鐵線路圖怎么弄內(nèi)容對大家有幫助,同時(shí)也希望大家多多支持創(chuàng)新互聯(lián),關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,遇到問題就找創(chuàng)新互聯(lián),詳細(xì)的解決方法等著你來學(xué)習(xí)!