javascript - 使用 d3.js 的多个圆/线

标签 javascript json d3.js

我有一个 d3.js 问题并且已经为此苦苦挣扎了一段时间,但似乎无法解决它。我相信这很简单,但缺少一些非常基本的东西。

具体来说,我有以下代码,它为 JSON 中的第一个条目生成一条线和两个圆圈 - 我已经为第一个条目“硬编码”了它。

我现在想将 JSON 文件的第二个和第三个条目添加到图形中,并控制线条和圆圈的颜色,然后概括代码。

通过阅读文档和 StackOverflow,似乎正确的方法是使用嵌套,但我似乎无法让它工作?

代码在以下 URL 的 jsfiddle 上,javascript 在下面。

http://jsfiddle.net/GVmVk/

        // INPUT
        dataset2 = 
        [
            {   
                movie : "test",     
                results : 
                [
                    { week: "20130101", revenue: "60"},
                    { week: "20130201", revenue: "80"}
                ]
            },
            {
                movie : "beta",     
                results : 
                [
                    { week: "20130101", revenue: "40"},
                    { week: "20130201", revenue: "50"}
                ]
            },
            {
                movie : "gamm",     
                results : 
                [
                    { week: "20130101", revenue: "10"},
                    { week: "20130201", revenue: "20"}
                ]
            }           
        ];


        console.log("1");

        var parseDate = d3.time.format("%Y%m%d").parse;

        var lineFunction = d3.svg.line()
            .x(function(d) { return xScale(parseDate(String(d.week))); })
            .y(function(d) { return yScale(d.revenue); })
            .interpolate("linear");

        console.log("2");

        //SVG Width and height
        var w = 750;
        var h = 250;


        //X SCALE AND AXIS STUFF

        //var xMin = 0;
        //var xMax = 1000;

        var xScale = d3.time.scale()
            .domain([parseDate("20130101"),parseDate("20131231")])
            .range([0, w]);

        console.log(parseDate("20130101"));
        console.log("3");

        var xAxis = d3.svg.axis()
              .scale(xScale)
              .orient("bottom");

        console.log("4S");

        //Y SCALE AND AXIS STUFF

        var yScale = d3.scale.linear()
                 .domain([0, 100])
                 .range([h, 0]);

        var yAxis = d3.svg.axis()
              .scale(yScale)
              .orient("left")
              .ticks(5);

        //Create SVG element
        var svg = d3.select("body")
                    .append("svg")
                    .attr("width", w)
                    .attr("height", h);

        console.log("4S1");

        //CREATE X-AXIS
        svg.append("g")
            .attr("class", "axis") 
            .attr("transform", "translate(0," + (h - 30) + ")")
            .call(xAxis);

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



        svg.selectAll("circle")
            .data(dataset2[0].results)
           .enter()
            .append("circle")
            .attr("cx", function(d) {
    //      console.log(d[0]);
            console.log(parseDate(d.week));
            return xScale(parseDate(d.week));
        })
        .attr("cy", function (d) {
            return yScale(d.revenue);
        })
        .attr("r", 3);    



        //create line
        var lineGraph = svg.append("path")
            .attr("d", lineFunction(dataset2[0].results))
            .attr("class", "line");

最佳答案

“嵌套”一词出现在 d3 的两个上下文中——使用 d3.nest 创建嵌套数据数组,并使用嵌套数据创建嵌套选择。

您的数据已经采用了正确的嵌套选择格式——一个对象数组,每个对象都有一个由单个数据点组成的子数组。因此,您无需担心操纵数据,您只需直接将数据连接到嵌套的 d3 选择中的元素:

我将带您快速完成它,但以下教程将是 future 很好的引用:

关于您的示例:您有一个顶级数据结构,它是一个电影对象数组,每个对象都包含一个每周收入值的子数组。您需要决定的第一件事是您希望哪种类型的元素与每个级别的数据相关联。您正在为子数组中的数据绘制一条线和一组圆圈,但目前没有为顶级数组对象(电影)添加任何内容。您需要为它们添加一些东西 以便嵌套选择起作用,并且它需要是可以包含您的线和圆的东西。在 SVG 中,这几乎总是一个 <g>。 (分组)元素。

有效地创建一个 <g>数据数组中每个对象的元素——并将数据对象附加到元素以供将来引用——你创建一个选择,将你的数据加入其中,然后使用enter()数据连接选择的方法为每个与元素不匹配的数据对象添加元素。在这种情况下,由于我们没有任何元素可以开始,所以所有数据对象都将位于 enter() 中。选择。然而,同样的模式也适用于更新一些数据。

var movies = svg  //start with your svg selection, 
     //it will become the parent to the entering <g> elements
   .selectAll("g.movie") //select all <g> elements with class "movie" 
                         //that are children of the <svg> element
                         //contained in the `svg` selection
                         //this selection will currently be empty

   .data( dataset2 ); //join the selection to a data array
                     //each object in the array will be associated with 
                     //an element in the selection, if those elements exist

                     //This data-joined selection is now saved as `movies`

movies.enter() //create a selection for the data objects that didn't match elements
   .append("g") //add a new <g> element for each data object
   .attr("class", "movie") //set it's class to match our selection criteria

       //for each movie group, we're going to add *one* line (<path> element), 
       //and then a create subselection for the circles

   .append("path") //add a <path> within *each* new movie <g> element
                    //the path will *inherit* the data from the <g> element
      .attr("class", "line"); //set the class for your CSS

var lineGraph = movies.select("path.line")
       //All the entered elements are now available within the movies selection
       //(along with any existing elements that we were updating).
       //Using select("path") selects the first (and only) path within the group
       //regardless of whether we just created it or are updating it.

      .attr("d", function(d){ return lineFunction(d.results); });
       //the "d" attribute of a path describes its shape;
       //the lineFunction creates a "d" definition based on a data array.
       //If the data object attached to the path had *only* been the d.results array
       //we could have just done .attr("d", lineFunction), since d3
       //automatically passes the data object to any function given as the value
       //of an .attr(name, value) command.  Instead, we needed to create an
       //anonymous function to accept the data object and extract the sub-array.

var circles = movies.selectAll("circle")
         //there will be multiple circles for each movie group, so we need a 
         //sub-selection, created with `.selectAll`.
         //again, this selection will initially be empty.
       .data( function(d) {return d.results; });
         //for the circles, we define the data as a function
         //The function will be called once for each *movie* element,
         //and passed the movie element's data object.
         //The resulting array will be assigned to circles within that particular
         //movie element (or to an `enter()` selection, if the circles don't exist).

circles.enter() //get the data objects that don't have matching <circle> elements
    .append("circle") //create a circle for each
                      //the circles will be added to the appropriate "g.movie"
                      //because of the nested selection structure
    .attr("r", 3); //the radius doesn't depend on the data, 
                   //so you can set it here, when the circle is created,
                   //the same as you would set a class.

circles //for attributes that depend on the data, they are set on the entire
        //selection (including updating elements), after having created the 
        //newly entered circles.
    .attr("cx", function(d) { return xScale( parseDate(d.week) ); })
    .attr("cy", function(d) { return yScale( d.revenue ); });

带有其余代码的实时版本:http://jsfiddle.net/GVmVk/3/

您需要调整 x 尺度的域,以便第一个数据点不会被 chop ,并且您需要决定如何使用您的电影标题属性,但这应该让您去。

关于javascript - 使用 d3.js 的多个圆/线,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21731859/

相关文章:

javascript - 用于查找对象类型的 Mongodb 查询

javascript - 在 anchor 上执行 Javascript 的最佳方式

javascript - 使用 -webkit-animation 时 webkitTransitionEnd 不触发

javascript - 在 ajax 加载元素后初始化 bootstrap popover

php - 解决从 JSON 解码的 PHP 数组

javascript - 在 ionic 项目中显示 json 文件中的数据

javascript - 创建 d3 等值线数据集

javascript - 如何在 d3js 中按不同组对链接进行着色

javascript - D3 实时流图(图形数据可视化)

javascript - 全局信息窗口的文本输入