简体   繁体   中英

Reusable functions in D3

I'm new to d3 and a pretty average javascript programmer. I've got some code to create a circle, and a function to get the x and y co-ordinates of a point on a circle:

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

It works. But I feel like continuing with this approach will make my code a messy grab bag of global functions, and that I should be able to make it object-oriented, so rather than calling:

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

I can instead call:

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

But this would involve me either attaching a pointAt function to a d3 object somehow, or creating my own Circle object that has a pointAt function, and wraps a d3 object. Is either of these a good idea?

There's other points where I feel like I'd want something similar - kind of like I want to be mapping 'objects' to documents, as opposed to plain old data to documents. Is this a common requirement, or am I missing something conceptually?

What's the best way to approach my pointOnCircle issue that I'm having above?

Most of the d3 examples are small, self-contained, and written in one single script. Are there any examples showing how to build something with more reusable functions?

You can follow d3.js style of functional programming as demonstrated below

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

This is a typical example of functional programming. The main object is _circle function is obtained by calling innerCircle . _circle draws a circle to an svg according to its set attributes ( current_attr , current_style ). To draw a circle to an svg, you can do it d3.js way:

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

The _circle function has 3 defined methods, attr , style and pointAt . attr and style are getter/setter functions, if you call them without arguments they will return the current value (getter), and if called with an argument, they will set the current value to it.

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

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

You can also call your pointAt function similarly.

new_circle.pointAt(Math.PI);

One last caveat to this programming style is the return _circle; statement at the end of all setter functions, which allows chaining. So, your example can be reproduced by:

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

svg.call(new_circle);

Hope this helps. Let me know of any unclear points.

I hope this helps.

 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> 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM