D3學(xué)習(xí)系列(一) 基礎(chǔ)知識(shí)與柱形圖繪制

「前言」

最開(kāi)始的初衷是想畫(huà)個(gè)弦圖(chord)與尚ǎ基圖(sankey)谱轨,真的很炫有沒(méi)有救湖!然而D3零基礎(chǔ)的我表示源碼看不懂拣播,受到1萬(wàn)點(diǎn)暴擊(+﹏+)~ 于是果斷去惡補(bǔ)D3的基礎(chǔ)知識(shí)晾咪,并加以整理同時(shí)加深對(duì)自己的印象。

「基礎(chǔ)概念」

選擇集
使用 d3.select() 或 d3.selectAll() 選擇元素后返回的對(duì)象贮配,就是選擇集

無(wú)名函數(shù)
function(d, i) 這個(gè)函數(shù)以后經(jīng)常要使用到

  • d 代表數(shù)據(jù)谍倦,也就是與某元素綁定的數(shù)據(jù)。
  • i 代表索引泪勒,代表數(shù)據(jù)的索引號(hào)昼蛀,從 0 開(kāi)始。

「數(shù)據(jù)綁定」

D3可以用兩種函數(shù)來(lái)綁定數(shù)據(jù):

  • datum(): 綁定一個(gè)數(shù)據(jù)到選擇集上圆存,這里的數(shù)據(jù)并非一定要是number(數(shù)值型)叼旋,也可以是string(字符串)、bollean(布爾型)和object(對(duì)象)
  • data(): 綁定一個(gè)數(shù)組到選擇集上沦辙,數(shù)組的各項(xiàng)值分別與選擇集的各元素綁定夫植,更常用

data() 函數(shù)的常用語(yǔ)法

var dataset = [10,20,30,40,50];
var body = d3.select("body");

body.selectAll("p")  //選擇body中的所有p,但是目前還沒(méi)有油讯,所以是空集
    .data(dataset)  //綁定數(shù)組
    .enter()  //指定選擇集的enter部分
    .append("p")
    .text(function(d){ 
        return d; 
    })

這里要解釋下 Enter 的概念详民,它與Update、Exit是D3中三個(gè)非常重要的概念陌兑,處理的是當(dāng)選擇集和數(shù)據(jù)的數(shù)量關(guān)系不確定的情況沈跨。

如果數(shù)組為 [3, 6, 9, 12, 15],將此數(shù)組綁定到三個(gè) p 元素的選擇集上兔综《隽荩可以想象狞玛,會(huì)有兩個(gè)數(shù)據(jù)沒(méi)有元素與之對(duì)應(yīng),這時(shí)候 D3 會(huì)建立兩個(gè)空的元素與數(shù)據(jù)對(duì)應(yīng)涧窒,這一部分就稱(chēng)為 Enter心肪。而有元素與數(shù)據(jù)對(duì)應(yīng)的部分稱(chēng)為 Update。如果數(shù)組為 [3]杀狡,則會(huì)有兩個(gè)元素沒(méi)有數(shù)據(jù)綁定蒙畴,那么沒(méi)有數(shù)據(jù)綁定的部分被稱(chēng)為 Exit。示意圖如下所示呜象。


「柱形圖」

Bar Chart一般包括:矩形膳凝、坐標(biāo)軸與文字。

矩形

這里我們直接定義一個(gè)數(shù)組恭陡,用數(shù)組項(xiàng)對(duì)應(yīng)矩形的長(zhǎng)短(然而這種方法并不理想)蹬音。

var dataset = [50, 43, 120, 87, 99, 167, 142];

定義一塊SVG的繪制區(qū)域:

var width = 600;    // SVG的寬度
var height = 600;   // SVG的長(zhǎng)度

var svg = d3.select("body")
            .append('svg')  // body中添加SVG
            .attr('width', width)
            .attr('height', height);

定義三個(gè)我們要用的變量

var padding = {top: 20, right: 20, bottom: 20, left: 20};
var rectStep = 35;
var rectWidth = 30;

padding是svg內(nèi)的最外一層區(qū)域,留一段空白寬度是為了防止圖形繪制帶svg區(qū)域外休玩。rectStep表示前一個(gè)矩形到下一個(gè)矩形的距離(包括空白間隔)著淆,而rectWidth是矩形實(shí)際的寬度。說(shuō)了這么多還是看圖更易懂:


添加矩形元素

var rect = svg.selectAll("rect")
              .data(dataset)
              .enter()  //獲取enter部分
              .append("rect")   //添加rect元素拴疤,使其與綁定數(shù)組的長(zhǎng)度一致
              .attr("fill","steelblue")
              .attr("x",function(d,i){  //設(shè)置X坐標(biāo)
                  return padding.left + i * rectStep;
              })
              .attr("y",function(d,i){  //設(shè)置Y坐標(biāo)
                  return height - padding.bottom - d;
              })
              .attr("width",rectWidth)  //設(shè)置矩形寬度永部,之前定義的
              .attr("height",function(d){   //設(shè)置矩形高度,即為數(shù)組中的各項(xiàng)值
                  return d
              });

因?yàn)閿?shù)組dataset的長(zhǎng)度為7呐矾,所以最后生成7個(gè)矩形苔埋。x與y坐標(biāo)是矩形的左上角頂點(diǎn)。 這個(gè)坐標(biāo)是相對(duì)應(yīng)svg繪圖區(qū)域來(lái)講的蜒犯,坐標(biāo)原點(diǎn)位于左上角(0,0)组橄。

一張圖直接說(shuō)明:


標(biāo)簽文字

var text = svg.selectAll(text)
                .data(dataset)
                .enter()
                .append("text")
                .attr("fill","white")
                .attr("font-size","14px")
                .attr("text-anchor","middle")
                .attr("x",function(d,i){    //與矩形的X坐標(biāo)一樣
                    return padding.left + i * rectStep;
                })
                .attr("y",function(d){
                    return height - padding.bottom - d;
                })
                .attr('dx', rectWidth/2)    //x軸相對(duì)平移距離
                .attr('dy', "1em")  //em單位表示的是當(dāng)前文字所占一行的高度
                .text(function(d){  //要顯示的文字內(nèi)容
                    return d;   
                });

添加文字標(biāo)簽的方法與添加矩形元素方法相類(lèi)似,不過(guò)顏色要與矩形的顏色區(qū)分罚随。通過(guò)設(shè)置元素的text-anchor玉工、x、y淘菩、dx與dy五個(gè)屬性遵班,讓文字顯示在每個(gè)矩形的正中心

其中dx,dy表示相對(duì)(x,y)平移的大小潮改,所以文本會(huì)從(x+dx,y+dy)位置開(kāi)始顯示费奸,這個(gè)位置也叫<u>起始位置</u>。屬性text-anchor有三個(gè)值:start进陡、middle、end,微服,這里用middle表示文字中心位于<u>起始位置</u>上趾疚。

還是上圖說(shuō)明問(wèn)題:


效果圖:


坐標(biāo)軸

坐標(biāo)軸的主直線(xiàn)由path構(gòu)成缨历,刻度由line繪制,刻度文字用text完成糙麦。之前我們直接用數(shù)值的大小來(lái)表示像素的大小辛孵,這里我們使用比例尺,定義如下:

// SVG畫(huà)布
var width = 600;
var height = 600;
var svg = d3.select("body").append('svg')
            .attr('width', width)
            .attr('height', height);

// 坐標(biāo)軸的線(xiàn)性比例尺
var xScale = d3.scale.linear()
                .domain([0,10]) //定義域
                .range([0,300]);    //值域

// 定義坐標(biāo)軸
var axis = d3.svg.axis()
            .scale(xScale)
            .orient("bottom")
            .ticks(5);  //刻度的數(shù)量赡磅,這里顯示5個(gè)

//在 SVG 中添加一個(gè)分組元素魄缚,再將坐標(biāo)軸的其他元素添加到里面
var gAxis = svg.append("g")
                .attr("transform","translate(80,80)");
                .call(axis)

gAxis.attr('class', 'axis');    //添加一些樣式,否則太太太丑了...

call()函數(shù)的使用十分常見(jiàn)焚廊,這里使用的參數(shù)是前面定義的坐標(biāo)軸axis冶匹,等價(jià)于axis(gAxis);的形式。效果圖如下:

「柱形圖的坐標(biāo)軸」

對(duì)初學(xué)者而言咆瘟,這里的坑更多(老司機(jī)請(qǐng)無(wú)視)嚼隘。主要是因?yàn)槭褂昧吮壤咧螅琗Y坐標(biāo)軸袒餐、矩形長(zhǎng)寬飞蛹、刻度都要與之相對(duì)應(yīng)。不要問(wèn)我為什么知道這么多灸眼,都是淚......

為矩形圖定義比例尺

var xAxisWidth = 300;   //x軸寬度
var yAxisWidth = 300;   //y軸寬度

var xScale = d3.scale.ordinal() //x軸比例尺(序數(shù)比例尺)
                .domain(d3.range(dataset.length))
                .rangeRoundBands([0,xAxisWidth],0.2);
var yScale = d3.scale.linear()  //y軸比例尺(線(xiàn)性比例尺)
                .domain([0,d3.max(dataset)])
                .range([0,yAxisWidth]); 

定義完比例尺之后卧檐,矩形的高度、位置都要用比例尺來(lái)計(jì)算焰宣。如此之后霉囚,僅需簡(jiǎn)單修改比例尺,圖表就能自動(dòng)伸縮宛徊,所以前面的<u>矩形元素</u><u>矩形文字</u>的代碼都需要修改

矩形元素修改部分

.attr("x",function(d,i){    
    return padding.left + xScale(i);    // return padding.left + i * rectStep;
})
.attr("y",function(d,i){    
    return height - padding.bottom - yScale(d); // return height - padding.bottom - d;
})
.attr("width",xScale.rangeBand()) 
.attr("height",function(d){
    return yScale(d);
})

標(biāo)簽文字修改部分

.attr("x",function(d,i){    //與矩形的X坐標(biāo)一樣
    return padding.left + xScale(i);
})
.attr("y",function(d){
    return height - padding.bottom - yScale(d);
})
.attr('dx', xScale.rangeBand()/2)   //x軸相對(duì)平移距離
.attr('dy', "1em")  //em單位表示的是當(dāng)前文字所占一行的高度
.text(function(d){  //要顯示的文字內(nèi)容
    return d;
});

定義坐標(biāo)軸

var xAxis = d3.svg.axis()
            .scale(xScale)
            .orient("bottom");

yScale.range([yAxisWidth,0]);   //值域相反

var yAxis = d3.svg.axis()
            .scale(yScale)
            .orient("left");

此外還要注意佛嬉,y軸坐標(biāo)的值域要與原來(lái)相反,從最大值到最小值闸天,否則最后會(huì)出現(xiàn)下面這種情況:
<img src="http://upload-images.jianshu.io/upload_images/4762054-04b43eb28412b558.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" style="width: 35px;"/>

添加坐標(biāo)軸元素

//添加x軸
svg.append("g") 
    .attr("class","axis")
    .attr("transform","translate(" + padding.left + "," + (height - padding.bottom) + ")")
    .call(xAxis);

//添加y軸
svg.append("g")
    .attr("class","axis")
    .attr("transform","translate(" + padding.left + "," + (height - padding.bottom - yAxisWidth) + ")")
    .call(yAxis);

這里要小心x軸暖呕、y軸平移到目標(biāo)位置的距離,以及你設(shè)置padding前后左右的寬度苞氮,防止坐標(biāo)軸跑到外面去(又是血與淚的教訓(xùn))湾揽。

最后效果圖:

完整源代碼

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title></title>
        <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
        <style>
            .axis path,
            .axis line{
                fill: none;
                stroke: black;
                shape-rendering: crispEdges;
            }
            .axis text{
                font-family: sans-serif;
                font-size: 11px;
            }
        </style>
    </head>
    <body>
        <script >
            // 添加SVG畫(huà)布
            var dataset = [50, 43, 120, 87, 99, 167, 142];
            var width = 600;    // SVG的寬度
            var height = 600;   // SVG的長(zhǎng)度
            var svg = d3.select("body")
                        .append('svg')  // body中添加SVG
                        .attr('width', width)
                        .attr('height', height);
            var padding = {top: 20, right: 20, bottom: 20, left: 30};

            // 定義數(shù)據(jù)與比例尺
            var xAxisWidth = 300;   //x軸寬度
            var yAxisWidth = 300;   //y軸寬度
            var xScale = d3.scale.ordinal() //x軸比例尺(序數(shù)比例尺)
                            .domain(d3.range(dataset.length))
                            .rangeRoundBands([0,xAxisWidth],0.2);
            var yScale = d3.scale.linear()  //y軸比例尺(線(xiàn)性比例尺)
                            .domain([0,d3.max(dataset)])
                            .range([0,yAxisWidth]);

            // 添加矩形和文字元素
            var rect = svg.selectAll("rect")
                            .data(dataset)
                            .enter()  //獲取enter部分
                            .append("rect") //添加rect元素,使其與綁定數(shù)組的長(zhǎng)度一致
                            .attr("fill","steelblue")
                            .attr("x",function(d,i){    //設(shè)置X坐標(biāo)
                                // return padding.left + i * rectStep;
                                return padding.left + xScale(i);

                            })
                            .attr("y",function(d,i){    //設(shè)置Y坐標(biāo)
                                // return height - padding.bottom - d;
                                return height - padding.bottom - yScale(d);
                            })
                            .attr("width",xScale.rangeBand())    //設(shè)置矩形寬度
                            .attr("height",function(d){
                                return yScale(d);
                            })
            var text = svg.selectAll(text)
                            .data(dataset)
                            .enter()
                            .append("text")
                            .attr("fill","white")
                            .attr("font-size","14px")
                            .attr("text-anchor","middle")
                            .attr("x",function(d,i){    //與矩形的X坐標(biāo)一樣
                                return padding.left + xScale(i);
                            })
                            .attr("y",function(d){
                                return height - padding.bottom - yScale(d);
                            })
                            .attr('dx', xScale.rangeBand()/2)   //x軸相對(duì)平移距離
                            .attr('dy', "1em")  //em單位表示的是當(dāng)前文字所占一行的高度
                            .text(function(d){  //要顯示的文字內(nèi)容
                                return d;
                            });

            // 定義坐標(biāo)軸
            var xAxis = d3.svg.axis()
                        .scale(xScale)
                        .orient("bottom");
            yScale.range([yAxisWidth,0]);
            var yAxis = d3.svg.axis()
                        .scale(yScale)
                        .orient("left");
                        
            // 添加坐標(biāo)軸
            svg.append("g")
              .attr("class","axis")
              .attr("transform","translate(" + padding.left + "," + (height - padding.bottom) + ")")
              .call(xAxis);

            svg.append("g")
              .attr("class","axis")
              .attr("transform","translate(" + padding.left + "," + (height - padding.bottom - yAxisWidth) + ")")
              .call(yAxis);
        </script>
    </body>
</html>

「參考資料」

Learning D3.JS
D3.js:Update笼吟、Enter库物、Exit
D3.js - 初體驗(yàn)
D3.js數(shù)據(jù)可視化系列教程

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市贷帮,隨后出現(xiàn)的幾起案子戚揭,更是在濱河造成了極大的恐慌,老刑警劉巖撵枢,帶你破解...
    沈念sama閱讀 221,635評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件民晒,死亡現(xiàn)場(chǎng)離奇詭異精居,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)潜必,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)靴姿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人磁滚,你說(shuō)我怎么就攤上這事佛吓。” “怎么了垂攘?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,083評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵维雇,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我搜贤,道長(zhǎng)谆沃,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,640評(píng)論 1 296
  • 正文 為了忘掉前任仪芒,我火速辦了婚禮唁影,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘掂名。我一直安慰自己据沈,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,640評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布饺蔑。 她就那樣靜靜地躺著锌介,像睡著了一般。 火紅的嫁衣襯著肌膚如雪猾警。 梳的紋絲不亂的頭發(fā)上孔祸,一...
    開(kāi)封第一講書(shū)人閱讀 52,262評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音发皿,去河邊找鬼崔慧。 笑死,一個(gè)胖子當(dāng)著我的面吹牛穴墅,可吹牛的內(nèi)容都是我干的惶室。 我是一名探鬼主播,決...
    沈念sama閱讀 40,833評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼玄货,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼皇钞!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起松捉,我...
    開(kāi)封第一講書(shū)人閱讀 39,736評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤夹界,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后隘世,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體可柿,經(jīng)...
    沈念sama閱讀 46,280評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡也拜,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,369評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了趾痘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,503評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蔓钟,死狀恐怖永票,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情滥沫,我是刑警寧澤侣集,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站兰绣,受9級(jí)特大地震影響世分,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜缀辩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,870評(píng)論 3 333
  • 文/蒙蒙 一臭埋、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧臀玄,春花似錦瓢阴、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,340評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至累贤,卻和暖如春叠穆,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背臼膏。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,460評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工硼被, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人讶请。 一個(gè)月前我還...
    沈念sama閱讀 48,909評(píng)論 3 376
  • 正文 我出身青樓祷嘶,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親夺溢。 傳聞我的和親對(duì)象是個(gè)殘疾皇子论巍,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,512評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • 這里講一下怎么樣用d3.js,輸入一個(gè)數(shù)據(jù)list风响,根據(jù)數(shù)據(jù)畫(huà)一個(gè)帶有坐標(biāo)軸的簡(jiǎn)單直方圖.以下是目標(biāo)效果. 直方圖...
    Kaidi_G閱讀 4,824評(píng)論 1 3
  • 本教程是一個(gè)簡(jiǎn)單的入門(mén)教程嘉汰,能夠幫助初學(xué)者快速掌握D3的基礎(chǔ)知識(shí)。 本節(jié)內(nèi)容介紹了添加元素状勤、綁定數(shù)據(jù)鞋怀、使用數(shù)據(jù)双泪、矢...
    笨笨的笨小孩閱讀 2,026評(píng)論 0 1
  • D3是用于數(shù)據(jù)可視化的Javascript庫(kù)。使用SVG密似,Canvas和HTML焙矛。結(jié)合強(qiáng)大的可視化技術(shù)和數(shù)據(jù)驅(qū)動(dòng)的...
    Evelynzzz閱讀 7,896評(píng)論 7 5
  • 1 前言 一直想沿著圖像處理這條線(xiàn)建立一套完整的理論知識(shí)體系,同時(shí)積累實(shí)際應(yīng)用經(jīng)驗(yàn)残腌。因此有了從使用AVFounda...
    RichardJieChen閱讀 5,677評(píng)論 5 12
  • 《小酌怡情》 (一)今天戰(zhàn)友小聚村斟,晚上小酌了幾杯,喝的是戰(zhàn)友帶回來(lái)的俄羅斯啤酒抛猫,每人兩瓶剛剛好蟆盹。我以前是個(gè)很討厭喝...
    沐乘風(fēng)閱讀 1,208評(píng)論 0 0