D3js更新直方图元素不起作用(常规更新模式)

基尚维卡尼

我正在尝试完成类似于此处的内容:https : //www.opportunityatlas.org/如果继续浏览此链接,然后单击“显示分布”以查看图形并选择“在屏幕上”,然后在地图上移动光标,您将看到矩形的大小发生更改,并且更新模式也有效,例如矩形已经在那里,它会水平移动到新值。

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

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>

标记

您的问题似乎是这些行:

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

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

另一个有趣的事情是您的垃圾箱数据密钥。由于您正在使用,因此x0g还将根据直方图函数计算仓位的方式进入/退出。

<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>

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章