简体   繁体   中英

d3.js center image in bubble chart

I am trying to center my append images to the circle but I cannot figure out how to set up my x and y since the size of the circle is dynamic and changes with the width of the svg .

What is the formula for finding the x and y coordinates given the current radius of the circle?

  vis
    .enter()
    .append("svg:image")
    .attr("transform", d => "translate(" + d.x + "," + d.y + ")")
    .attr("xlink:href", function(d) {
      return d.img;
    })
    .attr("x", 0)
    .attr("y", 0)
    .attr("width", d => d.r / 1.5);

 (function() { var json = { call_data: [ [ "Lifestyle", 1, "https://uploads-ssl.webflow.com/59df9e77ad9420000140eafe/5bb3ce2f801fbc657f83dd57_pp-lifestyle(white).svg" ], [ "Sports", 10, "https://uploads-ssl.webflow.com/59df9e77ad9420000140eafe/5c9131911ad86f445cb5abc7_pp-sport(white).svg" ], [ "Environment", 8, "https://uploads-ssl.webflow.com/59df9e77ad9420000140eafe/59f2a4bef42fff000159ba7a_pp-environ(white).svg" ], [ "Medical", 6, "https://uploads-ssl.webflow.com/59df9e77ad9420000140eafe/59f2a4dc831e8500015fda53_pp-health(white).svg" ], [ "Food", 4, "https://uploads-ssl.webflow.com/59df9e77ad9420000140eafe/59f8c2cc78cc2d0001fd4a7e_pp-food(white).svg" ] ] }; var svg = d3 .select(".bubble_chart") .append("svg") /* .attr("width", diameter) .attr("height", diameter); */ .attr("preserveAspectRatio", "xMinYMin meet") .attr("viewBox", "0 0 600 400") //class to make it responsive .classed("svg-content-responsive", true); var bubble = d3.layout .pack() .size([600, 400]) .value(function(d) { return d.size; }) .padding(2); // generate data with calculated layout values var nodes = bubble.nodes(processData(json)).filter(function(d) { return !d.children; }); // filter out the outer bubble var vis = svg.selectAll("circle").data(nodes, function(d, i) { return d.name + i; }); vis .enter() .append("circle") .attr("transform", function(d) { return "translate(" + dx + "," + dy + ")"; }) .attr("class", function(d) { return d.className; }) .attr("r", 0) .transition() .duration(1000) .attr("r", function(d) { return dr; }); vis .enter() .append("svg:image") .attr("transform", d => "translate(" + dx + "," + dy + ")") .attr("xlink:href", function(d) { return d.img; }) .attr("x", 0) .attr("y", 0) .attr("width", d => dr / 1.5); function processData(data) { var obj = data.call_data; var newDataSet = []; for (var prop in obj) { newDataSet.push({ name: obj[prop][0], className: obj[prop][0].toLowerCase(), size: obj[prop][1], img: obj[prop][2] }); } return { children: newDataSet }; } })(); 
 .lifestyle { fill: #89BED3; } .sports { fill: #2A83D4; } .environment { fill: #6CC070; } .food { fill: #665C9E; } .medical { fill: #C13E40; } .bubble_chart { border: 2px solid red; display: inline-block; position: absolute; width: 100%; padding-bottom: 100%; /* aspect ratio */ vertical-align: top; overflow: hidden; } .svg-content-responsive { border: 3px solid green; display: inline-block; position: absolute; top: 0; left: 0; } 
 <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.4.5/d3.js"></script> <div class="container"> <div class="bubble_chart"></div> </div> 

As you're already transforming the images using translate(" + dx + "," + dy + ") which results in the images having the start point as the center of the corresponding circles, you just have to offset the images by their respective height and widths.

ie applying the following x, y attributes:

.attr('x', d => -(d.r/1.5)/2)
.attr('y', d => -(d.r/1.5)/2)

will center the images where (dr/1.5) is the width/height of the image.

Code snippet:

 (function() { var json = { call_data: [ [ "Lifestyle", 1, "https://uploads-ssl.webflow.com/59df9e77ad9420000140eafe/5bb3ce2f801fbc657f83dd57_pp-lifestyle(white).svg" ], [ "Sports", 10, "https://uploads-ssl.webflow.com/59df9e77ad9420000140eafe/5c9131911ad86f445cb5abc7_pp-sport(white).svg" ], [ "Environment", 8, "https://uploads-ssl.webflow.com/59df9e77ad9420000140eafe/59f2a4bef42fff000159ba7a_pp-environ(white).svg" ], [ "Medical", 6, "https://uploads-ssl.webflow.com/59df9e77ad9420000140eafe/59f2a4dc831e8500015fda53_pp-health(white).svg" ], [ "Food", 4, "https://uploads-ssl.webflow.com/59df9e77ad9420000140eafe/59f8c2cc78cc2d0001fd4a7e_pp-food(white).svg" ] ] }; var svg = d3 .select(".bubble_chart") .append("svg") /* .attr("width", diameter) .attr("height", diameter); */ .attr("preserveAspectRatio", "xMinYMin meet") .attr("viewBox", "0 0 600 400") //class to make it responsive .classed("svg-content-responsive", true); var bubble = d3.layout .pack() .size([600, 400]) .value(function(d) { return d.size; }) .padding(2); // generate data with calculated layout values var nodes = bubble.nodes(processData(json)).filter(function(d) { return !d.children; }); // filter out the outer bubble var vis = svg.selectAll("circle").data(nodes, function(d, i) { return d.name + i; }); vis .enter() .append("circle") .attr("transform", function(d) { return "translate(" + dx + "," + dy + ")"; }) .attr("class", function(d) { return d.className; }) .attr("r", 0) .transition() .duration(1000) .attr("r", function(d) { return dr; }); vis .enter() .append("svg:image").style('opacity', 0) .attr("transform", d => "translate(" + dx + "," + dy + ")") .attr('x', d => -(dr/1.5)/2) .attr('y', d => -(dr/1.5)/2) .attr("xlink:href", function(d) { return d.img; }) .attr("width", d => dr / 1.5).transition().duration(1000).style('opacity', 1); function processData(data) { var obj = data.call_data; var newDataSet = []; for (var prop in obj) { newDataSet.push({ name: obj[prop][0], className: obj[prop][0].toLowerCase(), size: obj[prop][1], img: obj[prop][2] }); } return { children: newDataSet }; } })(); 
 .lifestyle { fill: #89BED3; } .sports { fill: #2A83D4; } .environment { fill: #6CC070; } .food { fill: #665C9E; } .medical { fill: #C13E40; } .bubble_chart { border: 2px solid red; display: inline-block; position: absolute; width: 100%; padding-bottom: 100%; /* aspect ratio */ vertical-align: top; overflow: hidden; } .svg-content-responsive { border: 3px solid green; display: inline-block; position: absolute; top: 0; left: 0; } 
 <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.4.5/d3.js"></script> <div class="container"> <div class="bubble_chart"></div> </div> 

Also, I've added an additional transition to the images' opacity to match with the circles visibility - .transition().duration(1000).style('opacity', 1) . Hope this helps.

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