学习d3.js(以下都简称d3)也有一段时间了,运行d3做了几个项目。我发现中文的d3教程很少,国外资料多但要求有一定的英文阅读能力(推荐网址:http://bl.ocks.org/mbostock),于是就萌发了写一个d3实际运用系列文章的想法,现在开始付之行动。在系列中,我会用d3+html5 canvas实现一些实际效果(如统计结果展示,地图数据展示等),希望可以跟大家共同学习交流。
代码我公布在git.cschina.com上,大家可以clone到本地运行,地址是:http://git.oschina.net/0604hx/d3lesson
运行环境是java 7+,tomcat 7.0.47+(以后会用到websocket,所以需要javaee7 跟 tomcat 7+的支持),IDE 是IntelliJ IDEA 13, 项目的视图使用了freemarker。
这是d3实践的特别篇,代码不是我写的,是从PornHub网站中copy出来,进行了简化、美化处理,并加上了一些注释。
早上看到了一则微博:
我想很多人对PornHub会有所认识,这是全球三大色情网站之一,内容可是相当丰富的哟。咳咳,跑题了...
微博里面提到的地图目测是d3.js的应用,所以想看看具体的代码。但是PornHub在国内是无法访问的(就连搜索这个关键字都不能....),于是使用vpn登录,可以正常访问PornHub网站,哇,里面好多资源呀!这网站不被墙真是没天理了!我收回心,毕竟我不是来看片的,我是来学习技术的呀!
我找到了交互式地图,网址为:http://www.pornhub.com/infographic/longest
从地图上可以看到,全球范围内的网民观看成人视频的平均时长。同时,可以切换到美国地图。除了根据国家形式查看,还可以转到城市级别,这时会在地图上标出全球的主要城市,当鼠标移动到城市的图标上去可以看到城市名字跟观看时长等数据。地图可以放大缩小。
看了下代码,js文件就两个:
data.min.js 是数据(包括了国家级别、城市级别的数据)
main.js 是绘制地图还有交互用的
usa.json 是美国的geoJson数据
world.json 则是世界地图数据
为了大家访问查看效果,我将地图部署到了d3lesson的项目中,有兴趣的朋友可以在git中导出项目然后运行。
通过这个例子,可以学习到地图的切换、缩放、小图标在大地图缩放是的处理方法(因为地图放大后,图标也会跟着放大,这样的话,不够美观)等。
具体的代码可以再git中查看(我都加了注释的),在这里主要讲一下里面绘图方法:
var m, //svg对象 r, //放置地图的 g 集合 u, //坐标转换器 n, //path a, //zoom元素 e; //地图块数据 var o, l; //绘制地图 function j() { o = $("#map").width(); //得到大地图宽度 l = $("#map").height(); //得到大地图高度 d3.json($("body").data("base-url") + c.mapScope + ".json", function(w) { m = d3.select("#map").append("svg").attr("width", o).attr("height", l); $("#map").css("background", c.mapOptions[c.mapDisplayMode].mapBackgroundColor).css("cursor", "move"); r = m.append("g"); if (c.mapScope == "world") { u = d3.geo.mercator().translate([0, 0]).scale(1).rotate([ - 11.5, 0]) } else { u = d3.geo.albersUsa().translate([0, 0]).scale(1) } e = topojson.feature(w, w.objects[(c.mapScope == "world") ? "collection": "usa"]); e.features = e.features.filter(function(x) { return x.id !== 11; }); var g = r.selectAll(".country").data(e.features); n = d3.geo.path().projection(u); q(); //完成地图的位移,不然页面时显示不出地图的 if (c.mapDisplayMode == "cities") { r.append("path").datum(topojson.merge(w, w.objects[(c.mapScope == "world") ? "collection": "usa"].geometries.filter(function(x) { return x.id !== 11 }))).attr("class", "dropshadow").attr("d", n).style("fill", "#5e8fa7").attr("width", o).attr("height", l).attr("transform", "translate(0,5)") } g.enter().append("path").attr("class", "country").attr("d", n).attr("id", function(y, x) { return "c" + y.id }); d3.selectAll("path.country").attr("class", "country") //给地图块上色,函数的z参数,就是对应元素的data对象(由之前调用data()函数分配) .style("fill",function(z) { if (c.mapDisplayMode == "countries") { //或者具体的时间长, 数据结构在 data.min.js var y = mapTimeData[c.mapScope][c.mapDisplayMode][z.id].t; var x = d(y); //判断颜色级别 return x } else { return c.mapOptions[c.mapDisplayMode].mapItemsColor } }) .style("stroke-width", 1).style("stroke", c.mapOptions[c.mapDisplayMode].strokeColor); p(); //创建qtip的提示 /* 一下的3行代码是设置svg的滚轮放大效果 */ d3.behavior.zoom(); a = d3.behavior.zoom().scaleExtent([c.minZoomLevel, c.maxZoomLevel]).on("zoom", k); m.call(a).call(a.event) }); }
再来几张效果图: