d3.js豎向直方圖與交互式提示框,V3與V4

效果圖

上一篇文章寫了如何根據(jù)數(shù)據(jù)表生成橫向直方圖溉苛,這次來寫一下豎向直方圖的生成和交互式提示框的加入.
與橫向相同镜廉,數(shù)據(jù)——比例尺——矩形——坐標(biāo)軸.
不過在畫直方圖之前,先來考慮一下瀏覽器適配的問題.

動(dòng)態(tài)SVG


如果想要SVG圖形可以隨瀏覽器改變大小愚战,應(yīng)注意以下設(shè)定:

var width = "100%";  //寬度與父元素保持一致
var height ="100%";   //高度與父元素保持一致
var country_svg = d3.select("#country_data_rank")     //根據(jù)id選擇父元素
    .append("svg")          //加入svg
    .attr("width", width)       
    .attr("height", height)
        .attr("viewBox","0 0 820 85") //viewBox 設(shè)定為(820娇唯,85)
        .attr("preserveAspectRatio","xMaxYMax meet")
    .attr("style", "border: 1px solid black");

具體細(xì)節(jié)可以參考這篇文章.
簡單來說就是,默認(rèn)情況下寂玲,SVG畫出來的東西將會(huì)以原始大兴濉(設(shè)定的像素)畫出,當(dāng)你縮放瀏覽器的時(shí)候拓哟,SVG在視覺上不會(huì)有任何變化想许,縮小瀏覽器比SVG的大小更小的時(shí)候?yàn)g覽器會(huì)將SVG直接截?cái)唷?br> 如果我們希望畫出來的SVG可以永遠(yuǎn)完整的顯示在一個(gè)大小不定的元素中,就必須用到視窗viewBox的概念.
視窗就是從你的原始SVG中,按照你給出的坐標(biāo)流纹,截取一部分圖形糜烹,將它拉伸或壓縮到你SVG設(shè)定的長寬中.
在這里"viewBox","0 0 820 85",既等同于漱凝,將原SVG中(0疮蹦,0)到(820,85)之間的圖像拉伸到長度為"100%"茸炒,寬度為"100%".
因?yàn)樵O(shè)定了SVG長寬是隨著父元素變化的愕乎,我們SVG畫布截取的這一部分圖像自然也會(huì)隨著變化,永遠(yuǎn)完整顯示.

數(shù)據(jù)的傳入


在這里壁公,我要從mysql里面查詢所有國家的數(shù)據(jù)量感论,排個(gè)序.
在python部的相應(yīng)route下面直接執(zhí)行一個(gè)新的sql查詢:

mysql = MySQL()
# MySQL configurations
app.config['MYSQL_DATABASE_USER'] = 'root'
app.config['MYSQL_DATABASE_PASSWORD'] = 'Password'
app.config['MYSQL_DATABASE_DB'] = 'test'
app.config['MYSQL_DATABASE_HOST'] = 'localhost'
mysql.init_app(app)

@app.route("/iotemplate")
def iotemplate():
  conn = mysql.connect() //連接數(shù)據(jù)庫
  cursor = conn.cursor() //設(shè)定cursor

  sql_country_data_count = "select keyword, count(*) from wbdata group by keyword order by count(*) desc;"
  cursor.execute(sql_country_data_count)
  sql_country_data_count_result = cursor.fetchall()

  def structure_result(myresult):
        datalist = []
        textlist = []
        outputdata = []
        for something in myresult:
            datalist.append(int(something[1]))
            textlist.append(something[0])
        outputdata.append(datalist)
        outputdata.append(textlist)
        return outputdata

  country_list = structure_result(sql_country_data_count_result)[0]
  country_list_name = structure_result(sql_country_data_count_result)[1]
  return render_template("iotemplate.html",datalist = datalist, textlist = mark_safe(textlist), country_list = country_list, country_list_name = mark_safe(country_list_name))

這一部分我看心情以后再單獨(dú)寫吧....注意這里必須要加mark_safe,不然傳輸數(shù)據(jù)會(huì)出現(xiàn)解碼錯(cuò)誤紊册,參照上一篇文章 Python到JS數(shù)據(jù)交互&#39解碼錯(cuò)誤.

JS動(dòng)態(tài)生成直方圖


第一步:接受數(shù)據(jù)

var country_list = {{country_list}};
var country_list_name ={{country_list_name}};

第二步:設(shè)定比例尺:

var max_height = 70;
var country_linear = d3.scaleLinear()
        .domain([0, d3.max(country_list)])
        .range([ max_height-5, 0]);

這里注意了比肄,range不再是從0到max而是反過來的,這是因?yàn)?strong>一個(gè)豎直向的直方圖以及豎著向上的坐標(biāo)是和屏幕坐標(biāo)相反的湿硝,對(duì)瀏覽器來說薪前,y方向是豎著向下的而不是向上的,如果還是使用從0到max的值域关斜,那最終的坐標(biāo)軸就會(huì)變成0在最上面示括,大數(shù)往下排.
但是當(dāng)值域顛倒以后也就意味著,原數(shù)據(jù)越大痢畜,算出來的像素值越小垛膝,和我們的實(shí)際想要效果是反的,因?yàn)樵谶@種情況下丁稀,我們可以知道:

  1. 值域max是矩形最長的范圍
  2. 我們想要的直方圖每個(gè)矩形的長度其實(shí)應(yīng)該是設(shè)定的值域最大長度減去比例尺算出來的那個(gè)反的像素值.
  3. 我們的直方圖矩形起始y坐標(biāo)將會(huì)不一樣
解釋

所以吼拥,進(jìn)行以下設(shè)置:

var country_rect_width = 3;   
country_svg.selectAll("rect")
    .data(country_list)
    .enter()
    .append("rect")
    .attr("y", function(d){return country_linear(d)+6})//6 是為預(yù)留上邊緣而給出的偏移量
    .attr("x",function(d,i){
         return i * (country_rect_width + 1)+22;
    })
    .attr("height",function(d){
         return max_height - country_linear(d); //值域最大值減去比例尺算出來的那個(gè)像素值.
    })
    .attr("width", country_rect_width)
    .attr("fill","steelblue")

交互式提示框


接下來就要加入提示框了,
理論很好理解线衫,當(dāng)鼠標(biāo)滑過某個(gè)矩形的時(shí)候凿可,一個(gè)div顯現(xiàn)出來,鼠標(biāo)劃出授账,此div消失枯跑,鼠標(biāo)移動(dòng),div出現(xiàn)的坐標(biāo)跟著移動(dòng).
所以白热,先生成div敛助,設(shè)定透明度為0

var tooltip = d3.select("body")
                            .append("div")
                            .attr("class","tooltip")
                            .style("opacity",0.0);

再接著剛才生成"rect"的代碼繼續(xù)加入以下屬性:

.attr("opacity",0.4) //矩形原始透明度為0.4
        .on("mouseover",function(d,i){
            d3.select(this) 
                .attr("opacity",1);//當(dāng)鼠標(biāo)在上,矩形變成全不透明
            tooltip.html(i+1 +":"+ country_list_name[i])//且提示框內(nèi)部html動(dòng)態(tài)生成
                .style("left", (d3.event.pageX+ 10) + "px")//x位置為當(dāng)前鼠標(biāo)X坐標(biāo)向右10px
                .style("top", (d3.event.pageY - 10) + "px")//y位置為當(dāng)前鼠標(biāo)Y坐標(biāo)向上10px
                .style("opacity",1.0);//不透明
        })
        .on("mouseout",function(d,i){//當(dāng)鼠標(biāo)移出矩形
            d3.select(this)
                .transition()
                .duration(500)
                .attr("opacity",0.4);//變回0.4的透明度
            tooltip.style("opacity",0.0);//提示框直接變?yōu)槿该?        })
        .on("mousemove",function(d){//當(dāng)鼠標(biāo)移動(dòng)
            tooltip.style("left", (d3.event.pageX+ 10) + "px")//提示框跟著鼠標(biāo)移動(dòng)
              .style("top", (d3.event.pageY - 10) + "px");//提示框跟著鼠標(biāo)移動(dòng)
         })

再稍微設(shè)定一下樣式:

.tooltip{
    font-family: simsun;
     font-size: 70%;
     width: 120;
     height: auto;
     position: absolute;
     text-align: center;
     padding: 1px;
     border: 1px solid darkgray;
     background-color: white;
}

最后屋确,再和之前文章里寫的一樣纳击,
召喚坐標(biāo)軸 XD

var country_axis = d3.axisLeft(country_linear)
          .tickSize(780)
          .ticks(4);

country_svg.append("g")
        .attr("transform", "translate(800,10)")
         .call(country_axis);

country_svg.selectAll("text")
        .attr("font-size", "70%");

country_svg.selectAll("line")
        .attr("stroke","grey")
        .attr("stroke-dasharray","2.2");

country_svg.select(".domain").remove()

餅圖的話续扔,可以參考這篇教程哦~【 D3.js 高級(jí)系列 — 9.0 】 交互式提示框
如果要變換文本方向,記得參考CSS屬性writing-mode.

到此焕数,首圖上的直方圖和交互式提示框就應(yīng)該可以實(shí)現(xiàn)啦~
:)


這里是V3版本

主要只有兩個(gè)地方的不同纱昧,既axis的語法和scale的語法.

var width = "100%";  
var height ="100%";   
var country_svg = d3.select("#country_data_rank")     
    .append("svg")          
    .attr("width", width)       
    .attr("height", height)
        .attr("viewBox","0 0 820 85")
        .attr("preserveAspectRatio","xMaxYMax meet")
    .attr("style", "border: 1px solid black");

var country_list = {{country_list}};

var country_list_name ={{country_list_name}};

var max_height = 70;
var country_linear = d3.scale.linear() //這里語法不同
        .domain([0, d3.max(country_list)])
        .range([ max_height-5, 0]);

var country_rect_width = 3;
country_svg.selectAll("rect")
    .data(country_list)
    .enter()
    .append("rect")
        .attr("y", function(d){return country_linear(d)+6})
    .attr("x",function(d,i){
         return i * (country_rect_width + 1)+22;
    })
    .attr("height",function(d){
         return max_height - country_linear(d);
    })
    .attr("width", country_rect_width)
    .attr("fill","steelblue")
        .attr("opacity",0.4)
        .on("mouseover",function(d,i){
            d3.select(this)
                .attr("opacity",1);

          tooltip.html(i+1 +":"+ country_list_name[i])
                    .style("left", (d3.event.pageX+10) + "px")
                    .style("top", (d3.event.pageY - 10) + "px")
                    .style("opacity",1.0);
        })
        .on("mouseout",function(d,i){
            d3.select(this)
                .transition()
                .duration(500)
                .attr("opacity",0.4);
          tooltip.style("opacity",0.0);
        })
        .on("mousemove",function(d){
                tooltip.style("left", (d3.event.pageX+ 10) + "px")
                        .style("top", (d3.event.pageY - 10) + "px");
            })
        .on("click",function () {

        });


var tooltip = d3.select("body")
                            .append("div")
                            .attr("class","tooltip")
                            .style("opacity",0.0);

  var country_axis = d3.svg.axis() //這里語法不同
            .scale(country_linear)
          .orient("left")
          .tickSize(780)
          .ticks(4);

  country_svg.append("g")
        .attr("transform", "translate(800,10)")
   .call(country_axis);

country_svg.selectAll("text")
        .attr("font-size", "70%");

  country_svg.selectAll("line")
        .attr("stroke","grey")
        .attr("stroke-dasharray","2.2");

country_svg.select(".domain").remove()
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市堡赔,隨后出現(xiàn)的幾起案子砌些,更是在濱河造成了極大的恐慌,老刑警劉巖加匈,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異仑荐,居然都是意外死亡雕拼,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門粘招,熙熙樓的掌柜王于貴愁眉苦臉地迎上來啥寇,“玉大人,你說我怎么就攤上這事洒扎〖穑” “怎么了?”我有些...
    開封第一講書人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵袍冷,是天一觀的道長磷醋。 經(jīng)常有香客問我,道長胡诗,這世上最難降的妖魔是什么邓线? 我笑而不...
    開封第一講書人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮煌恢,結(jié)果婚禮上骇陈,老公的妹妹穿的比我還像新娘。我一直安慰自己瑰抵,他們只是感情好你雌,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著二汛,像睡著了一般婿崭。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上习贫,一...
    開封第一講書人閱讀 51,301評(píng)論 1 301
  • 那天逛球,我揣著相機(jī)與錄音,去河邊找鬼苫昌。 笑死颤绕,一個(gè)胖子當(dāng)著我的面吹牛幸海,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播奥务,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼物独,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了氯葬?” 一聲冷哼從身側(cè)響起挡篓,我...
    開封第一講書人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎帚称,沒想到半個(gè)月后官研,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡闯睹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年戏羽,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片楼吃。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡始花,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出孩锡,到底是詐尸還是另有隱情酷宵,我是刑警寧澤,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布躬窜,位于F島的核電站浇垦,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏斩披。R本人自食惡果不足惜溜族,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望垦沉。 院中可真熱鬧煌抒,春花似錦、人聲如沸厕倍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽讹弯。三九已至况既,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間组民,已是汗流浹背棒仍。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留臭胜,地道東北人莫其。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓癞尚,卻偏偏與公主長得像,于是被迫代替她去往敵國和親乱陡。 傳聞我的和親對(duì)象是個(gè)殘疾皇子浇揩,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354

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

  • 這里講一下怎么樣用d3.js,輸入一個(gè)數(shù)據(jù)list憨颠,根據(jù)數(shù)據(jù)畫一個(gè)帶有坐標(biāo)軸的簡單直方圖.以下是目標(biāo)效果. 直方圖...
    Kaidi_G閱讀 4,818評(píng)論 1 3
  • 餅圖的繪制稍微復(fù)雜一點(diǎn)點(diǎn)爽彤,主要是網(wǎng)上很多教程還是老版本d3.這里用的是 D3 4.0 API Reference....
    Kaidi_G閱讀 3,497評(píng)論 0 0
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,110評(píng)論 25 707
  • 【正式測定:阜陽地震4.3級(jí)!】中國地震臺(tái)網(wǎng)正式測定:3月14日14時(shí)13分在安徽阜陽市市轄區(qū)(北緯33.0度匙瘪,東...
    劉漢皇閱讀 718評(píng)論 4 1
  • 人心總是隔肚皮。不知道是自己特別zuo蝶缀,還是怎么回事丹喻?最見不得那些自己親近的人的欺騙,我既信了你翁都,你又為何還欺騙我...
    拾麥穗的小女孩閱讀 197評(píng)論 0 0