首页 > 解决方案 > d3.js 中的形状覆盖而不是过渡

问题描述

我试图让 beeswarm plot shift 数据点位置无济于事。首先,我制作了一个简单的散点图,除了切换时的 x 轴覆盖之外,它可以转换。

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>D3: Transitioning points to randomized values, plus rescaled axes!</title>
        <script src="https://d3js.org/d3.v4.min.js"></script><style type="text/css">
            /* No style rules here yet */
        </style>
    </head>
    <body>
    <label>
        <input type="radio" name='market' value="a" checked/>a 
        <input type="radio" name='market' value="b"/>b
</label> 
        <p>Click on this text to update the chart with new data values as many times as you like!</p>

        <script type="text/javascript">

            //Width and height
            var w = 500;
            var h = 300;
            var padding = 30;

            //Dynamic, random dataset
            dataset = [
                {"id":1, 
                "value":20,
                "group":"a"},
                {"id":1, 
                "value":10,
                "group":"a"},
                {"id":1, 
                "value":30,
                "group":"a"},
                {"id":1, 
                "value":40,
                "group":"a"},
                {"id":1, 
                "value":42,
                "group":"a"},
                {"id":1, 
                "value":10,
                "group":"b"},
                {"id":1, 
                "value":12,
                "group":"b"},
                {"id":1, 
                "value":15,
                "group":"b"},
                {"id":1, 
                "value":23,
                "group":"b"},
                {"id":1, 
                "value":22,
                "group":"b"},
                {"id":1, 
                "value":54,
                "group":"b"}
                ]


            //Create scale functions
            var xScale = d3.scaleLinear()
                                 .range([padding, w - padding * 2]);

            var yScale = d3.scaleLinear()
                                 .range([h - padding, padding]);

            //Define X axis
            var xAxis = d3.axisBottom()
                              .scale(xScale)
                              .ticks(5);

            //Define Y axis
            var yAxis = d3.axisLeft()
                              .scale(yScale)
                              .ticks(5);

            //Create SVG element
            var svg = d3.select("body")
                        .append("svg")
                        .attr("width", w)
                        .attr("height", h);
        draw(dataset.filter(d=> d.group=="a"));


      //////////////////
      //toggle//
      //////////////////
      d3.selectAll("input")
      .on("change",  function() {
          var data_new = dataset.filter(d => (d.group == this.value));
          draw(data_new);
          });

      //////////////////
      //Create circles
      //////////////////
    function draw(dataset) {
         //////////////////
        //Create axis


        xScale.domain([0, d3.max(dataset, function(d) { return d.value; })])
        yScale.domain([0, 2])
        var xAxis = d3.axisBottom()
                  .scale(xScale)
                  .ticks(5);
        //Create X axis
        svg.append("g")
            .attr("class", "x axis")
            .attr("transform", "translate(0," + (h - padding) + ")")
     .transition(2000)
            .call(xAxis);

        //Create Y axis
        svg.append("g")
            .attr("class", "y axis")
            .attr("transform", "translate(" + padding + ",0)")
            .call(yAxis);
        //////////////////
        //draw circle

        svg.selectAll("circle")
               .data(dataset)
               .enter()
               .append("circle")
               .attr("cx", function(d) {
                    return xScale(d.value);
               })
               .attr("cy", function(d) {
                    return yScale(d.id);
               })
               .attr("r", 3)
         .attr("opacity",0.2);

        //update
        svg.selectAll("circle")
        .transition()
                       .duration(1000)  
        .attr("cx", function(d) {
                    return xScale(d.value);
               })
      }


        </script>
    </body>
</html>

然而,当对 beeswarm 绘图应用类似的代码时,这些点不会在选择时移动位置,它们只是分层,就像第一个示例中的 x 轴:

<!DOCTYPE html>
<meta charset="utf-8">
<style>
</style>
<label>
        <input type="radio" name='market' value="a" checked/>a 
        <input type="radio" name='market' value="b"/>b
</label>

<svg width="400" height="200"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>

var svg = d3.select("svg"),
    margin = {top: 40, right: 40, bottom: 40, left: 40},
    width = svg.attr("width") - margin.left - margin.right,
    height = svg.attr("height") - margin.top - margin.bottom;

var formatValue = d3.format(",d");

var x = d3.scaleLog()
    .rangeRound([0, width]);

var g = svg.append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

data = [
  {"id":1, 
  "value":20,
  "group":"a"},
  {"id":1, 
  "value":21,
  "group":"a"},
  {"id":1, 
  "value":30,
  "group":"a"},
  {"id":1, 
  "value":32,
  "group":"a"},
  {"id":1, 
  "value":42,
  "group":"a"},
   {"id":1, 
  "value":10,
  "group":"b"},
  {"id":1, 
  "value":12,
  "group":"b"},
  {"id":1, 
  "value":15,
  "group":"b"},
  {"id":1, 
  "value":23,
  "group":"b"},
  {"id":1, 
  "value":22,
  "group":"b"},
  {"id":1, 
  "value":24,
  "group":"b"}
]
  //default
  draw(data.filter(d=> d.group=="a"));

  d3.selectAll("input")
  .on("change", function()
{
  var newdata = draw(data.filter(d=> d.group==this.value));
  draw(newdata)

}  )


  /////////////////
//draw swarmplot
/////////////////

function draw(data) {

  // transition
  var t = d3.transition()
          .duration(750);


  x.domain(d3.extent(data, d=> d.value));

  var simulation = d3.forceSimulation(data)
      .force("x", d3.forceX(function(d) { return x(d.value); }).strength(1))
      .force("y", d3.forceY(height / 3))
      .force("collide", d3.forceCollide(18))
      .stop();

  for (var i = 0; i < 120; ++i) simulation.tick();

  //axis
  g.append("g")

      .attr("class", "axis axis--x")
      .attr("transform", "translate(0," + height + ")")
      .call(d3.axisBottom(x).ticks(20, ".0s"));

  //for mouse-over
  var cell = g.append("g")
      .attr("class", "cells")
    .selectAll("g").data(d3.voronoi()
        .extent([[-margin.left, -margin.top], [width + margin.right, height + margin.top]])
        .x(function(d) { return d.x; })
        .y(function(d) { return d.y; })
      .polygons(data)).enter().append("g");

  //circle
  cell.append("circle")
      .attr("r", 3)
      .attr("cx", function(d) { return d.data.x; })
      .attr("cy", function(d) { return d.data.y; })
      .attr("fill", d => (d.data.Food_Sub_Group))
      .attr("opacity", 0.4);
 //update circle
  cell.selectAll("circle")
        .transition()
                       .duration(1000)  
        .attr("cx", function(d) { return d.data.x; })

} 


</script>

这里有什么问题吗?谢谢。

标签: d3.jstransition

解决方案


这是区别:

在第一个代码的绘制函数中,您将数据绑定到“圆形”元素。如果之前有 5 个圆形元素,新数据大小为 5,则不添加任何元素,改变的只是数据。如果您之前有 5 个圆圈,并且新数据大小为 6,那么使用 'enter()' 和 'append' 函数,执行后,您将有 6 个元素。

在第二个代码的绘制函数中,您添加了新元素。每次调用 draw 函数都会增加新的点。如果您之前有 5 个圆形元素并且新数据大小为 5,那么添加 5 个元素,您将有 10 个元素。

您所说的“无移位位置”实际上是在同一个地方(该点似乎更暗)或不同的地方(更多点)绘制多个点。

请参阅d3 中的数据绑定,这可能会有所帮助。


推荐阅读