本篇文章主要介绍了使用 D3.js 创建物流地图的示例代码, 小编觉得挺不错的, 现在分享给大家, 也给大家做个参考一起跟随小编过来看看吧
本文介绍了使用 D3.js 创建物流地图的示例代码, 分享给大家, 具体如下:
示例图
制作思路
需要绘制一张中国地图, 做为背景
需要主要城市的经纬坐标, 以绘制路张起点和终点
接收到物流单的城市, 绘制一个闪烁的标记
已经有过物流单的目标城市, 不再绘制路线
每次新产生一笔物流单, 都有一个标记沿路线移向目标的动画效果
绘制结束后的数据, 需要清理掉, 以减少对浏览器的资源占用
开始撸码
1. 创建网页模板
加载 D3JS, 为了方便调试, 直接下载 d3.js 文件在本地, 实际使用的时候, 可以换成加载路径注意, 使用的是 V4 版的 D3, 和 V3 版有差异
创建一个 DIV 块, 准备绘图
- <!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>
创建 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"); //
创建 SVG 图形分组, 以备调用
gmp, 保存背景地图和起点标记
mline, 保存起点和目标之间的连线, 以及目标点
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");
创建投影函数
经纬度不能直接用来绘图, 需要转换成平面坐标 d3js 提供了比较丰富的投影方法, 本例中使用了 geoEquirectangular()方法
projection 是将经纬度转换为平面坐标的方法
path 是将经纬度转换为连线绘制点坐标的方法(省得自己再写函数构造 path 路径)
- var projection = d3.geoEquirectangular()
- .center([465,395]) // 指定投影中心, 注意 [] 中的是经纬度
- .scale(height)
- .translate([width / 2, height / 2]);
- var path = d3.geoPath().projection(projection);
创建 marker 标记, 以便多个连线终点调用
由于会有多个物流连线的终点, 所以都放在一个 marker 标记中调用
这个标记是由中间的 圆形 + 外圈 构成外圈的闪烁效果另外创建
- marker = svg.append("defs")
- .append("marker")
- .append("marker")
- .attr("id", "pointer")
- .attr("viewBox","0 0 12 12") // 可见范围
- .attr("markerWidth","12") // 标记宽度
- .attr("markerHeight","12") // 标记高度
- .attr("orient", "auto") //
- .attr("markerUnits", "strokeWidth") // 随连接线宽度进行缩放
- .attr("refX", "6") // 连接点坐标
- .attr("refY", "6")
- // 绘制标记中心圆
- marker.append("circle")
- .attr("cx", "6")
- .attr("cy", "6")
- .attr("r", "3")
- .attr("fill", "white");
- // 绘制标记外圆, 之后在 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");
绘制中国地图, 并标记起点(长沙)
地图使用的经纬集为 china.json, 这个文件网上有很多
- // 记录长沙坐标
- var changsha = projection([112.59,28.12]);
- // 读取地图数据, 并绘制中国地图
- mapdata = [];
- d3.json('china.json', function(error, data){
- if (error)
- console.log(error);
- // 读取地图数据
- mapdata = data.features;
- // 绘制地图
- gmap.selectAll("path")
- .data(mapdata)
- .enter()
- .append("path")
- .attr("d", path);
- // 标记长沙
- 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");
- });
创建方法, 绘制一条从指定起点到终点的连线, 并在络点绘制 marker 标记
方法需要输入终点城市名称 (city) 和经纬度(data)
调用之前建立的 project()方法, 将终点经纬度转换为平面坐标
计算起点 (长沙) 和终点之前的距离, 做为线条长度和动画时间参数
在线条上绘制一个圆形标记, 并实现从起点到终点的移动动画
标记移动到终点后, 即删除, 节省资源
如果线点在之前已经绘制过, 则不绘线条, 只绘制移动标记
每处理一次物流单, 则城市记录 + 1
- // 创建对象, 保存每个城市的物流记录数量
- var citylist = new Object();
- // 创建方法, 输入 data 坐标, 绘制发射线
- 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();
- };
创建动画队例, 实现标记外圈的闪烁效果
- 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);
- });
创建测试按钮和测试的目标城市数据
- 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],
- };
- // 随机获得目标城市
- var cityname = [], total = 0;
- for (var key in cityordinate){
- cityname[total++] = key;
- };
- // 创建操作按钮, 每次点击发射一条物流线
- 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]]);
- });
来源: http://www.phperz.com/article/18/0316/363161.html