1. 程式人生 > >【d3.js實踐教程特別篇】PornHub釋出基於d3的網民觀看成人視訊時長分佈互動式地圖

【d3.js實踐教程特別篇】PornHub釋出基於d3的網民觀看成人視訊時長分佈互動式地圖

學習d3.js(以下都簡稱d3)也有一段時間了,執行d3做了幾個專案。我發現中文的d3教程很少,國外資料多但要求有一定的英文閱讀能力(推薦網址:http://bl.ocks.org/mbostock),於是就萌發了寫一個d3實際運用系列文章的想法,現在開始付之行動。在系列中,我會用d3+html5 canvas實現一些實際效果(如統計結果展示,地圖資料展示等),希望可以跟大家共同學習交流。

執行環境是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)
        });
    }

再來幾張效果圖: