Keon Kim
Keon Kim

Reputation: 760

embedding an image via d3 does not work

I am using an example from here

And I am trying to put an image into the white child node circles. So I made svg def, and pattern dom like the one below

var svg = d3.select("div.global-main-wrap").append("svg")
  .attr("width", window.innerWidth * 2)
  .attr("height", window.innerHeight * 2)
  .append("g")
  .attr("transform", "translate(" + diameter / 1.2 + "," + diameter / 2 + ")");

var pattern = d3.select("svg")
  .append('svg:defs')
  .append('pattern')
    .attr('id', 'album')
    .attr("patternUnits", "userSpaceOnUse")
    .attr('width', '6')
    .attr('height', '6')
  .append('image')
    .attr("xlink:href", "/images/artist.jpg")
    .attr('x', 0)
    .attr('y', 0)
    .attr('width', 6)
    .attr('height', 6);

The image successfully imports, but does not attach to the circle.

When i used it in here:

svg.selectAll(".node--leaf")
  .data(nodes)
  .style("fill", function(d){
    console.log("fuck this")
    return("url(#album)");
  })

The html(under svg) generated by d3 looks like this

<svg width="3840" height="1232">
   <g transform="translate(800,480)">
      <circle class="node" transform="translate(113.71054875330515,-165.44378762634966)" r="81.17208760605139" style="fill: rgb(255, 201, 192);">
         <title>Hyukoh</title>
      </circle>
      <circle class="node node--leaf" transform="translate(77.73322389314524,-134.28651033724577)" r="33.578560397369024" style="fill: url(#album);">
         <title>Hooka</title>
      </circle>
      <circle class="node node--leaf" transform="translate(149.68787361346506,-134.28651033724577)" r="33.578560397369024" style="fill: url(#album);">
         <title>Wi ing Wi ing</title>
      </circle>
      <circle class="node node--leaf" transform="translate(-139.849884911782,-131.45698460661947)" r="53.09236570260736" style="fill: url(#album);">
         <title>Gangnam Style</title>
      </circle>
      <circle class="node node--leaf" transform="translate(-179.79062119212261,-44.78531265608177)" r="37.54197181754974" style="fill: url(#album);">
         <title>Gentleman</title>
      </circle>
      <circle class="node node--leaf" transform="translate(-99.90914863144138,-44.78531265608177)" r="37.54197181754974" style="fill: url(#album);">
         <title>Champion</title>
      </circle>
      <text class="label" font-family="sans-serif" dy=".35em" transform="translate(-9.852034042048786,139.01271989766576)" style="fill-opacity: 1; font-size: 18px;">Kim Kwang-Seok</text>
      <text class="label" font-family="sans-serif" dy=".35em" transform="translate(53.85251368137091,139.01271989766576)" style="fill-opacity: 0; display: none; font-size: 18px;">Yu Jae-Ha</text>
      <text class="label" font-family="sans-serif" dy=".35em" transform="translate(-77.2754011091547,139.01271989766576)" style="fill-opacity: 0; display: none; font-size: 18px;">Kim Hyun-Sik</text>
      <text class="label" font-family="sans-serif" dy=".35em" transform="translate(-217.33259300967237,-95.89634557387944)" style="fill-opacity: 1; font-size: 18px;">Pop</text>
      <text class="label" font-family="sans-serif" dy=".35em" transform="translate(-322.1393458205501,-95.89634557387944)" style="fill-opacity: 0; display: none; font-size: 18px;">Girl's Generation</text>
      <text class="label" font-family="sans-serif" dy=".35em" transform="translate(-139.849884911782,-95.89634557387944)" style="fill-opacity: 0; display: none; font-size: 18px;">Psy</text> 
   </g>
   <defs>
      <pattern id="album" patternUnits="userSpaceOnUse" width="6" height="6">
         <image xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/images/artist.jpg" x="0" y="0" width="6" height="6"></image>
      </pattern>
   </defs>
</svg>

Upvotes: 1

Views: 83

Answers (2)

Cyril Cherian
Cyril Cherian

Reputation: 32327

You need to first define patterns in your def in svg like:

<body>
    <svg id="mySvg" width="80" height="80">
        <defs id="mdef">
            <pattern id="image0" x="0" y="0" height="5" width="5">
                <image x="0" y="0" width="5" height="5" xlink:href="https://cdn0.iconfinder.com/data/icons/superhero-2/256/Flash-128.png"></image>
            </pattern>
            <pattern id="image1" x="0" y="0" height="15" width="15">
                <image x="0" y="0" width="15" height="15" xlink:href="https://cdn0.iconfinder.com/data/icons/superhero-2/256/Flash-128.png"></image>
            </pattern>
            <pattern id="image2" x="0" y="0" height="20" width="20">
                <image x="0" y="0" width="20" height="20" xlink:href="https://cdn0.iconfinder.com/data/icons/superhero-2/256/Flash-128.png"></image>
            </pattern>
            <pattern id="image3" x="0" y="0" height="25" width="25">
                <image x="0" y="0" width="25" height="25" xlink:href="https://cdn0.iconfinder.com/data/icons/superhero-2/256/Flash-128.png"></image>
            </pattern>
            <pattern id="image4" x="0" y="0" height="30" width="30">
                <image x="0" y="0" width="30" height="30" xlink:href="https://cdn0.iconfinder.com/data/icons/superhero-2/256/Flash-128.png"></image>
            </pattern>
            <pattern id="image5" x="0" y="0" height="50" width="50">
                <image x="0" y="0" width="50" height="50" xlink:href="https://cdn0.iconfinder.com/data/icons/superhero-2/256/Flash-128.png"></image>
            </pattern>
            <pattern id="image6" x="0" y="0" height="45" width="45">
                <image x="0" y="0" width="45" height="45" xlink:href="https://cdn0.iconfinder.com/data/icons/superhero-2/256/Flash-128.png"></image>
            </pattern>
        </defs>
        </svg>
</body>

In this different image for different radius else the image will not come proper and it will be cut off...if you are a css expert you will definitely find a better way :)

Then a function to return image based on radius size:

var getImage = function (d) {
    if (d.r > 0 && d.r <= 5) {
        return "url(#image0)"
    }
    if (d.r > 5 && d.r <= 10) {
        return "url(#image1)"
    }
    if (d.r > 10 && d.r <= 15) {
        return "url(#image2)"
    }
    if (d.r > 15 && d.r <= 20) {
        return "url(#image4)"
    }
    if (d.r > 20 && d.r <= 25) {
        return "url(#image3)"
    }
    if (d.r > 25 && d.r <= 30) {
        return "url(#image6)"
    }
    if (d.r > 30 && d.r <= 35) {
        return "url(#image5)"
    }
    if (d.r > 35) {
        return "#FFF";
    }
}

The above function is called when we define the style for circle like this:

var circleFill = function (d) {
    if (d['color']) {
        return d.color;
    } else {
        return d.children ? color(d.depth) : getImage(d);
    }
}

Note on zoom I am removing the images like this:

//this will make the circles not have images on zoom
var circleFillZoom = function (d) {
    if (d['color']) {
        return d.color;
    } else {
        return d.children ? color(d.depth) : '#FFF';
    }
}

Working code here.

Upvotes: 1

Robert Longson
Robert Longson

Reputation: 124249

Create the pattern with objectBoundingBox units e.g.

var pattern = d3.select("svg")
  .append('svg:defs')
  .append('pattern')
    .attr('id', 'album')
    .attr("patternContentUnits", "objectBoundingBox")
    .attr('width', '1')
    .attr('height', '1')
  .append('image')
    .attr("xlink:href", "/images/artist.jpg")
    .attr('width', 1)
    .attr('height', 1);

Then you'll get this

Upvotes: 3

Related Questions