D3js更新直方图元素不起作用(常规更新模式) [英] D3js Updating Histogram elements not working (General Update Pattern)

查看:33
本文介绍了D3js更新直方图元素不起作用(常规更新模式)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试完成与此处类似的操作: https://www.opportunityatlas.org/.如果您继续浏览此链接,然后点击显示分布" 以查看图表,然后选择在屏幕上" ,然后在地图上移动光标,您将看到矩形的大小会发生变化,更新模式也会起作用,即,如果矩形已经存在,则它将水平移动到新值.

I am trying to accomplish something similar to what is here : https://www.opportunityatlas.org/. If you proceed further to this link and click on 'Show Distribution' to see the graph and select 'On Screen' and then move the cursor around the map you will see the size of the rectangles changes and also the update patterns works i.e. if a rectangle was already there it moves horizontally to the new value.

我试图做同样的事情,但是无法实现更新部分.你能指出我错过的地方吗?我已经附加了一部分代码,其中有两个数据集 data1 data2 ,但有一些常见的ID正如您看到的那样,当您单击更新以更改数据集时,所有矩形均处于输入阶段,并且它们均未更改现有矩形的位置(在更新阶段均未更改).如果有人可以指导我完成这一过程,将会很有帮助.如果还有其他方法可以实现我在链接中提供的同一图形,那么如果有其他方法也将很有帮助. 谢谢!

I have tried doing the same but could not achieve the update part. Could you please point me out where I missed out. I have attached a part of my code where there are two data sets data1 and data2 with some id's common but as you can see when you click on update to change the data set all rectangles are in the enter phase and none of them change position for existing ones (none in update phase). It will be helpful if someone can guide me through this one. If there is some other way to implement the same graph that I provided in the link, that would also be helpful if there is some other approach. Thanks in advance !

let initialRender = true;
let selectedData1 = true;
const margin = {
    top: 10,
    right: 30,
    bottom: 30,
    left: 30
  },
  width = 550 - margin.left - margin.right,
  height = 150 - margin.top - margin.bottom;

function printChart(asd, data, dataGradients) {
  const svg = d3.select('#data-viz')
  // const isZoomed = map.getZoom() > zoomThreshold;

  // if (isZoomed !== changedZoom) {
  //   initialRender = true;
  //   changedZoom = isZoomed;
  // }

  // X axis and scale ------------->>>>>>>>>>>>>>>>>>>>
  const xScale = d3.scaleLinear()
    .domain(d3.extent(data.map(d => d.value)))
    .range([0, width])

  const xAxisCall = d3.axisBottom(xScale)
    .tickFormat(d3.format(".2s"))
    .ticks(5)
    .tickSizeOuter(0);

  let xAxis = null
  if (initialRender) {
    d3.select(".axis-x").remove()
    xAxis = svg.append("g")
      .attr("class", "axis-x")
      .attr("transform", "translate(0," + 115 + ")")
    initialRender = false
  } else {
    xAxis = d3.select(".axis-x")
  }

  xAxis.transition()
    .duration(2000)
    .ease(d3.easeSinInOut)
    .call(xAxisCall)
  // X axis and scale <<<<<<<<<<<<<<<<-----------------------------
  const binMin = 5;
  const binMax = 150;
  const tDuration = 3000;

  // Just to calculate max elements in each bin ---------->>>>>>>>>>>>>>>>>>
  let histogram = d3.histogram()
    .value(d => d.value)
    .domain(xScale.domain())
    .thresholds(xScale.ticks(10));

  let bins = histogram(data).filter(d => d.length > 0);
  console.log(bins);
  const max = d3.max(bins.map(bin => bin.length))
  const maxBinSize = max <= 10 ? 10 : max
  // Just to calculate max elements in each bin <<<<<<<<<<<<----------------

  // Decide parameters for histogram ------------>>>>>>>>>>>>>>>>>
  const dotSizeScale = d3.scaleLinear()
    .domain([binMin, binMax])
    .range([10, 4])
  const dotSize = dotSizeScale(maxBinSize);

  const dotSpacingScale = d3.scaleLinear()
    .domain([binMin, binMax])
    .range([12, 6])
  const dotSpacing = dotSpacingScale(maxBinSize);

  const thresholdScale = d3.scaleLinear()
    .domain([binMin, binMax])
    .range([10, 100])
  const threshold = thresholdScale(maxBinSize);

  const yTransformMarginScale = d3.scaleLinear()
    .domain([binMin, binMax])
    .range([100, 100])
  const yTransformMargin = yTransformMarginScale(maxBinSize);

  if (dotSize !== 10) {
    d3.selectAll('.gBin').remove()
    d3.selectAll('rect').remove()
  }

  histogram = d3.histogram()
    .value(d => d.value)
    .domain(xScale.domain())
    .thresholds(xScale.ticks(threshold));

  bins = histogram(data).filter(d => d.length > 0);
  // Decide parameters for histogram <<<<<<<<<<<<<<<<<<<<--------------------------

  // Y axis scale -------------------->>>>>>>>>>>>>>>>>>>>
  var yScale = d3.scaleLinear()
    .range([height, 0]);

  yScale.domain([0, d3.max(bins, (d) => d.length)]);
  svg.append("g")
    .attr("class", "axis-y")
    .call(d3.axisLeft(yScale));
  d3.select(".axis-y")
    .remove()
  // Y axis scale <<<<<<<<<<<<<<<<<<<<<<<-----------------

  const binGroup = svg.selectAll(".gBin")
    .data(bins,
      (d) => {
        console.log('id 1', d.x0)
        return d.x0
      }
    )

  binGroup
    .exit()
    .transition()
    .duration(2000)
    .style("opacity", 0)
    .remove()

  const binGroupEnter = binGroup
    .enter()
    .append("g")
    .merge(binGroup)
    .attr("class", "gBin")
    .attr("x", 1)
    .attr("transform", function(d) {
      return "translate(" + xScale(d.x0) + "," + yTransformMargin + ")";
    })
    .attr("width", 10)

  const elements = binGroupEnter.selectAll("rect")
    .data(d => d.map((p, i) => ({
        id: p.id,
        idx: i,
        value: p.value,
      })),
      function(d) {
        console.log('id 2', d)
        return d.id
      }
    )

  elements.exit()
    .transition()
    .duration(tDuration)
    .style("opacity", 0)
    .remove()

  elements
    .enter()
    .append("rect")
    .merge(elements)
    .attr("y", -(height + margin.top))
    // .on("mouseover", tooltipOn)
    // .on("mouseout", tooltipOff)
    .transition()
    .delay(function(d, i) {
      return 50 * i;
    })
    .duration(tDuration)
    .attr("id", d => d.value)
    .attr("y", (d, i) => -(i * dotSpacing))
    .attr("width", dotSize)
    .attr("height", dotSize)
    // .style("fill", (d) => getBinColor(d.value, dataGradients))
    .style("fill", 'red')
}

const data1 = [{
  id: 1,
  value: 14
}, {
  id: 13,
  value: 12
}, {
  id: 2,
  value: 50
}, {
  id: 32,
  value: 142
}]
const data2 = [{
  id: 1,
  value: 135
}, {
  id: 7,
  value: 2
}, {
  id: 2,
  value: 50
}, {
  id: 32,
  value: 50
}]
printChart(null, data1, null)

function changeData() {
  selectedData1 ?
    printChart(null, data2, null) :
    printChart(null, data1, null)
  selectedData1 = !selectedData1
}

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
<button onclick="changeData()"> Update data </button>
<svg width="550" height="250" id="data-viz">
      <g transform="translate(30, 100)">
      </g>
    </svg>

推荐答案

您的问题似乎是这些行:

Your problem appears to be these lines:

if (dotSize !== 10) {
  d3.selectAll('.gBin').remove();
  d3.selectAll('rect').remove();
}

在计算任何选择之前,将删除所有元素,以便所有内容(bin bin g 和元素 rect )都变为 enter .

All your elements are removed before any selections are calculated so everything (both your bin g and element rect) become enter.

另一个有趣的事情是您的垃圾箱数据密钥.由于您使用的是 x0 ,因此您的 g 也会根据直方图函数计算bin的方式进入/退出.

Another interesting thing is your data key for your bins. Since you are using the x0 your g will also enter/exit depending on how the histogram function calculates the bins.

<html>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
  <button onclick="changeData()">Update data</button>
  <svg width="550" height="250" id="data-viz">
    <g transform="translate(30, 100)"></g>
  </svg>
  <script>
    let initialRender = true;
    let selectedData1 = true;
    const margin = {
        top: 10,
        right: 30,
        bottom: 30,
        left: 30,
      },
      width = 550 - margin.left - margin.right,
      height = 150 - margin.top - margin.bottom;

    function printChart(asd, data, dataGradients) {
    
      console.clear();
    
      const svg = d3.select('#data-viz');
      // const isZoomed = map.getZoom() > zoomThreshold;

      // if (isZoomed !== changedZoom) {
      //   initialRender = true;
      //   changedZoom = isZoomed;
      // }

      // X axis and scale ------------->>>>>>>>>>>>>>>>>>>>
      const xScale = d3
        .scaleLinear()
        .domain(d3.extent(data.map((d) => d.value)))
        .range([0, width]);

      const xAxisCall = d3
        .axisBottom(xScale)
        .tickFormat(d3.format('.2s'))
        .ticks(5)
        .tickSizeOuter(0);

      let xAxis = null;
      if (initialRender) {
        d3.select('.axis-x').remove();
        xAxis = svg
          .append('g')
          .attr('class', 'axis-x')
          .attr('transform', 'translate(0,' + 115 + ')');
        initialRender = false;
      } else {
        xAxis = d3.select('.axis-x');
      }

      xAxis.transition().duration(2000).ease(d3.easeSinInOut).call(xAxisCall);
      // X axis and scale <<<<<<<<<<<<<<<<-----------------------------
      const binMin = 5;
      const binMax = 150;
      const tDuration = 3000;

      // Just to calculate max elements in each bin ---------->>>>>>>>>>>>>>>>>>
      let histogram = d3
        .histogram()
        .value((d) => d.value)
        .domain(xScale.domain())
        .thresholds(xScale.ticks(10));

      let bins = histogram(data).filter((d) => d.length > 0);
      //console.log(bins);
      const max = d3.max(bins.map((bin) => bin.length));
      const maxBinSize = max <= 10 ? 10 : max;
      // Just to calculate max elements in each bin <<<<<<<<<<<<----------------

      // Decide parameters for histogram ------------>>>>>>>>>>>>>>>>>
      const dotSizeScale = d3
        .scaleLinear()
        .domain([binMin, binMax])
        .range([10, 4]);
      const dotSize = dotSizeScale(maxBinSize);

      const dotSpacingScale = d3
        .scaleLinear()
        .domain([binMin, binMax])
        .range([12, 6]);
      const dotSpacing = dotSpacingScale(maxBinSize);

      const thresholdScale = d3
        .scaleLinear()
        .domain([binMin, binMax])
        .range([10, 100]);
      const threshold = thresholdScale(maxBinSize);

      const yTransformMarginScale = d3
        .scaleLinear()
        .domain([binMin, binMax])
        .range([100, 100]);
      const yTransformMargin = yTransformMarginScale(maxBinSize);

      /*
      if (dotSize !== 10) {
        d3.selectAll('.gBin').remove()
        d3.selectAll('rect').remove()
      }
      */

      histogram = d3
        .histogram()
        .value((d) => d.value)
        .domain(xScale.domain())
        .thresholds(xScale.ticks(threshold));

      bins = histogram(data).filter((d) => d.length > 0);
      // Decide parameters for histogram <<<<<<<<<<<<<<<<<<<<--------------------------

      // Y axis scale -------------------->>>>>>>>>>>>>>>>>>>>
      var yScale = d3.scaleLinear().range([height, 0]);

      yScale.domain([0, d3.max(bins, (d) => d.length)]);
      svg.append('g').attr('class', 'axis-y').call(d3.axisLeft(yScale));
      d3.select('.axis-y').remove();
      // Y axis scale <<<<<<<<<<<<<<<<<<<<<<<-----------------

      const binGroup = svg.selectAll('.gBin').data(bins, (d) => {
        //console.log('id 1', d.x0)
        return d.x0;
      });

      binGroup.exit().transition().duration(2000).style('opacity', 0).remove();

      const binGroupEnter = binGroup
        .enter()
        .append('g')
        .merge(binGroup)
        .attr('class', 'gBin')
        .attr('x', 1)
        .attr('transform', function (d) {
          return 'translate(' + xScale(d.x0) + ',' + yTransformMargin + ')';
        })
        .attr('width', 10);

      const elements = binGroupEnter.selectAll('rect').data(
        (d) =>
          d.map((p, i) => ({
            id: p.id,
            idx: i,
            value: p.value,
          })),
        function (d) {
          //console.log('id 2', d)
          return d.id;
        }
      );

      let eex = elements
        .exit()
        .transition()
        .duration(tDuration)
        .style('opacity', 0)
        .remove();

      console.log("rects exiting", eex.nodes().map(e => "rect" + e.getAttribute('id')))

      let een = elements
        .enter()
        .append('rect')
        .attr('id', (d) => d.value);

      console.log("rects entering", een.nodes().map(e => "rect" + e.getAttribute('id')))

      let eem = een
        .merge(elements);

      console.log("rects merge", eem.nodes().map(e => "rect" + e.getAttribute('id')))

      eem
        .attr('y', -(height + margin.top))
        // .on("mouseover", tooltipOn)
        // .on("mouseout", tooltipOff)
        .transition()
        .delay(function (d, i) {
          return 50 * i;
        })
        .duration(tDuration)
        .attr('y', (d, i) => -(i * dotSpacing))
        .attr('width', dotSize)
        .attr('height', dotSize)
        // .style("fill", (d) => getBinColor(d.value, dataGradients))
        .style('fill', 'red');
    }

    const data1 = [
      {
        id: 1,
        value: 14,
      },
      {
        id: 13,
        value: 12,
      },
      {
        id: 2,
        value: 50,
      },
      {
        id: 32,
        value: 142,
      },
    ];
    const data2 = [
      {
        id: 1,
        value: 135,
      },
      {
        id: 7,
        value: 2,
      },
      {
        id: 2,
        value: 50,
      },
      {
        id: 32,
        value: 50,
      },
    ];
    printChart(null, data1, null);

    function changeData() {
      selectedData1
        ? printChart(null, data2, null)
        : printChart(null, data1, null);
      selectedData1 = !selectedData1;
    }
  </script>
</html>

这篇关于D3js更新直方图元素不起作用(常规更新模式)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆