D3 中的可重用函数

Reusable functions in D3

我是 d3 的新手,是一个相当普通的 javascript 程序员。我有一些代码可以创建一个圆,还有一个函数可以获取圆上某个点的 x 和 y 坐标:

var innerCircle = svg.append('circle')
  .attr({
    cx: 100,
    cy: 100,
    r: 50,
    'stroke': 'white',
    'fill': 'transparent',
  });

var pointOnCircle = function(circle, radians){
  var cx = parseInt(circle.attr('cx'));
  var cy = parseInt(circle.attr('cy'));
  var r = parseInt(circle.attr('r'));
  var x = cx + Math.sin(radians) * r;
  var y = cy + Math.cos(radians) * r;
  return {x: x, y: y};
}

有效。但我觉得继续使用这种方法会使我的代码成为一个混乱的全局函数包,而且我应该能够使其面向对象,而不是调用:

var point = pointOnCircle(circle, Math.PI);

我可以改为调用:

var point = circle.pointAt(Math.PI);

但这将涉及我以某种方式将 pointAt 函数附加到 d3 对象,或者创建我自己的 Circle 具有 pointAt 函数并包装 d3 对象的对象.这些都是好主意吗?

还有其他一些地方我觉得我想要类似的东西 - 有点像我想将 'objects' 映射到文档,而不是将普通的旧数据映射到文档。这是一个普遍的要求,还是我在概念上遗漏了什么?

解决我遇到的 pointOnCircle 问题的最佳方法是什么?

大多数 d3 示例都是小型的、独立的,并且是用一个脚本编写的。是否有任何示例说明如何构建具有更多可重用功能的东西?

希望对您有所帮助。

var myProgram = {};
myProgram.circleModule = (function() {
  var innerCircle = d3.select("#svg").append('circle')
    .attr({
      cx: 100,
      cy: 100,
      r: 50,
      'stroke': 'black',
      'fill': 'red',
    });

  var pointOnCircle = function(circle, radians) {
    var cx = parseInt(circle.attr('cx'));
    var cy = parseInt(circle.attr('cy'));
    var r = parseInt(circle.attr('r'));
    var x = cx + Math.sin(radians) * r;
    var y = cy + Math.cos(radians) * r;
    return {
      x: x,
      y: y
    };
  }

  return {
    circle: innerCircle,
    pointOnCircle: pointOnCircle
  }
})();
<!DOCTYPE html>
<html>

<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <title>Untitled Document</title>
  <script src="http://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
</head>

<body>
  <svg id="svg" width="200" height="200">

  </svg>

您可以遵循 d3.js 函数式编程风格,如下所示

function innerCircle() {
    var current_attr, current_style, circle_elem;

    var _circle = function (svg) {
        circle_elem = svg.append('circle')
            .attr(current_attr)
            .attr(current_style);

        return circle_elem;
    }

    _circle.pointAt = function (randians) {
        if(! circle_elem)       //If the circle is not drawn yet.
            return {x: -1, y: -1};

      var cx = parseInt(circle_elem.attr('cx'));
      var cy = parseInt(circle_elem.attr('cy'));
      var r = parseInt(circle_elem.attr('r'));
      var x = cx + Math.sin(radians) * r;
      var y = cy + Math.cos(radians) * r;
      return {x: x, y: y};
    }

    _circle.attr = function(attr_val){
        if(! arguments.length)
            return current_attr;

        current_attr = attr_val;
        return _circle;
    }

    _circle.style = function(style_val){
        if(arguments.length == 1)
            return current_style;

        current_style = style_val;
        return _circle;
    }

    return _circle;
}

这是函数式编程的典型例子。主要对象是_circle函数,通过调用innerCircle获得。 _circle 根据其设置的属性(current_attrcurrent_style)绘制一个圆到svg。要在 svg 上画一个圆,你可以 d3.js 方法:

var new_circle = innerCircle();
svg.call( new_circle );

_circle 函数有 3 个定义的方法,attrstylepointAtattrstyle 是 getter/setter 函数,如果您不带参数调用它们,它们将 return 当前值 (getter),如果带参数调用,他们将为其设置当前值。

new_circle.style(); //get the current style

//set attributes
new_circle.attr({
    cx: 100,
    cy: 100,
    r: 50,
    'stroke': 'white',
    'fill': 'transparent',
});

您也可以类似地调用您的 pointAt 函数。

new_circle.pointAt(Math.PI);

这种编程风格的最后一个警告是所有 setter 函数末尾的 return _circle; 语句,它允许链接。因此,您的示例可以通过以下方式复制:

var new_circle = innerCircle()
  .attr({
    cx: 100,
    cy: 100,
    r: 50,    
  })
  .style({
    'stroke': 'white',
    'fill': 'transparent',
  });

svg.call(new_circle);

希望这对您有所帮助。让我知道任何不清楚的地方。