使用D3.js創(chuàng)建物流地圖的示例代碼
本文介紹了使用D3.js創(chuàng)建物流地圖的示例代碼,分享給大家,具體如下:
示例圖
制作思路
- 需要繪制一張中國地圖,做為背景。
- 需要主要城市的經(jīng)緯坐標(biāo),以繪制路張起點(diǎn)和終點(diǎn)。
- 接收到物流單的城市,繪制一個(gè)閃爍的標(biāo)記。
- 已經(jīng)有過物流單的目標(biāo)城市,不再繪制路線。
- 每次新產(chǎn)生一筆物流單,都有一個(gè)標(biāo)記沿路線移向目標(biāo)的動畫效果。
- 繪制結(jié)束后的數(shù)據(jù),需要清理掉,以減少對瀏覽器的資源占用。
開始擼碼
1.創(chuàng)建網(wǎng)頁模板
加載D3JS,為了方便調(diào)試,直接下載d3.js文件在本地,實(shí)際使用的時(shí)候,可以換成加載路徑。注意,使用的是V4版的D3,和V3版有差異。
創(chuàng)建一個(gè)DIV塊,準(zhǔn)備繪圖。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf8"> <script type="text/javascript" src="../../static/d3/d3.js"></script> <title>地圖</title> </head> <body> <div class="fxmap"> </div> </body> <script type="text/javascript"></script> </html>
創(chuàng)建SVG,以下所有代碼放在<script></script>中
var width=1000 , height=800; // 定義SVG寬高 var svg = d3.select("body div.fxmap") .append("svg") .attr("width", "width) .attr("height", height) .style("background","#000"); //
創(chuàng)建SVG圖形分組,以備調(diào)用
- gmp,保存背景地圖和起點(diǎn)標(biāo)記。
- mline,保存起點(diǎn)和目標(biāo)之間的連線,以及目標(biāo)點(diǎn)。
- buttion,測試用的按鈕
gmap = svg.append("g").attr("id", "map").attr("stroke", "white").attr("stroke-width",1); mline = svg.append("g").attr("id", "moveto").attr("stroke", "#FFF").attr("stroke-width", 1.5).attr("fill","#FFF"); button = svg.append("g").attr("id", "button").attr("stroke", "white").attr("stroke-width", 1).attr("fill", "white");
創(chuàng)建投影函數(shù)
- 經(jīng)緯度不能直接用來繪圖,需要轉(zhuǎn)換成平面坐標(biāo)。d3js提供了比較豐富的投影方法,本例中使用了geoEquirectangular()方法。
- projection 是將經(jīng)緯度轉(zhuǎn)換為平面坐標(biāo)的方法
- path 是將經(jīng)緯度轉(zhuǎn)換為連線繪制點(diǎn)坐標(biāo)的方法(省得自己再寫函數(shù)構(gòu)造path路徑)
var projection = d3.geoEquirectangular() .center([465,395]) // 指定投影中心,注意[]中的是經(jīng)緯度 .scale(height) .translate([width / 2, height / 2]); var path = d3.geoPath().projection(projection);
創(chuàng)建marker標(biāo)記,以便多個(gè)連線終點(diǎn)調(diào)用
- 由于會有多個(gè)物流連線的終點(diǎn),所以都放在一個(gè)marker標(biāo)記中調(diào)用。
- 這個(gè)標(biāo)記是由中間的 圓形 + 外圈 構(gòu)成。外圈的閃爍效果另外創(chuàng)建。
marker = svg.append("defs") .append("marker") .append("marker") .attr("id", "pointer") .attr("viewBox","0 0 12 12") // 可見范圍 .attr("markerWidth","12") // 標(biāo)記寬度 .attr("markerHeight","12") // 標(biāo)記高度 .attr("orient", "auto") // .attr("markerUnits", "strokeWidth") // 隨連接線寬度進(jìn)行縮放 .attr("refX", "6") // 連接點(diǎn)坐標(biāo) .attr("refY", "6") // 繪制標(biāo)記中心圓 marker.append("circle") .attr("cx", "6") .attr("cy", "6") .attr("r", "3") .attr("fill", "white"); // 繪制標(biāo)記外圓,之后在timer()中添加閃爍效果 marker.append("circle") .attr("id", "markerC") .attr("cx", "6") .attr("cy", "6") .attr("r", "5") .attr("fill-opacity", "0") .attr("stroke-width", "1") .attr("stroke", "white");
繪制中國地圖,并標(biāo)記起點(diǎn)(長沙)
地圖使用的經(jīng)緯集為china.json,這個(gè)文件網(wǎng)上有很多
// 記錄長沙坐標(biāo) var changsha = projection([112.59,28.12]); // 讀取地圖數(shù)據(jù),并繪制中國地圖 mapdata = []; d3.json('china.json', function(error, data){ if (error) console.log(error); // 讀取地圖數(shù)據(jù) mapdata = data.features; // 繪制地圖 gmap.selectAll("path") .data(mapdata) .enter() .append("path") .attr("d", path); // 標(biāo)記長沙 gmap.append("circle").attr("id","changsha") .attr("cx", changsha[0]) .attr("cy", changsha[1]) .attr("r", "6") .attr("fill", "yellow") gmap.append("circle").attr("id","changshaC") .attr("cx", changsha[0]) .attr("cy", changsha[1]) .attr("r", "10") .attr("stroke-width", "2") .attr("fill-opacity", "0"); });
創(chuàng)建方法,繪制一條從指定起點(diǎn)到終點(diǎn)的連線,并在絡(luò)點(diǎn)繪制marker標(biāo)記。
- 方法需要輸入終點(diǎn)城市名稱(city)和經(jīng)緯度(data)
- 調(diào)用之前建立的project()方法,將終點(diǎn)經(jīng)緯度轉(zhuǎn)換為平面坐標(biāo)。
- 計(jì)算起點(diǎn)(長沙)和終點(diǎn)之前的距離,做為線條長度和動畫時(shí)間參數(shù)。
- 在線條上繪制一個(gè)圓形標(biāo)記,并實(shí)現(xiàn)從起點(diǎn)到終點(diǎn)的移動動畫。
- 標(biāo)記移動到終點(diǎn)后,即刪除,節(jié)省資源。
- 如果線點(diǎn)在之前已經(jīng)繪制過,則不繪線條,只繪制移動標(biāo)記。
- 每處理一次物流單,則城市記錄+1。
// 創(chuàng)建對象,保存每個(gè)城市的物流記錄數(shù)量 var citylist = new Object(); // 創(chuàng)建方法,輸入data坐標(biāo),繪制發(fā)射線 var moveto = function(city, data){ var pf = {x:projection([112.59,28.12])[0], y:projection([112.59,28.12])[1]}; var pt = {x:projection(data)[0], y:projection(data)[1]}; var distance = Math.sqrt((pt.x - pf.x)**2 + (pt.y - pf.y)**2); if (city in citylist){ citylist[city]++; }else{ mline.append("line") .attr("x1", pf.x) .attr("y1", pf.y) .attr("x2", pt.x) .attr("y2", pt.y) .attr("marker-end","url(#pointer)") .style("stroke-dasharray", " "+distance+", "+distance+" ") .transition() .duration(distance*30) .styleTween("stroke-dashoffset", function(){ return d3.interpolateNumber(distance, 0); }); citylist[city] = 1; }; mline.append("circle") .attr("cx", pf.x) .attr("cy", pf.y) .attr("r", 3) .transition() .duration(distance*30) .attr("transform", "translate("+(pt.x-pf.x)+","+(pt.y-pf.y)+")") .remove(); };
創(chuàng)建動畫隊(duì)例,實(shí)現(xiàn)標(biāo)記外圈的閃爍效果
var scale = d3.scaleLinear(); scale.domain([0, 1000, 2000]) .range([0, 1, 0]); var start = Date.now(); d3.timer(function(){ var s1 = scale((Date.now() - start)%2000); // console.log(s1); gmap.select("circle#changshaC") .attr("stroke-opacity", s1); marker.select("circle#markerC") .attr("stroke-opacity", s1); });
創(chuàng)建測試按鈕和測試的目標(biāo)城市數(shù)據(jù)
var cityordinate = { '哈爾濱':[126.5416150000,45.8088260000], '石家莊':[116.46,39.92], '北京':[116.39564503787867,39.92998577808024], '上海':[121.480539,31.235929], '廣州':[113.271431,23.135336], '重慶':[106.558434,29.568996], '青島':[120.38442818368189,36.10521490127382], '福州':[119.30347,26.080429], '蘭州':[103.840521,36.067235], '貴陽':[106.636577,26.653325], '成都':[104.081534,30.655822], '西安':[108.946466,34.347269], '長春':[125.3306020000,43.8219540000], '臺灣':[120.961454,23.80406], '呼和浩特':[111.7555090000,40.8484230000], '澳門':[113.5494640000,22.1929190000], '武漢':[114.3115820000,30.5984670000], '昆明':[102.71460113878045,25.049153100453159], '烏魯木齊':[87.56498774111579,43.84038034721766], '益陽':[112.36654664522563,28.58808777988717], '南京':[118.77807440802562,32.05723550180587], '武昌':[114.35362228468498,30.56486029278519], }; // 隨機(jī)獲得目標(biāo)城市 var cityname = [], total = 0; for (var key in cityordinate){ cityname[total++] = key; }; // 創(chuàng)建操作按鈕,每次點(diǎn)擊發(fā)射一條物流線 button.append("circle") .attr("cx", width*0.9) .attr("cy", height*0.8) .attr("r", width/20) .attr("text","click") .attr("fill", "grey"); button.append("text") .attr("x", width*0.87) .attr("y", height*0.81) .style("font-size", "30px") .text("click"); button.on("click", function(){ var _index = ~~(Math.random() * total); moveto(cityname[_index], cityordinate[cityname[_index]]); });
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- D3.js實(shí)現(xiàn)折線圖的方法詳解
- JavaScript可視化圖表庫D3.js API中文參考
- D3.js實(shí)現(xiàn)柱狀圖的方法詳解
- d3.js實(shí)現(xiàn)簡單的網(wǎng)絡(luò)拓?fù)鋱D實(shí)例代碼
- D3.js中data(), enter() 和 exit()的問題詳解
- D3.js實(shí)現(xiàn)餅狀圖的方法詳解
- 基于d3.js實(shí)現(xiàn)實(shí)時(shí)刷新的折線圖
- D3.js實(shí)現(xiàn)雷達(dá)圖的方法詳解
- D3.js實(shí)現(xiàn)文本的換行詳解
- D3.js實(shí)現(xiàn)散點(diǎn)圖和氣泡圖的方法詳解
相關(guān)文章
JS可斷點(diǎn)續(xù)傳文件上傳實(shí)現(xiàn)代碼解析
這篇文章主要介紹了JS可斷點(diǎn)續(xù)傳文件上傳實(shí)現(xiàn)代碼解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-07-07JavaScript利用虛擬列表實(shí)現(xiàn)高性能渲染數(shù)據(jù)詳解
在前文中我們提到可以使用時(shí)間分片的方式來對長列表進(jìn)行渲染,但這種方式更適用于列表項(xiàng)的DOM結(jié)構(gòu)十分簡單的情況,所以本文來講講如何使用虛擬列表的方式,來同時(shí)加載大量數(shù)據(jù)吧2023-05-05JS+Canvas實(shí)現(xiàn)上傳圖片截圖功能
在我們平時(shí)開發(fā)圖片上傳時(shí),有時(shí)需要實(shí)現(xiàn)圖片的裁剪功能,這篇文章主要為大家介紹了如何使用Canvas實(shí)現(xiàn)上傳圖片截圖功能,希望對大家有所幫助2023-10-10