javascript - 面向对象的 d3

标签 javascript oop d3.js svg

我有要添加到 SVG 的数据对象。考虑以下伪代码段:

var data = [], counter = 0;
for (var col=1; col<=5; col++)
  for (var row=1; row<=3; row++)
    data.push({
         id: "obj-" + ++counter
        ,x: col * 120
        ,y: row * 120
        ,width: 40
        ,height: 40
        ,shape: counter % 2 ? "circle" : "rect"
    });

 d3.select(".container").selectAll(".obj")
     .data(data)
     .enter()
         .append("g")
             .attr("id", function(d){ return d.id; }
/*** 
     now I want to draw here a circle or rect based on the shape key 
     so if (d.shape == "rect") -- we will use width and height
        if (d.shape == "rect" && d.width == d.height) we will set "r" to "width", etc.
***/

理想情况下,我会创建一个 Shape 类型的对象,例如

function Shape(id, shape, x, y, w, h) {

    this.id = id;
    this.shape = shape;
    this.x = x;
    this.y = y;
    this.width = w;
    this.height = h;

    this.render = function(parent) {

        var g = parent.append("g")
            .attr("id", this.id);

        switch (this.shape) {
            case "circle":
                g.append("circle")
                    .attr( /* more code here */ )
                break;
            case "rect":
                g.append("rect")
                    .attr( /* more code here */ )
                break;
            case "triangle":
                g.append("polygon")
                    .attr( /* more code here */ )
                break;
        }
    }
}

然后我就可以做类似的事情了:

var data = [], counter = 0;
for (var col=1; col<=5; col++)
  for (var row=1; row<=3; row++)
    data.push(new Shape({
         id: "obj-" + ++counter
        ,x: col * 120
        ,y: row * 120
        ,width: 40
        ,height: 40
        ,shape: counter % 2 ? "circle" : "rect"
    )});

但是如何从 d3 调用 Shape 的 render() 方法呢?即

 d3.select(".container").selectAll(".obj")
     .data(data)
     .enter()
         /* given a datum named d, call d.render(parent) ? */

我是 d3 的新手,所以也许数据连接是错误的方式?是否有不同的方法来呈现更适合这种情况的数据项?

最佳答案

要让数据对象以面向对象的方式呈现自己,您可以求助于鲜为人知的用法 selection.append(name) .根据文档,您可以向 .append() 提供回调,它需要返回一个 DOM 元素以附加:

selection.append(name)

[…]

The name may be specified either as a constant string or as a function that returns the DOM element to append. If name is a function, it is passed the current datum d and the current index i, with the this context as the current DOM element. To append an arbitrary element based on the bound data it must be created in the function. For example:

selection.enter().append(function(d) {
    return document.createElementNS("http://www.w3.org/2000/svg", d.type)
})

为了这个问题的目的,这可能被修改为不在适当的位置创建元素,而是将创建委托(delegate)给数据对象的 .render() 方法。

d3.select(".container").selectAll("g")
  .data(data)
  .enter()
  .append(function(d) {
      return d.render();   // .render() will return the DOM element to append
  });

您的 .render() 方法可能如下所示:

this.render = function() {

    // Create the group.
    var g = document.createElementNS(d3.ns.prefix.svg, "g");
    g.setAttribute("id", this.id);

    // Create and configure the child element based on this.shape.
    var child;
    switch (this.shape) {
        case "circle":
            child = document.createElementNS(d3.ns.prefix.svg, "circle");
            child.setAttribute("cx", this.x);
            child.setAttribute("cy", this.y);
            child.setAttribute("r", this.width/2);
            break;

        case "rect":
            child = document.createElementNS(d3.ns.prefix.svg, "rect")
            break;

        case "triangle":
            child = document.createElementNS(d3.ns.prefix.svg, "polygon")
            break;
    }

    // Append the child to the group and return the g DOM element.
    g.appendChild(child);
    return g;
}

查看此代码段以获取工作示例:

function Shape(id, shape, x, y, w, h) {

    this.id = id;
    this.shape = shape;
    this.x = x;
    this.y = y;
    this.width = w;
    this.height = h;

    this.render = function() {

        // Create the group.
        var g = document.createElementNS(d3.ns.prefix.svg, "g");
        g.setAttribute("id", this.id);
        
        // Create and configure the child element based on this.shape.
        var child;
        switch (this.shape) {
            case "circle":
                child = document.createElementNS(d3.ns.prefix.svg, "circle");
                child.setAttribute("cx", this.x);
                child.setAttribute("cy", this.y);
                child.setAttribute("r", this.width/2);
                break;
                
            case "rect":
                child = document.createElementNS(d3.ns.prefix.svg, "rect")
                child.setAttribute("x", this.x);
                child.setAttribute("y", this.y);
                child.setAttribute("width", this.width);
                child.setAttribute("height", this.height);
                break;
                
            case "triangle":
                child = document.createElementNS(d3.ns.prefix.svg, "polygon")
                break;
        }
        
        // Append the child to the group and return the g DOM element.
        g.appendChild(child);
        return g;
    }
}

var data = [], counter = 0;
for (var col=1; col<=5; col++)
  for (var row=1; row<=3; row++)
    data.push(new Shape(
         "obj-" + ++counter
        ,counter % 2 ? "circle" : "rect"
        ,col * 120
        ,row * 120
        ,40
        ,40
    ));
    
console.log(data);

d3.select(".container").selectAll("g")
  .data(data)
  .enter()
  .append(function(d) {
    return d.render();   // .render() will return the DOM element to append
  });
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<svg width="400" height="400">
  <g class="container"></g>
</svg>

关于javascript - 面向对象的 d3,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31443468/

相关文章:

javascript - Rails - 禁用 js&css//=测试需要

c# - 为什么这个 Upcast 不起作用?

c++ - 创建用户所需的输入对象数 C++

javascript - Spotify API 的 POST 请求问题

javascript - 我想显示一条消息以确认删除

javascript - 有条件地操作数组中元素的属性

java - 类的序列化、演化

javascript - 发生拖动后运行函数?

javascript - 在 d3.js 中,如何在不进行新选择的情况下检查元素是否已被删除?

R2D3 与 D3.js 相比