Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

D3.js #11

Open
rainit2006 opened this issue Feb 1, 2018 · 18 comments
Open

D3.js #11

rainit2006 opened this issue Feb 1, 2018 · 18 comments

Comments

@rainit2006
Copy link
Owner

No description provided.

@rainit2006
Copy link
Owner Author

rainit2006 commented Feb 1, 2018

  • 官网
    https://d3js.org/

  • D3.js开发数据可视化,性能方面和highcharts,echarts对比区别是什么?
    https://www.zhihu.com/question/28687373
    (关于d3.js性能 - 最近也有比较流行把react和d3一起用的。利用react的virtual dom,只用d3计算。)
    (总的来说,所有的第三方库都是基于这两种浏览器图形渲染技术实现的: Canvas 和 SVG 。
    )
    image

@rainit2006
Copy link
Owner Author

rainit2006 commented Feb 1, 2018

@rainit2006
Copy link
Owner Author

D3(Data Driven Document).jsとは
D3.jsとは、JSON、XML、CSV、タブ区切りテキスト、javascriptの配列などの
様々な形式のデータを可視化する時に便利なライブラリで、下記のような特徴があります。

・データに基づいてドキュメントを操作する。 =データドリブン(データ駆動)
・データの表現に必要な変換は行うが、表現そのものは直接行わない。
 ⇒表現は制作者に委ねられていますが、そのために複雑な表現が可能になっています。
 ⇒表現には主にSVGを使用します。
・モダンブラウザでの動作を前提としている。
 ⇒標準では古いブラウザはサポートしない。(IEは9以上)
・jQueryと同じようなチェーン構文を使用している。

<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>

HTML 5 提供两种强有力的“画布”:SVG 和 Canvas。
SVG,指可缩放矢量图形(Scalable Vector Graphics),是用于描述二维矢量图形的一种图形格式,是由万维网联盟制定的开放标准。 SVG 使用 XML 格式来定义图形,除了 IE8 之前的版本外,绝大部分浏览器都支持 SVG,可将 SVG 文本直接嵌入 HTML 中显示。

添加画布
D3 虽然没有明文规定一定要在 SVG 中绘图,但是 D3 提供了众多的 SVG 图形的生成器,它们都是只支持 SVG 的。因此,建议使用 SVG 画布。

var width = 300;  //画布的宽度
var height = 300;   //画布的高度

var svg = d3.select("body")     //选择文档中的body元素
    .append("svg")          //添加一个svg元素
    .attr("width", width)       //设定宽度
    .attr("height", height);    //设定高度
  • 常见语句:
svg.selectAll("rect")   //选择svg内所有的矩形
    .data(dataset)  //绑定数组
    .enter()        //指定选择集的enter部分
    .append("rect") //添加足够数量的矩形元素
   .attr("x",20) //x是矩形左上角x的坐标,y是矩形左上角y的坐标
    .attr("y",function(d,i){    
         return i * rectHeight;
    })
    .attr("width",function(d){
         return d;
    })
    .attr("height",rectHeight-2)
    .attr("fill","steelblue");
  • 比例尺(Scale)
    将 dataset 中最小的值,映射成 0;将最大的值,映射成 300。
var dataset = [1.2, 2.3, 0.9, 1.5, 3.3];

var min = d3.min(dataset);
var max = d3.max(dataset);

var linear = d3.scale.linear()
        .domain([min, max])
        .range([0, 300]);

linear(0.9);    //返回 0
linear(2.3);    //返回 175
linear(3.3);    //返回 300

其中,d3.scale.linear() 返回一个线性比例尺。domain() 和 range() 分别设定比例尺的定义域和值域。

  • 序数比例尺
var index = [0, 1, 2, 3, 4];
var color = ["red", "blue", "green", "yellow", "black"];

var ordinal = d3.scale.ordinal()
        .domain(index)
        .range(color);

ordinal(0); //返回 red
ordinal(2); //返回 green
ordinal(4); //返回 black

@rainit2006
Copy link
Owner Author

rainit2006 commented Feb 1, 2018

动画实例


Transition continuously
https://stackoverflow.com/questions/17127805/d3-js-run-a-transition-continuously

function myTrans() {
    d3.select("body").transition().duration(1000).style("background-color", "red")
      .each("end", function() {
        d3.select(this).transition().duration(1000).style("background-color", "blue")
          .each("end", function() { myTrans(); });
      });
}

动画显示

例1:
d3.select("body").append("svg").append("rect").attrs({
    x: 10, y: 10, width: 20, height: 20, fill: "red"
  }).transition().attrs({
    x: 20, y: 20, width: 40, height: 10, fill: "blue"
  });


例2,加上时间控制
d3.select("body").append("svg").append("rect").attrs({
    x: 10, y: 10, width: 20, height: 20, fill: "red"
  }).transition().duration(5000).delay(2000).attrs({
    fill: "black"
  });

transition比较核心的一点就是,必须要有两个核心的frame:起始frame是怎么样,终止frame是什么样。这些都要在schedule一个transition动作的时候指定好。因此有些�操作就没法加transition,例如,创建一个新的DOM element,因为没有起始frame.

并不是所有的属性都能加transition,目前D3支持四种数据/属性的transition:
-- numbers
-- colors
-- geometric transforms
-- strings with embedded numbers (e.g., "96px") (注意,这是目前能够应用transition的几种属性)

补间(tween)函数

setInterval(関数function, 一定時間の指定[, 引数1, 引数2, …])

@rainit2006
Copy link
Owner Author

rainit2006 commented Feb 2, 2018

SVGグループ要素
<g>..</g>
SVGグループ要素は、SVG要素をグループ化するために使われます。

g 要素はオブジェクトをグループ化するためのコンテナです。g 要素に適用された変形はその全ての子要素に対して実行されます。適用された属性は子要素に継承されます。加えて、複雑なオブジェクトを g 要素を使って定義しておくと後に 要素で参照することができます。

ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
x軸とy軸の設定:
d3.svg.axis()、方向はorient()で指定する。

例:X軸
var xAxis = d3.svg.axis()
                .scale(xScale)
                .orient("bottom")
                .tickSize(6, -height) // 棒の長さと方向。
                .tickFormat(function(d){ return d+"年"; }); // 数字に年をつけている。

Y軸
var yAxis = d3.svg.axis()
                .ticks(5) // 軸のチックの数。
                .scale(yScale)
                .orient("left")
                .tickSize(6, -width);

@rainit2006
Copy link
Owner Author

topojsonプラグイン

  1. データの入手
    地図のデータは通常shapefileのフォーマットとして提供されていることが多いようです。それをWebに描画する時は、GeoJSONフォーマットに変換して使います。さらに容量を小さくしたいときはTopoJSON に変換します.

  2. メルカトル図法
    地図は地球という球体の一部を平面図したものですから、なんらかの投影法(プロジェクション)が必要となります。メルカトル図法はgoogleマップやほとんどのWebマップで使われているものです。

  3. 実装
    d3.geoMercator()ですが、これでメルカトル図法を指定します。center([ 136.0, 35.6 ])で日本の中心を経度緯度で指定します。この指定を忘れると日本地図は画面上に描かれないので注意してください。

translate([width/2, height/2])で画面の中央を指定します。scale(scale)でデフォルトのスケールを指定します。これはズームイベントで変更可能となります。

次にd3.geoPath()は地図グラフのpath generator
var geoPath = d3.geoPath().projection(aProjection);

https://qiita.com/sand/items/422d4fab77ea8f69dfdf
http://deep-blend.com/jp/2014/06/d3-js-map-japanese-map/

@rainit2006
Copy link
Owner Author

rainit2006 commented Feb 3, 2018

動畫實現: 數據移動軌跡圖

       var width = 960,
        height = 700;
 
        var svg = d3.select("body").append("svg")
        .attr("width", width)
        .attr("height", height);

        var dataset = 
            [
                {x: 7, y: 24, 増減: "up"},
                {x: 8, y: 38, 増減: "down"},
                {x: 9, y: 90, 増減: "down"},
                {x: 10, y: 112, 増減: "up"},
                {x: 11, y: 210, 増減: "down"},
                {x: 12, y: 317, 増減: "up"},
                {x: 13, y: 425, 増減: "up"}
            ];
        
        // 白からスティールブルー(Steelblue)へのグラデーション
        var color = d3.scale
                        .linear()
                        .domain([0, dataset.length])
                        .range(["red", "white"])
                        .interpolate(d3.interpolateLab);
                        
        var index = 0;

        function fun(){
            if(index >= dataset.length)
                return;
            
            svg.append("circle")
                .attr("cx",dataset[index]["x"]*50)
                .attr("cy",dataset[index]["y"])
                .attr("r",10)
                .attr("fill","green");
            
            svg.selectAll("circle").style({"r": 20+index, "fill": function(d, i){return color(index-i)}});
            //注意,这里是color(i)而不是color[i]。 color是一个函数。

            index ++;

        }
        
       
        setInterval(fun, 1000);

注意: setInterval函数可以通过clearInterval函数来解除。

var timeID = setInterval( function(){
    //do something.
    ...
   if(counter > 1000){
       clearInterval(timeID);
   }
  counter++;
}, 1000);

@rainit2006
Copy link
Owner Author

rainit2006 commented Feb 3, 2018

csv文件处理

cities.csv:

city,state,population,land area
seattle,WA,652405,83.9
new york,NY,8405837,302.6
boston,MA,645966,48.3
kansas city,MO,467007,315.0

使用d3的csv函数读取csv文件:

d3.csv("/data/cities.csv", function(data) {
  console.log(data[0]); //显示第一行的数据
});

=> {city: "seattle", state: "WA", population: "652405", land area: "83.9"}

注意 : 在chrome中测试上面的代码会出现以下错误:
cannot load file:/cities.csv. Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, https, chrome-extension-resource.
Uncaught NetworkError: Failed to execute 'send' on 'XMLHttpRequest': Failed to load

这是因为安全机制禁止了cross origin request,不允许直接读取本地文件.
回避方法:
https://qiita.com/cigalecigales/items/33afaa42f91542ffa62e
https://dev.classmethod.jp/etc/chrome-localfile-security/

@rainit2006
Copy link
Owner Author

画网格:

  // Scale the range of the data
  x.domain(d3.extent(data, function(d) { return d.date; }));
  y.domain([0, d3.max(data, function(d) { return d.close; })]);

  // add the X gridlines
  svg.append("g")			
      .attr("class", "grid")
      .attr("transform", "translate(0," + height + ")")
      .call(make_x_gridlines()
          .tickSize(-height)  
          .tickFormat("")
      )

  // add the Y gridlines
  svg.append("g")			
      .attr("class", "grid")
      .call(make_y_gridlines()
          .tickSize(-width)   //网格线的长度
          .tickFormat("")
      )

  // add the valueline path.
  svg.append("path")
      .data([data])
      .attr("class", "line")
      .attr("d", valueline);

  // add the X Axis
  svg.append("g")
      .attr("transform", "translate(0," + height + ")")
      .call(d3.axisBottom(x));

  // add the Y Axis
  svg.append("g")
      .call(d3.axisLeft(y));

tickSize: 网格线的长度
ticks:坐标点的数量。

@rainit2006
Copy link
Owner Author

rainit2006 commented Feb 16, 2018

behaviour メソッドの基本
http://blog.aagata.ciao.jp/?eid=107
D3.jsには、ブラウザ上でのユーザ操作 (クリックやドラッグ) に応じてSVG要素を変化させるための behaviour メソッドが用意されています。

behaviour メソッドには、大きく drag() メソッドと zoom() メソッドの2種類が用意されています。

D3.js v3でDrag Behaviorを取得
var drag = d3.behavior.drag(); //V4ではエラー
D3.js/V4 でDrag Behaviorを取得
var drag = d3.drag(); // これでOK

var drag = d3.behavior.drag(); //V4ではエラー

var svg = d3.select("#MyGraph");
 
// SVG に circle 要素を追加
var circle = svg.append("circle")
  .attr({
    "cx" : 10,
    "cy" : 10,
    "r" : 10,
    "fill-opacity" : 0.8,
    "fill" : "orange"
  });
 
// ドラッグビヘイビアを準備
var drag = d3.behavior.drag()
  .on("drag", function(d){
    d3.select(this)
      .attr({
        "cx" : d3.event.x,
        "cy" : d3.event.y
      })
  });
 
// circle 要素をドラッグビヘイビアに渡す
circle.call(drag);
var svg = d3.select("svg")
	.attr("width", w)
	.attr("height", h)

//ズーム対象とするレイヤーを生成	
var zoomLayer  = svg.append("g")

//ズーム時の処理を設定
var zoomed = function() {
  zoomLayer.attr("transform", d3.event.transform);
}

//ズームイベントをsvg要素に束縛
svg.call(d3.zoom()
	.scaleExtent([0.1, 100])  //设定Zoom范围:从0.1到100倍大小。
	.on("zoom", zoomed));	

■想调整Zoom的变更量:

let zoomed = function() {
var delta = d3.event.transform.k - imgInfo.scale;
d3.event.transform.k = imgInfo.scale + delta * 0.01;
imgInfo.scale = d3.event.transform.k;
imgLayer.attr('transform', function(){
return 'translate(' + [ imgInfo.x, imgInfo.y ] + '),rotate('+imgInfo.r+' ,160, 160),scale('+imgInfo.scale+','+imgInfo.scale+')';
});
}

@rainit2006
Copy link
Owner Author

SVG的z轴顺序设定:如何让某组element显示在最顶层?
首先要注意,SVG不支持css的Z-index属性。即使设定了也无效。
方法:
I would recommend creating some "layers" using svg g elements which stands for "group".
Then when you append new elements, you can decide which layer you want them to be on, and it won't matter what order you assign them in, because the group elements have already been added to the DOM, and are ordered.

var layer1 = svg.append('g');
var layer2 = svg.append('g');

var redCircle = layer2.append('circle')
    .attr('cx', 50)
    .attr('cy', 50)
    .attr('r', 16)
    .attr('fill', 'red')

var blueSquare = layer1.append('rect')
    .attr('x', 25)
    .attr('y', 25)
    .attr('width', 50)
    .attr('height', 50)
    .attr('fill', 'blue');

http://jsfiddle.net/yDErU/

@rainit2006
Copy link
Owner Author

■更改text文本的字体颜色:
用fill属性,而不是font-color
.style("fill", function(d){ return z(d.id);});

@rainit2006
Copy link
Owner Author

■Legendを追加(d3 v4)

注意: legend[0][j]是取不出legend里的元素的,需要legend._groups[0][j]

    subContainer.html('<strong>Route Data Comparison</strong><div id="legend-div"></div>');

//////Legend要素追加////////

    var legendVals = ["Queens", "Kings", "Bronx", "Manhatan","Richmond"] ;
var color = d3.scaleOrdinal(d3.schemeCategory10);
 
  
var legend = d3.select("#legend-div")
    .append("svg")
  .attr("width", 500)
  .attr("height", 50)
  .selectAll('g')
    .data(legendVals)
    .enter()
  .append('g')
    .attr("class", "legends");

legend.append('rect') // 凡例の色付け四角
    .attr("x", 0)
    .attr("y", 10)
    .attr("width", 10)
    .attr("height", 10)
    .style("fill", function (d, i) { return color(i); }) // 色付け
 
legend.append('text')  // 凡例の文言
    .attr("x", 20)
    .attr("y", 20)
    .text(function (d, i) { return d ; })
    .attr("class", "textselected")
    .style("text-anchor", "start")
    .style("font-size", 15)
  ;
   
var padding = 20;  

legend.attr("transform", function (d, i) 
		          {
		            return "translate("+(d3.sum(legendVals, function(e,j) {if (j < i) { 
		                        	return legend._groups[0][j].getBBox().width;   // 各凡例の横幅サイズ取得
		              			} else {
		                			return 0; 
		                		}}) + padding * i) + ",0)";
    		});

@rainit2006
Copy link
Owner Author

■关于object的选择(d3 v3和v4有很大不同)
According to D3 4.x API:
Selections no longer subclass Array using prototype chain injection; they are now plain objects, improving performance.
So, in D3 version 4.x, selections are objects.
Also, it's worth mentioning that you're using the compressed version (https://d3js.org/d3.v4.min.js), which returns:
zi {_groups: Array[1], _parents: Array[1]}

image

@rainit2006
Copy link
Owner Author

rainit2006 commented Jul 10, 2018

数据绑定
D3 中是通过以下两个函数来绑定数据的:

  • datum():绑定一个数据(单数)到选择集上
  • data():绑定一个数组(复数)到选择集上,数组的各项值分别与选择集的各元素绑定

http://bubkoo.com/2014/10/24/thinking-about-D3-s-data-bingding/

理解下面代码

svg.selectAll("circle")
    .data(data)
  .enter().append("circle")
    .attr("cx", function(d) { return d.x; })
    .attr("cy", function(d) { return d.y; })
    .attr("r", 2.5);

image
那些已经和数据绑定的元素构成中间的 update 集合;而那些数据集合中存在,但在元素集合中还不存在的部分构成左边的 enter 集合,代表那些将被添加的元素;同理,那些在数据集合中不存在,而在元素集合中存在的元素构成了右边的 exit 集合,代表那些将被移除的元素。
现在我们可以揭开数据绑定 (data-join) 的神秘面纱:
(1) 首先,svg.selectAll("circle") 返回一个空选集,因为当前 SVG 还没有任何子元素,该选集的父节点是这个 SVG 容器。
(2) 然后将该选集与数据绑定,产生三个新的子选集,分别代表三种可能的状态:enter、update 和 exit。由于当前选集为空,所以 update 和 exit 子选集也为空,enter 子选集就包含了每条数据对应的元素的占位符。
(3) update 子选集直接通过 selection.data 返回,enter 和 exit 子选集分别通过 selection.enter 和 selection.exit 返回。
(4) 那些缺少的元素通过对 enter 子选集调用 selection.append 方法来添加到 SVG 中,这样就为每条数据添加了一个新的圆点到 SVG 中。
数据绑定意味着在数据和元素之间建立了一种关系,然后通过 enter、update 和 exit 三个子选集来实现这种关系。

但是为什么要这么麻烦呢?为什么不直接创建这些元素?数据绑定的优点在于更具通用性。上面代码仅仅处理了 enter 子选集,这对于静态数据足够了,我们可以方便地扩展上面的代码,只需要对 update 和 exit 稍作修改,就可以使其支持动态数据可视化。这意味着,我们可以实现实时数据的可视化。

var circle = svg.selectAll("circle")
    .data(data);
circle.exit().remove();
circle.enter().append("circle")
    .attr("r", 2.5);
circle
    .attr("cx", function(d) { return d.x; })
    .attr("cy", function(d) { return d.y; });

上面这段代码将重新计算数据绑定,并维护数据和元素之间的关系。如果新数据集小于旧集合,那些 exit 子选集中的多余元素将被移除;如果新数据集大于旧集合,那些 enter 子选集中的元素将被添加到 SVG 中;如果新数据集恰好与旧集合同样大小,那么所有这些元素仅仅会更新自身的位置,没有元素被添加或移除。

@rainit2006
Copy link
Owner Author

rainit2006 commented Jul 11, 2018

Enter()

.enter 指明了所有和data相比dom元素的缺口,enter和exit都是定义在update selection上的 (the selection returned by .data):

d3.select('#content')
  .selectAll('div')
  .data(myData)
  .enter();

.enter 返回代表需要增加的那些元素(已经绑定了数据)的selection. 通常跟随.enter的都是 .append 操作.

d3.select('#content')
  .selectAll('div')
  .data(myData)
  .enter()
  .append('div');

@rainit2006
Copy link
Owner Author

rainit2006 commented Jul 11, 2018

.exit
.exit返回未能绑定到数据的dom元素,一般来说紧跟着的是 .remove操作:

d3.select('#content')
  .selectAll('div')
  .data(myData)
  .exit()
  .remove();

@rainit2006
Copy link
Owner Author

rainit2006 commented Apr 4, 2019

问题解决: 发现一些plot无法响应鼠标的event (比如下图红圈里的点)。
image

原因排查: 肯定是某些element挡住(或干扰)到这个element了,导致鼠标放在上面的时候没有响应(被其他元素截获了)。后来发现是下面path线导致的干扰。

let path = pathView.append("path")
      .attr("class", "line")
      .attr("id", dataId)
      .attr("opacity", 1)
      .attr("d", function (d) { return line(d.values); })
			.style("stroke", function(d){return getColor(d.id);});

修改如下:

let path = pathView.append("path")
      .attr("class", "line")
      .attr("id", dataId)
      .attr("opacity", 1)
      .attr("d", function (d) { return line(d.values); })
			.style("stroke", function(d){return getColor(d.id);})
			.attr("pointer-events", "none");

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant