基础部分

坐标系

D3通过SVG画图,SVG的坐标系同HTML中的坐标系类似,都是以左上角作为坐标原点,向下向右为正延伸。

坐标轴

坐标轴的实现有多种方法,主要可以分为以下几种:

  • 连续性输入连续性输出:线性比例尺scalelinear,通过把domain中给定的范围通过线性关系映射到SVG中由range规定的指定范围
  • 连续性输入离散性输出:scaleQuantile将连续性的domain映射为离散的range块
  • 离散性输入离散性输出:scaleordinal将离散的domain映射为离散的range块
  • 离散性输入连续性输出:通过划分n个一定宽度的band,把domain中的离散的数据,每个数据作为一个band,映射到range中指定的区域

坐标轴的绘制通过上述的各种映射,然后根据映射设置样式,如d3中的axisBottom()绘制在底部的坐标轴,还可以设置刻度的长度、数量等等,然后通过添加g元素,通过call回调来画出给定格式的坐标轴,轴通过path绘制,刻度通过d3中提供的line(“path generator“之一)进行绘制,刻度之下的标注通过text标签,各项的位置可以通过attr添加transform属性来添加translate进行微调。

主要的图表元素

例如矩形图中的rect、散点图中的circle都是SVG中的元素,在d3中通过selectAll("rect").data(dataset).enter().append("rect")这种方式来给各项创建的矩形绑定数据。
.data会给所选择的元素分配数据,如果所给元素少于可分配数据的话,就会把数据存于placeholder,.enter返回代表需要增加的那些元素(已经绑定了placeholder数据)的selection,所以在后面接的append()创建元素一经创建都绑定了所给的数据。矩形元素的定位依靠x,y,其中x,y的数据需要与坐标轴进行绑定,就是通过上述的各种映射,计算出相应的在SVG图上的位置。

在SVG中可以通过path路径来进行产生任意的graph,d3提供了path generator可以根据points数组为我们自动创建path所需的d属性字符串,两者结合,可以很轻松的创建复杂的图形,对于想饼图这种需要startangle、endangle,输入的数据只是一个数组,故先把数组转换为对应的[startangle,endangle]数组,然后通过弧生成器来绘制最终的饼图。

Brush and Zoom

刷子选定选区,zoom进行缩放,缩放的原理是将选中的选区映射到整个画布之中,相当于一种对画布的重新绘制。
条形图、面积图和折线图的zoom采用的机制都比较类似。都是先调用zoom的各项API创建一个zoom behavior,
zoom.extent()设置平移显示的视口范围,
zoom.scaleExtent()设置缩放的倍数,
zoom.translateExtent()设置可以平移的元素范围,
zoom.on()声明监听器,处理鼠标手势发生时的进行的操作,

在监听器处理程序之中,最主要的一步就是
xScale.range([2*padding,width-2*padding].map(d => d3.event.transform.applyX(d)));这是对x轴的比例尺的range进行改写,
d3.event.transform.applyX(d)返回的是转换之后的x轴坐标.对比例尺进行改写之后再重新绘制各个元素的位置达到缩放的效果。

demo

完整代码见 github

  • bar chart


    屏幕快照 2018-11-19 下午12.43.30.png
//部分关键代码
var svg = d3.select("#chart")
        .append("svg")
        .attr("id","column")
        .attr("width",width)
        .attr("height",height)
        .call(zoom);
  
    var xScale = d3.scaleBand()
        .domain(tumourid)
        .rangeRound([2*padding,width-2*padding]);
  
    var yScale = d3.scaleLinear()
        .domain([0,1.1*yAxisWidth])
        .range([height-3*padding,3*padding]);
  
    var xAxis = d3.axisBottom(xScale)
        .tickSize(0)
        .tickPadding(6)
        .tickValues(xScale.domain().filter(function(d,i){return !(i%10)}));
        
    var yAxis = d3.axisLeft(yScale).tickSize(0);
   //缩放操作
    function zoom(svg){
      const extent = [[2*padding,3*padding],[width-2*padding,height-3*padding]];
      svg.call(d3.zoom()
        .scaleExtent([1,20])
        .translateExtent(extent)
        .extent(extent)
        .on("zoom",zoomed));
      function zoomed(){
        xScale.range([2*padding,width-2*padding].map(d => d3.event.transform.applyX(d)));
        svg.select("#svgrects")
          .selectAll("rect")
          .attr("x",function(d){
            return xScale(d["ID "])+xScale.bandwidth()/2;
          })
          .attr("width",xScale.bandwidth());
        svg.select(".axis--x").call(xAxis);
      }
    }
//缩放裁剪
    svg.append("defs").append("clipPath")
        .attr("id","myclip")
        .append("rect")
        .attr("x","40")
        .attr("y","0")
        .attr("width","600")
        .attr("height","500");

    function make_y_axis(){
      return d3.axisLeft(yScale);
    }
    svg.append("g")
        .attr("stroke","lightgray")
        .attr("stroke-opacity","0.1")
        .attr("shape-rendering","crispEdges")
        .call(make_y_axis()
         .tickSize(-width+4*padding,0,0)
         .tickFormat(""))
        .attr("transform","translate("+(2*padding)+",0)");
  
    svg.append("g")
        .attr("class","axis axis--x")
        .attr("clip-path","url(#myclip)")
        .attr("transform","translate(0,"+(height-3*padding)+")")
        .call(xAxis)
        .selectAll("text")
        .attr("transform","rotate(90)"+"translate("+(1.4*padding)+",0)")
    svg.append("text")
        .attr("fill","black")
        .attr("text-anchor","end")
        .attr("font-size",10)
        .attr("x",width-padding)
        .attr("y",height-2*padding)
        .text("肿瘤ID");
    
    svg.append("g")
        .attr("class","axis axis--y")
        .attr("transform","translate("+(2*padding)+",0)")
        .call(yAxis)
        .append("text")
        .attr("text-anchor","middle")
        .attr("font-size",10)
        .attr("fill","black")
        .attr("x",0)
        .attr("y",3*padding)
        .text(s);
    
    svg.selectAll("text")
        .attr("fill","black");
    var rects = svg.append("g")
        .attr("id","svgrects")
        .attr("clip-path","url(#myclip)")
        .selectAll("rect")
        .data(dataset)
        .enter()
        .append("rect")
        .attr("fill",document.getElementById("variablescolor").value)
        .attr("x",function(d){
          return xScale(d["ID "])+xScale.bandwidth()/2;
        })
        .attr("y",function(d){
          return yScale(d[s]);
        })
        .attr("width",xScale.bandwidth())
        .attr("height",function(d){
          return height-yScale(d[s])-3*padding;
        }) ;
  • scatter diagram


    屏幕快照 2018-11-19 下午1.12.32.png
//部分关键代码
var svg = d3.select("#chart")
        .append("svg")
        .attr("id","solid")
        .attr("width",width)
        .attr("height",height)
  
    var xScale = d3.scaleLinear()
        .domain([0,1.1*xAxisWidth])
        .range([2*padding,width-padding*2]);
  
    var yScale = d3.scaleLinear()
        .domain([0,1.1*yAxisWidth])
        .range([height-padding,padding]);
      var circle =svg.append("g")
        .attr("id","svgcircles")
        .attr("clip-path","url(#myclip)")
        .selectAll("circle")
        .data(dataset)
        .enter()
        .append("circle")
        .attr("fill",document.getElementById("variablescolor").value)
        .attr("cx",function(d){
          return xScale(d[xstring]);
        })
        .attr("cy",function(d){
          return yScale(d[ystring]);
        })
        .attr("r",document.getElementById("variablessize").value);
  
    var xAxis = d3.axisBottom(xScale).tickSize(0,0,0);
       
    var yAxis = d3.axisLeft(yScale).tickSize(0,0,0);
    
    function make_x_axis(){
      return d3.axisBottom(xScale);
    }
    function make_y_axis(){
      return d3.axisLeft(yScale);
    }
    svg.append("g")
      .attr("calss","grid grid--x")
      .attr("stroke","lightgray")
      .attr("stroke-opacity","0.1")
      .attr("shape-rendering","crispEdges")
      .call(make_x_axis()
        .tickSize(height-2*padding,0,0)
        .tickFormat("")
      )
      .attr("transform","translate(0,"+padding+")");
    svg.append("g")
    .attr("class","grid grid--y")
    .attr("stroke","lightgray")
    .attr("stroke-opacity","0.1")
    .attr("shape-rendering","crispEdges")
    .call(make_y_axis()
      .tickSize(-width+4*padding,0,0)
      .tickFormat("")
    )
    .attr("transform","translate("+(2*padding)+",0)");
    svg.append("g")
        .attr("class","axis axis--x")
        .attr("transform","translate(0,"+(height-padding)+")")
        .call(xAxis)
        .append("text")
        .attr("fill","black")
        .attr("text-anchor","end")
        .attr("font-size",10)
        .attr("x",width)
        .attr("y",0)
        .text(xstring);
    
    svg.append("g")
        .attr("class","axis axis--y")
        .attr("transform","translate("+(2*padding)+",0)")
        .call(yAxis)
        .append("text")
        .attr("fill","black")
        .attr("text-anchor","middle")
        .attr("font-size",10)
        .attr("x",0)
        .attr("y",padding)
        .text(ystring);
  
    svg.selectAll("text")
        .attr("fill","black");
   
    svg.append("defs").append("clipPath")
        .attr("id","myclip")
        .append("rect")
        .attr("x","40")
        .attr("y","20")
        .attr("width","420")
        .attr("height","460");
    var brush = d3.brush().on("end",brushended),
        idleTimeout,
        idleDelay = 350;
    svg.append("g")
        .attr("id","brush")
        .call(brush);
    d3.select(".overlay").attr("pointer-events","none");
    d3.select("svg")
    .on("mousedown",function(){
      d3.select(".overlay").attr("pointer-events","all");
    });
        
    function brushended(){
      var s = d3.event.selection;
      if(!s){
        if(!idleTimeout){
          return idleTimeout = setTimeout(idled,idleDelay);
        }
        xScale.domain([0,1.1*xAxisWidth]);
        yScale.domain([0,1.1*yAxisWidth]);
      }else{
        xScale.domain([s[0][0],s[1][0]].map(xScale.invert,xScale));
        yScale.domain([s[1][1],s[0][1]].map(yScale.invert,yScale));
        svg.select("#brush").call(brush.move,null);
      }
      d3.select(".overlay").attr("pointer-events","none");
      zoom();
    }
    function idled(){
      idleTimeout = null;
    }
    function zoom(){
      var t = svg.transition().duration(750);
      svg.select(".axis--x").transition(t).call(xAxis);
      svg.select(".axis--y").transition(t).call(yAxis);
      svg.select(".grid--x").transition(t).call(make_x_axis()
      .tickSize(height-2*padding,0,0)
      .tickFormat(""));
      svg.select(".grid--y").transition(t).call(make_y_axis()
      .tickSize(-width+4*padding,0,0)
      .tickFormat(""));
      d3.select("#svgcircles").selectAll("circle").transition(t)
          .attr("cx",function(d){return xScale(d[xstring]);})
          .attr("cy",function(d){return yScale(d[ystring]);});
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,122评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,070评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,491评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,636评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,676评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,541评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,292评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,211评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,655评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,846评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,965评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,684评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,295评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,894评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,012评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,126评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,914评论 2 355

推荐阅读更多精彩内容

  • d3 (核心部分)选择集d3.select - 从当前文档中选择一系列元素。d3.selectAll - 从当前文...
    谢大见阅读 3,441评论 1 4
  • Django 准备 “虚拟环境为什么需要虚拟环境:到目前位置,我们所有的第三方包安装都是直接通过 pip inst...
    33jubi阅读 1,326评论 0 5
  • 一:java概述: 1,JDK:Java Development Kit,java的开发和运行环境,java的开发...
    慕容小伟阅读 1,789评论 0 10
  • Data Visualization with D3 D3: SVG中的jQurey 1. Add Documen...
    王策北阅读 761评论 0 2
  • Swift学习总结 基础部分 程序是指令的集合,写程序就是写一系列的指令去控制计算机去做我们想做的事,编译:将程序...
    青昀兄阅读 468评论 0 3