使用 D3.js 将图像放在旭日图的中心

Placing Image in the Center of a Sunburst Chart with D3.js

我几乎完成了可缩放旭日图的制作,我最不想做的就是将图像放在中心。我已经检查了其他 Whosebug 问题,但我无法找到可行的解决方案,因为我对 D3.js 或 svg 图形不是很有经验。
我在我正在使用的 JSON 文件中包含了一个图像 属性,但是中心是我想要图像的唯一位置。所以我一直在想,直接把图片放在D3.js画的canvas的中央会更好 如果有人对如何解决这个问题有任何建议或想法,我将不胜感激,谢谢。

这是 JSON 文件中的图像 属性:

{
 "name": "Wine Aroma Wheel",
 "description": "The Davis Wine Aroma Wheel is divided into several sections to help you visualize the different flavors, scents and aromatic qualities found in most red and white wines, regardless of the grape variety.  The same aromatic complexities and sensations pictured on the wine aroma wheel are found in red and white wines all over the world, from Bordeaux, to California and the Rhone Valley, as well as every other viticultural area.",
 "image": "images/Grape-2.jpg",
 "children": [
  {
. . .

这是我的 CSS3 代码:

circle,
path {
  cursor: pointer;
}

circle {
  fill: none;
  pointer-events: all;  
}

#tooltip { background-color: white;
              padding: 3px 5px;
              border: 1px solid black;
              text-align: center;}

html {
    font-family: sans-serif;
}

这里是旭日图的 JavaScript 代码:

var margin = {top: 650, right: 650, bottom: 650, left: 650},
    radius = Math.min(margin.top, margin.right, margin.bottom, margin.left) - 168;

function filter_min_arc_size_text(d, i) {return (d.dx*d.depth*radius/1)>14};

var hue = d3.scale.category10();

var luminance = d3.scale.sqrt()
    .domain([0, 1e6])
    .clamp(true)
    .range([80, 20]);

var svg = d3.select("body").append("svg")
    .attr("width", margin.left + margin.right)
    .attr("height", margin.top + margin.bottom)
    .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

var partition = d3.layout.partition()
    .sort(function(a, b) { return d3.ascending(a.name, b.name); })
    .size([2 * Math.PI, radius]);

var arc = d3.svg.arc()
    .startAngle(function(d) { return d.x; })
    .endAngle(function(d) { return d.x + d.dx - .01 / (d.depth + .5); })
    .innerRadius(function(d) { return (radius + 6) / 3 * d.depth; })
    .outerRadius(function(d) { return (radius + 6) / 3 * (d.depth + 1.) - 1; });

//Tooltip description
var tooltip = d3.select("body")
    .append("div")
    .attr("id", "tooltip")
    .style("position", "absolute")
    .style("z-index", "10")
    .style("opacity", 0);

function format_number(x) {
  return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}


function format_description(d) {
  var description = d.description;
      return  '<b>' + d.name + '</b></br>'+ d.description + '<br> (' + format_number(d.value) + ')';
}

function computeTextRotation(d) {
    var rotation = (d.x + d.dx / 2) * 180 / Math.PI - 90;
    return {
        global: rotation,
        correction: rotation > 90 ? 180 : 0
    };
}

function isRotated(d) {
    var rotation = (d.x + d.dx / 2) * 180 / Math.PI - 90;
    return rotation > 90 ? true : false
}

function mouseOverArc(d) {
             d3.select(this).attr("stroke","black")
          tooltip.html(format_description(d));
          return tooltip.transition()
            .duration(50)
            .style("opacity", 0.9);
        }

function mouseOutArc(){
    d3.select(this).attr("stroke","")
    return tooltip.style("opacity", 0);
}

function mouseMoveArc (d) {
          return tooltip
            .style("top", (d3.event.pageY-10)+"px")
            .style("left", (d3.event.pageX+10)+"px");
}

var root_ = null;
d3.json("data/davis-aroma-wheel.json", function(error, root) {
  if (error) return console.warn(error);
  // Compute the initial layout on the entire tree to sum sizes.
  // Also compute the full name and fill color for each node,
  // and stash the children so they can be restored as we descend.

  partition
      .value(function(d) { return d.size; })
      .nodes(root)
      .forEach(function(d) {
        d._children = d.children;
        d.sum = d.value;
        d.key = key(d);
        d.fill = fill(d);
      });

  // Now redefine the value function to use the previously-computed sum.
  partition
      .children(function(d, depth) { return depth < 3 ? d._children : null; })
      .value(function(d) { return d.sum; });

  var center = svg.append("circle")
      .attr("r", radius / 3)
      .on("click", zoomOut);

  center.append("title")
      .text("Zoom Out");


  var partitioned_data = partition.nodes(root).slice(1)

  var path = svg.selectAll("path")
      .data(partitioned_data)
      .enter().append("path")
      .attr("d", arc)
      .style("fill", function(d) { return d.fill; })
      .each(function(d) { this._current = updateArc(d); })
      .on("click", zoomIn)
          .on("mouseover", mouseOverArc)
      .on("mousemove", mouseMoveArc)
      .on("mouseout", mouseOutArc);

  var texts = svg.selectAll("text")
      .data(partitioned_data)
      .enter().append("text")
          .filter(filter_min_arc_size_text)
      .attr("transform", function(d) 
        {
          var r = computeTextRotation(d);
          return "rotate(" + r.global + ")"
            + "translate(" + radius / 3. * d.depth + ")"
            + "rotate(" + -r.correction + ")";
        })
      .style("font-weight", "bold")
      .style("text-anchor", "middle")
          .attr("dx", function(d) {return isRotated(d) ? "-85" : "85"}) //margin
      .attr("dy", ".35em") // vertical-align
      .on("click", zoomIn)
          .text(function(d,i) {return d.name})

  function zoomIn(p) {
    if (p.depth > 1) p = p.parent;
    if (!p.children) return;
    zoom(p, p);
  }

  function zoomOut(p) {
    if (!p.parent) return;
    zoom(p.parent, p);
  }

  // Zoom to the specified new root.
  function zoom(root, p) {
    if (document.documentElement.__transition__) return;

    // Rescale outside angles to match the new layout.
    var enterArc,
        exitArc,
        outsideAngle = d3.scale.linear().domain([0, 2 * Math.PI]);

    function insideArc(d) {
      return p.key > d.key
          ? {depth: d.depth - 1, x: 0, dx: 0} : p.key < d.key
          ? {depth: d.depth - 1, x: 2 * Math.PI, dx: 0}
          : {depth: 0, x: 0, dx: 2 * Math.PI};
    }

    function outsideArc(d) {
      return {depth: d.depth + 1, x: outsideAngle(d.x), dx: outsideAngle(d.x + d.dx) - outsideAngle(d.x)};
    }

    center.datum(root);

    // When zooming in, arcs enter from the outside and exit to the inside.
    // Entering outside arcs start from the old layout.
    if (root === p) enterArc = outsideArc, exitArc = insideArc, outsideAngle.range([p.x, p.x + p.dx]);

     var new_data=partition.nodes(root).slice(1)

    path = path.data(new_data, function(d) { return d.key; });

     // When zooming out, arcs enter from the inside and exit to the outside.
    // Exiting outside arcs transition to the new layout.
    if (root !== p) enterArc = insideArc, exitArc = outsideArc, outsideAngle.range([p.x, p.x + p.dx]);

    d3.transition().duration(d3.event.altKey ? 7500 : 750).each(function() {
      path.exit().transition()
          .style("fill-opacity", function(d) { return d.depth === 1 + (root === p) ? 1 : 0; })
          .attrTween("d", function(d) { return arcTween.call(this, exitArc(d)); })
          .remove();

      path.enter().append("path")
        .style("fill-opacity", function(d) { return d.depth === 2 - (root === p) ? 1 : 0; })
        .style("fill", function(d) { return d.fill; })
        .on("click", zoomIn)
              .on("mouseover", mouseOverArc)
        .on("mousemove", mouseMoveArc)
        .on("mouseout", mouseOutArc)
        .each(function(d) { this._current = enterArc(d); });


      path.transition()
          .style("fill-opacity", 1)
          .attrTween("d", function(d) { return arcTween.call(this, updateArc(d)); });

    });


     texts = texts.data(new_data, function(d) { return d.key; })

    texts.exit()
        .remove()
    texts.enter()
      .append("text")

    texts.style("opacity", 0)
      .attr("transform", function(d) {
        var r = computeTextRotation(d);
        return "rotate(" + r.global + ")" 
        + "translate(" + radius / 3 * d.depth + ",0)" 
        + "rotate(" + -r.correction + ")";
      })
      .style("font-weight", "bold")
      .style("text-anchor", "middle")
          .attr("dx", function(d) {return isRotated(d) ? "-85" : "85"}) //margin
      .attr("dy", ".35em") // vertical-align
      .filter(filter_min_arc_size_text)
      .on("click", zoomIn)
      .text(function(d,i) {return d.name})
          .transition().delay(750).style("opacity", 1)

  }
});

function key(d) {
  var k = [], p = d;
  while (p.depth) k.push(p.name), p = p.parent;
  return k.reverse().join(".");
}

function fill(d) {
  var p = d;
  while (p.depth > 1) p = p.parent;
  var c = d3.lab(hue(p.name));
  c.l = luminance(d.sum);
  return c;
}

function arcTween(b) {
  var i = d3.interpolate(this._current, b);
  this._current = i(0);
  return function(t) {
    return arc(i(t));
  };
}

function updateArc(d) {
  return {depth: d.depth, x: d.x, dx: d.dx};
}

d3.select(self.frameElement).style("height", margin.top + margin.bottom + "px");

您可以在 svg 中使用图像元素。 (Reference here)

根据你的代码使用d3,它会是这样的

svg.append("image").attr("xlink:href", "firefox.jpg")
    .attr("width", w).attr("height", h)
    .attr("x", -w/2).attr("y", -h/2)

在 Daniela Mogini 的建议下,我解决了这个难题。 我将葡萄图像的 canvas 大小调整为 1300x1300px,背景透明,葡萄图像在圆圈中。

然后我在下面添加了这个冷:var arc = d3.svg.arc()

svg.append("image")
  .attr("xlink:href", "images/grapes.png")
  .attr("x", -650)
  .attr("y", -650);