【问题标题】:Defining custom D3 symbols定义自定义 D3 符号
【发布时间】:2019-01-29 16:13:52
【问题描述】:

我正在尝试在 D3 中开发一个自定义形状,但我有点不懂 Javascript,我不确定 D3-Shapes 库在后台做什么。

特别是,我不确定symbol.js 中的default 函数在做什么或如何使用它。从我所知道的一点点 JS 来看,它似乎是在描述一个原型或可能是一个类。据说可以使用symbol.type 类/函数注册其他形状,但我不确定我应该如何调用它,例如var symbol = symbol("SYMBOL").type(SYMBOL) 或可能是 var SYMBOL = symbol().type(SYMBOL) 或其他一些组合。 Issue 23 讨论了形状库如何从原始实现发生变化,我认为这否定了SO: answer。我还搜索了D3-Symbol-Extra 以寻找线索,但没有骰子。

到目前为止,我有一个超椭圆的以下形状定义,它与 D3 形状库中的符号定义相匹配,例如circlediamond

var superellipse = {
 draw : function (context, size) {
  var w = size;
  var h = size;
  var n = 2/4;
  context.moveTo(w/2*Math.sign(Math.cos( 0/9*2*Math.PI))*Math.pow(Math.abs(Math.cos( 0/9*2*Math.PI)), n), h/2*Math.sign(Math.sin( 0/9*2*Math.PI))*Math.pow(Math.abs(Math.sin( 0/9*2*Math.PI)), n));
  context.lineTo(w/2*Math.sign(Math.cos( 1/9*2*Math.PI))*Math.pow(Math.abs(Math.cos( 1/9*2*Math.PI)), n), h/2*Math.sign(Math.sin( 1/9*2*Math.PI))*Math.pow(Math.abs(Math.sin( 1/9*2*Math.PI)), n));
  context.lineTo(w/2*Math.sign(Math.cos( 2/9*2*Math.PI))*Math.pow(Math.abs(Math.cos( 2/9*2*Math.PI)), n), h/2*Math.sign(Math.sin( 2/9*2*Math.PI))*Math.pow(Math.abs(Math.sin( 2/9*2*Math.PI)), n));
  context.lineTo(w/2*Math.sign(Math.cos( 3/9*2*Math.PI))*Math.pow(Math.abs(Math.cos( 3/9*2*Math.PI)), n), h/2*Math.sign(Math.sin( 3/9*2*Math.PI))*Math.pow(Math.abs(Math.sin( 3/9*2*Math.PI)), n));
  context.lineTo(w/2*Math.sign(Math.cos( 4/9*2*Math.PI))*Math.pow(Math.abs(Math.cos( 4/9*2*Math.PI)), n), h/2*Math.sign(Math.sin( 4/9*2*Math.PI))*Math.pow(Math.abs(Math.sin( 4/9*2*Math.PI)), n));
  context.lineTo(w/2*Math.sign(Math.cos( 5/9*2*Math.PI))*Math.pow(Math.abs(Math.cos( 5/9*2*Math.PI)), n), h/2*Math.sign(Math.sin( 5/9*2*Math.PI))*Math.pow(Math.abs(Math.sin( 5/9*2*Math.PI)), n));
  context.lineTo(w/2*Math.sign(Math.cos( 6/9*2*Math.PI))*Math.pow(Math.abs(Math.cos( 6/9*2*Math.PI)), n), h/2*Math.sign(Math.sin( 6/9*2*Math.PI))*Math.pow(Math.abs(Math.sin( 6/9*2*Math.PI)), n));
  context.lineTo(w/2*Math.sign(Math.cos( 7/9*2*Math.PI))*Math.pow(Math.abs(Math.cos( 7/9*2*Math.PI)), n), h/2*Math.sign(Math.sin( 7/9*2*Math.PI))*Math.pow(Math.abs(Math.sin( 7/9*2*Math.PI)), n));
  context.lineTo(w/2*Math.sign(Math.cos( 8/9*2*Math.PI))*Math.pow(Math.abs(Math.cos( 8/9*2*Math.PI)), n), h/2*Math.sign(Math.sin( 8/9*2*Math.PI))*Math.pow(Math.abs(Math.sin( 8/9*2*Math.PI)), n));
  context.lineTo(w/2*Math.sign(Math.cos( 9/9*2*Math.PI))*Math.pow(Math.abs(Math.cos( 9/9*2*Math.PI)), n), h/2*Math.sign(Math.sin( 9/9*2*Math.PI))*Math.pow(Math.abs(Math.sin( 9/9*2*Math.PI)), n));
  context.closePath();
  }
};

我可以根据默认大小绘制要绘制的形状,但是我无法通过设置r 属性来调整圆形的尺寸;在这种情况下,通过设置 nhw 属性。

var svg = d3.select("svg")
    .attr("width", 500)
    .attr("height", 500);

function visualize(json) {
    var nodes = svg.selectAll("g nodetext")
        .data(json.nodes);

    var entryPoint = nodes.enter()
        .append("g")
        .attr("class", "node")
        .attr("transform", function(d) {
            return "translate(" + d.x + ",80)"
        });

    // var ellipse = entryPoint.append("circle")
    //               .attr("r", 50);

    var nodeShape = entryPoint.append("path")
        .attr("d", d3.symbol().type(superellipse))
        .style("stroke", "gray")
        .attr("fill", "pink")
        // .n(function(d) {return d.n;})
        .attr("w", function(d) {
            return d.w;
        })
        .attr("h", function(d) {
            return d.h;
        });

    var nodeText = entryPoint.append("text")
        .text(function(d) {
            return d.text;
        })
        .attr({
            "text-anchor": "middle"
        });
};

明确地说,我按如下方式调用它:

var superellipse = {
  draw: function(context, size) {
    var w = size;
    var h = size;
    var n = 2 / 4;
    context.moveTo(w / 2 * Math.sign(Math.cos(0 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.cos(0 / 9 * 2 * Math.PI)), n), h / 2 * Math.sign(Math.sin(0 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.sin(0 / 9 * 2 * Math.PI)), n));
    context.lineTo(w / 2 * Math.sign(Math.cos(1 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.cos(1 / 9 * 2 * Math.PI)), n), h / 2 * Math.sign(Math.sin(1 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.sin(1 / 9 * 2 * Math.PI)), n));
    context.lineTo(w / 2 * Math.sign(Math.cos(2 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.cos(2 / 9 * 2 * Math.PI)), n), h / 2 * Math.sign(Math.sin(2 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.sin(2 / 9 * 2 * Math.PI)), n));
    context.lineTo(w / 2 * Math.sign(Math.cos(3 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.cos(3 / 9 * 2 * Math.PI)), n), h / 2 * Math.sign(Math.sin(3 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.sin(3 / 9 * 2 * Math.PI)), n));
    context.lineTo(w / 2 * Math.sign(Math.cos(4 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.cos(4 / 9 * 2 * Math.PI)), n), h / 2 * Math.sign(Math.sin(4 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.sin(4 / 9 * 2 * Math.PI)), n));
    context.lineTo(w / 2 * Math.sign(Math.cos(5 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.cos(5 / 9 * 2 * Math.PI)), n), h / 2 * Math.sign(Math.sin(5 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.sin(5 / 9 * 2 * Math.PI)), n));
    context.lineTo(w / 2 * Math.sign(Math.cos(6 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.cos(6 / 9 * 2 * Math.PI)), n), h / 2 * Math.sign(Math.sin(6 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.sin(6 / 9 * 2 * Math.PI)), n));
    context.lineTo(w / 2 * Math.sign(Math.cos(7 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.cos(7 / 9 * 2 * Math.PI)), n), h / 2 * Math.sign(Math.sin(7 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.sin(7 / 9 * 2 * Math.PI)), n));
    context.lineTo(w / 2 * Math.sign(Math.cos(8 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.cos(8 / 9 * 2 * Math.PI)), n), h / 2 * Math.sign(Math.sin(8 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.sin(8 / 9 * 2 * Math.PI)), n));
    context.lineTo(w / 2 * Math.sign(Math.cos(9 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.cos(9 / 9 * 2 * Math.PI)), n), h / 2 * Math.sign(Math.sin(9 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.sin(9 / 9 * 2 * Math.PI)), n));
    context.closePath();
  }
};

var svg = d3.select("svg")
  .attr("width", 500)
  .attr("height", 500);

function visualize(json) {
  var nodes = svg.selectAll("g nodetext")
    .data(json.nodes);

  var entryPoint = nodes.enter()
    .append("g")
    .attr("class", "node")
    .attr("transform", function(d) {
      return "translate(" + d.x + ",80)"
    });

  // var ellipse = entryPoint.append("circle")
  //               .attr("r", 50);

  var nodeShape = entryPoint.append("path")
    .attr("d", d3.symbol().type(superellipse))
    .style("stroke", "gray")
    .attr("fill", "pink")
    // .n(function(d) {return d.n;})
    .attr("w", function(d) {
      return d.w;
    })
    .attr("h", function(d) {
      return d.h;
    });

  var nodeText = entryPoint.append("text")
    .text(function(d) {
      return d.text;
    })
    .attr({
      "text-anchor": "middle"
    });
}

var json = {
  "nodes": [{
      "text": "test A",
      "x": 100,
      "y": 100,
      "w": 200,
      "n": 0.5,
      "h": 200,
      "color": "red"
    },
    {
      "text": "test B",
      "x": 200,
      "y": 200,
      "w": 200,
      "n": 3,
      "h": 100,
      "color": "red"
    },
  ]
};
visualize(json);
<script src="https://d3js.org/d3.v5.min.js"></script>
<svg></svg>

【问题讨论】:

    标签: javascript d3.js svg html5-canvas


    【解决方案1】:

    你声称你...

    无法通过设置 r 属性来调整圆的尺寸;在这种情况下,通过设置 n、h 和 w 属性。

    好吧,在您的情况下,您可以使用symbol.size() 传递一个属性。比如100:

    .attr("d", d3.symbol().type(superellipse).size(100))
    

    但是,问题在于symbol.size() 将只传递一个值:您不能尝试传递多个参数、数组、对象或类似的任何东西。因此,在上面的示例中,whn 将是 100

    如果您检查symbol.size()source code,您会看到:

    symbol.size = function(_) {
        return arguments.length ? 
            (size = typeof _ === "function" ? _ : constant(+_), symbol) : size;
    };
    

    那个一元加号告诉我们,如果你尝试传递一个数组或一个对象,你会得到一个NaN,如果你传递了多个值,它们将被简单地忽略。

    那么,我们该如何解决呢?

    任务的正确工具

    最后,在我看来,您的问题只是您为此任务使用了错误的通行费。不需要符号生成器,可以使用通用函数生成&lt;path&gt;元素的d属性。

    例如:

    function superellipse (w, h, n){
        return "M" + (w / 2 * Math.sign(Math.cos(0 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.cos(0 / 9 * 2 * Math.PI)), n) + "," + (h / 2 * Math.sign(Math.sin(0 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.sin(0 / 9 * 2 * Math.PI)), n)))
            + " L" + (w / 2 * Math.sign(Math.cos(1 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.cos(1 / 9 * 2 * Math.PI)), n) + "," + (h / 2 * Math.sign(Math.sin(1 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.sin(1 / 9 * 2 * Math.PI)), n)))
            + " L" + (w / 2 * Math.sign(Math.cos(2 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.cos(2 / 9 * 2 * Math.PI)), n) + "," + (h / 2 * Math.sign(Math.sin(2 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.sin(2 / 9 * 2 * Math.PI)), n)))
            + " L" + (w / 2 * Math.sign(Math.cos(3 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.cos(3 / 9 * 2 * Math.PI)), n) + "," + (h / 2 * Math.sign(Math.sin(3 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.sin(3 / 9 * 2 * Math.PI)), n)))
            + " L" + (w / 2 * Math.sign(Math.cos(4 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.cos(4 / 9 * 2 * Math.PI)), n) + "," + (h / 2 * Math.sign(Math.sin(4 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.sin(4 / 9 * 2 * Math.PI)), n)))
            + " L" + (w / 2 * Math.sign(Math.cos(5 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.cos(5 / 9 * 2 * Math.PI)), n) + "," + (h / 2 * Math.sign(Math.sin(5 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.sin(5 / 9 * 2 * Math.PI)), n)))
            + " L" + (w / 2 * Math.sign(Math.cos(6 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.cos(6 / 9 * 2 * Math.PI)), n) + "," + (h / 2 * Math.sign(Math.sin(6 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.sin(6 / 9 * 2 * Math.PI)), n)))
            + " L" + (w / 2 * Math.sign(Math.cos(7 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.cos(7 / 9 * 2 * Math.PI)), n) + "," + (h / 2 * Math.sign(Math.sin(7 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.sin(7 / 9 * 2 * Math.PI)), n)))
            + " L" + (w / 2 * Math.sign(Math.cos(8 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.cos(8 / 9 * 2 * Math.PI)), n) + "," + (h / 2 * Math.sign(Math.sin(8 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.sin(8 / 9 * 2 * Math.PI)), n)))
            + " L" + (w / 2 * Math.sign(Math.cos(9 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.cos(9 / 9 * 2 * Math.PI)), n) + "," + (h / 2 * Math.sign(Math.sin(9 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.sin(9 / 9 * 2 * Math.PI)), n)));
    };
    

    这样你就可以得到datum对象中定义的whn属性,并毫无问题地将它们传递给函数。

    这是一个包含您的数据的演示:

    function superellipse (w, h, n){
    	return "M" + (w / 2 * Math.sign(Math.cos(0 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.cos(0 / 9 * 2 * Math.PI)), n) + "," + (h / 2 * Math.sign(Math.sin(0 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.sin(0 / 9 * 2 * Math.PI)), n)))
    		+ " L" + (w / 2 * Math.sign(Math.cos(1 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.cos(1 / 9 * 2 * Math.PI)), n) + "," + (h / 2 * Math.sign(Math.sin(1 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.sin(1 / 9 * 2 * Math.PI)), n)))
    		+ " L" + (w / 2 * Math.sign(Math.cos(2 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.cos(2 / 9 * 2 * Math.PI)), n) + "," + (h / 2 * Math.sign(Math.sin(2 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.sin(2 / 9 * 2 * Math.PI)), n)))
    		+ " L" + (w / 2 * Math.sign(Math.cos(3 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.cos(3 / 9 * 2 * Math.PI)), n) + "," + (h / 2 * Math.sign(Math.sin(3 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.sin(3 / 9 * 2 * Math.PI)), n)))
    		+ " L" + (w / 2 * Math.sign(Math.cos(4 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.cos(4 / 9 * 2 * Math.PI)), n) + "," + (h / 2 * Math.sign(Math.sin(4 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.sin(4 / 9 * 2 * Math.PI)), n)))
    		+ " L" + (w / 2 * Math.sign(Math.cos(5 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.cos(5 / 9 * 2 * Math.PI)), n) + "," + (h / 2 * Math.sign(Math.sin(5 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.sin(5 / 9 * 2 * Math.PI)), n)))
    		+ " L" + (w / 2 * Math.sign(Math.cos(6 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.cos(6 / 9 * 2 * Math.PI)), n) + "," + (h / 2 * Math.sign(Math.sin(6 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.sin(6 / 9 * 2 * Math.PI)), n)))
    		+ " L" + (w / 2 * Math.sign(Math.cos(7 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.cos(7 / 9 * 2 * Math.PI)), n) + "," + (h / 2 * Math.sign(Math.sin(7 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.sin(7 / 9 * 2 * Math.PI)), n)))
    		+ " L" + (w / 2 * Math.sign(Math.cos(8 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.cos(8 / 9 * 2 * Math.PI)), n) + "," + (h / 2 * Math.sign(Math.sin(8 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.sin(8 / 9 * 2 * Math.PI)), n)))
    		+ " L" + (w / 2 * Math.sign(Math.cos(9 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.cos(9 / 9 * 2 * Math.PI)), n) + "," + (h / 2 * Math.sign(Math.sin(9 / 9 * 2 * Math.PI)) * Math.pow(Math.abs(Math.sin(9 / 9 * 2 * Math.PI)), n)));
    };
    
    var svg = d3.select("svg")
      .attr("width", 500)
      .attr("height", 500);
    
    function visualize(json) {
      var nodes = svg.selectAll("g nodetext")
        .data(json.nodes);
    
      var entryPoint = nodes.enter()
        .append("g")
        .attr("class", "node")
        .attr("transform", function(d) {
          return "translate(" + d.x + "," + d.y + ")"
        });
    
      var nodeShape = entryPoint.append("path")
        .attr("d", function(d){
        	return superellipse(d.w, d.h, d.n)
        })
        .style("stroke", "gray")
        .attr("fill", "pink");
    
      var nodeText = entryPoint.append("text")
        .text(function(d) {
          return d.text;
        })
        .attr({
          "text-anchor": "middle"
        });
    }
    
    var json = {
      "nodes": [{
          "text": "test A",
          "x": 100,
          "y": 100,
          "w": 200,
          "n": 0.5,
          "h": 200,
          "color": "red"
        },
        {
          "text": "test B",
          "x": 300,
          "y": 100,
          "w": 200,
          "n": 3,
          "h": 100,
          "color": "red"
        },
      ]
    };
    visualize(json);
    <script src="https://d3js.org/d3.v5.min.js"></script>
    <svg></svg>

    【讨论】:

    • 哦,太好了,这对我很有帮助,谢谢 :D(我的 JavaScript 有点垃圾)。尽管我仍然不确定如何将符号集成为 D3 形状,但我的意思是编写 D3-Symbols-Extra 库的人似乎已经解决了一些问题,但他们使用的是与 D3 相同的魔法,并且我不完全了解该机制是如何工作的。他们如何使用symbol.js 中的default 函数,以及如何使用symbol.type 根据引用的D3 文档注册形状。
    猜你喜欢
    • 1970-01-01
    • 2020-03-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-08
    相关资源
    最近更新 更多