dlrandy / note-issues

2 stars 0 forks source link

D3-蛋疼 #44

Open dlrandy opened 6 years ago

dlrandy commented 6 years ago

D3是完全模块的,有很多小的库组成,可以单独使用 ,也可以 一起使用,也可以自定义build

scaleLinear用于将数据转换成值 var scaleLinear = d3.scaleLinear().domain([]).range([]).clamp(true) domain = scaleLinear.invert(range)

dlrandy commented 6 years ago

scaleTime 将时间转换成value scaleTime.invert

映射一个抽象的值到视觉描述正是数据可视化做的,也是D3做的

scaleQuantize从数字数据创建label 但是它对应的是连续的domain scaleQuanize.invertExtent()

使用 scaleOrdinal 从非数字值创建label 它对应的是一组特定的值 scaleOrdinal()

d3.max(data, function(d){ return d.property }) d3.min d3.extent d3.set

d3.csv产生的数据除了对象,还有一个columns

dlrandy commented 6 years ago

d3的data不指定key方法的时候,是第一个数据对应着对一个元素,第二个对应第二个,以此类推。

key函数控制着数据项分给哪一个元素,他可以返回元素的key,也会返回数据的key。

指定key的数据分配给匹配key的元素。

如果多个元素具有同一个key,重复的元素会被放入exit selection;如果多个数据具有同一个key,重复的数据会被放入enter selection。

selection.data是有一个merge的过程的,

key函数的参数,指的是邦定在元素上的数据

dlrandy commented 6 years ago

selectAll().nodes() d3.select是整个document selection.select是整个selection

d3.select(').attr().property().style().classed().text().html()

d3.select().append(tag).insert(tag, before).remove() 上面的操作会修改目标tag,就是接下来的操作影响的都是加入的tag

d3里的一个select约定是新的selection缩进两个空格已经存在的selection缩进四个。这个数量都是相对于最一开始的位置。实际就是2个

data默认返回的是update selection,为什么叫做update selection是因为有元素和一些对应的数据了

当你的数据随着时间变化的时候,D3 不会重新创建整个图表,而是一些元素进行更新

data的key方法用于将数据项映射到DOM元素

D3.js使用key方法返回来的key来区分元素存在与否,并进行merge

整个的data区域update.merge(enter)

dlrandy commented 6 years ago

D3能够创建普通的DOM元素,也能够渲染到canvas,但是最常见的输出类型是svg

在使用svg的时候不需要设置width,y等的像素后缀,因为svg里一切都是像素的

svg有个默认size的,

因为svg没有自动布局,所以margin不好使,元素在没有设置位置的情况下 会彼此堆叠。

svg里的g元素,像DOM里的div,g是graphic的意思,经常备用为基本容器,承载rect或者text等。它没有x, y。需要 使用transform来设置位移。

大多数的D3原生selection API都会返回一个selection,使能够启用链式方法。

因为我们写的code不在selection的prototype上,所以链式我们自己的方法需要一些额外的工作的。可以使用selection.call来调用自己的方法引用,只需要提供selection作为第一个参数,然后调用之后它会返回一个selection。它的具有较好的复用性和可读性,还能支持链式调用。

dlrandy commented 6 years ago

一个图标如果没有为轴留有空间是不能够添加轴的

D3社区有一个定义margin的约定

var margin = { top: 10, right: 20, bottom: 25, left: 25 };
var width = 425 - margin.left - margin.right;
var height = 625 - margin.top - margin.bottom;

var svg = d3.select('.chart')
  .append('svg')
    .attr('width', width + margin.left + margin.right)
    .attr('height', height + margin.top + margin.bottom)
  .append('g')
    .attr('transform', `translate(${margin.left}, ${margin.top})`);

svg.append('rect')
  .attr('width', width / 2)
  .attr('height', height)
  .style('fill', 'blue')
  .style('stroke', 'green');

svg.append('rect')
  .attr('x', width / 2)
  .attr('width', width / 2)
  .attr('height', height)
  .style('fill', 'lightblue')
  .style('stroke', 'green');

Y坐标轴的domain是反向设置的,因为svg里Y的尺寸是从上到下运行的。所以最小的值在最底部,最大的值在最顶部。

var yScale = d3.scaleLinear()
     .domain([0, 1])
     .range([height, 0])
var yAxis = d3.axisLeft(yScale)//default allocate optimal seg
      .tricks(n, '.2%'[formatstr])// n seg,如果formatstr是%,那么domain要设置成0-1,这个还适用.1s小数点的情况

selection.call(yAxis)

设置X轴的时候和Y不一样,设置Y轴的时候selection.call就可以了。X轴则需要加一个额外的g,然后通过transform样式translate到底部,在调用call

var xAxis = d3.axisBottom(xScale)
//.ticks(d3.timeMinute.every(45))
.ticks(5)

svg.append('g')
  .attr('transform', `translate(0, ${height})`)
  .call(xAxis)

//
var xAxis = d3.axisBottom(xScale).ticks(5)
.tickSizeInner(10)
.tickSizeOuter(20)
.tickPadding(5)
svg.append('g')
  .attr('transform', `translate(0, ${height})`)
  .call(xAxis)
dlrandy commented 6 years ago

使用viewBox属性使得D3的chart响应式

svg的视口就是SVG里可见区

viewbox是把svg绘制到画布的实际坐标系统。它是把crop的区域 重新缩放到当前的整个区间

var margin = { top: 10, right: 20, bottom: 30, left: 30 };
var width = 400 - margin.left - margin.right;
var height = 600 - margin.top - margin.bottom;

var svg = d3.select('.chart')
  .append('svg')
    .attr('width', width + margin.left + margin.right)
    .attr('height', height + margin.top + margin.bottom)
    .call(responsivefy)
  .append('g')
    .attr('transform', 'translate(' + margin.left + ', ' + margin.top + ')');

svg.append('rect')
  .attr('width', width)
  .attr('height', height)
  .style('fill', 'lightblue')
  .style('stroke', 'green');

var yScale = d3.scaleLinear()
  .domain([0, 100])
  .range([height, 0]);
var yAxis = d3.axisLeft(yScale);
svg.call(yAxis);

var xScale = d3.scaleTime()
  .domain([new Date(2016, 0, 1, 6), new Date(2016, 0, 1, 9)])
  .range([0, width]);

var xAxis = d3.axisBottom(xScale)
  .ticks(5)
  .tickSize(10)
  .tickPadding(5);
svg
  .append('g')
    .attr('transform', `translate(0, ${height})`)
  .call(xAxis);

function responsivefy(svg) {
  // get container + svg aspect ratio
  var container = d3.select(svg.node().parentNode),
      width = parseInt(svg.style("width")),
      height = parseInt(svg.style("height")),
      aspect = width / height;

  // add viewBox and preserveAspectRatio properties,
  // and call resize so that svg resizes on inital page load
  svg.attr("viewBox", "0 0 " + width + " " + height)
      .attr("preserveAspectRatio", "xMinYMid")
      .call(resize);

  // to register multiple listeners for same event type,
  // you need to add namespace, i.e., 'click.foo'
  // necessary if you call invoke this function for multiple svgs
  // api docs: https://github.com/mbostock/d3/wiki/Selections#on
  d3.select(window).on("resize." + container.attr("id"), resize);

  // get width of container and resize svg to fit it
  function resize() {
      var targetWidth = parseInt(container.style("width"));
      svg.attr("width", targetWidth);
      svg.attr("height", Math.round(targetWidth / aspect));
  }
}
dlrandy commented 6 years ago

创建不同类型的图表需要使用不同scale。

dlrandy commented 6 years ago

复用transition有两种方式:一种是在函数里声明transition,之后立即多个selection使用(声明在一起是因为transition一旦声明就会执行,一旦结束就没有效果了);第二种就是单独声明transition,每一个selection的transition使用call方法进行调用

dlrandy commented 6 years ago

data的key方法就是说在做join的时候,查找该属性唯一识别这些对象

dlrandy commented 6 years ago

d3的on函数 回调函数的第三个参数是elements,一般在这种回调函数里使用selection.call对一些代码

margin 约定就是 svg元素的width、height是marin+ 已经去掉margin的width,然后追加一个g,调整它得到transform到margin 的left和bottom

dlrandy commented 6 years ago

scaleBand的domain需要持有所有对应轴的值

scaleSqurt用于设置scateer的半径变化

常见的更新模式 update exit.remove() enter update.merge(enter) 别忘了data的key函数

想设置动态的axis,就需要重新设置scale的domain