如何使用 D3.js 将图像添加到 svg 容器

Posted

技术标签:

【中文标题】如何使用 D3.js 将图像添加到 svg 容器【英文标题】:How to add an image to an svg container using D3.js 【发布时间】:2013-01-12 03:10:34 【问题描述】:

我创建了一个示例 Asp.Net MVC 4 应用程序,其中我使用 D3.js 附加了一个 SVG 元素,然后在 SVG 内部附加了一个文本元素(请参见下面的代码)。在我尝试使用本地 png 文件将 img 附加到 SVG 之前,这一切都很好。 img 被附加到 DOM,但 img 不会呈现在页面上。任何想法我在这里做错了什么,以及如何解决它?

@
    ViewBag.Title = "Home Page";


<script src="~/Scripts/d3.v3.js"></script>
<script type="text/javascript">
    var svg = d3.select("body")
        .append("svg")
        .attr("width", 200)
        .attr("height", 100)
        .style("border", "1px solid black");

    var text = svg.selectAll("text")
        .data([0])
        .enter()
        .append("text")
        .text("Testing")
        .attr("x", "40")
        .attr("y", "60");

    var imgs = svg.selectAll("img").data([0]);
    imgs.enter()
        .append("img")
        .attr("xlink:href", "@Url.Content("~/Content/images/icons/refresh.png")")
        .attr("x", "60")
        .attr("y", "60")
        .attr("width", "20")
        .attr("height", "20");

</script>

@Richard Marr - 下面是尝试在纯 html 中做同样的事情,这给了我相同的结果。我不确定我的代码是否可以通过这种方式从本地驱动器获取 refresh.png 文件。

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <script src="http://d3js.org/d3.v2.js"></script>

    </head>
    <body>
        <script type="text/javascript">
            var svg = d3.select("body")
                .append("svg")
                .attr("width", 200)
                .attr("height", 100)
                .style("border", "1px solid black");

            var text = svg.selectAll("text")
                .data([0])
                .enter()
                .append("text")
                .text("Testing")
                .attr("x", "40")
                .attr("y", "60");

            var imgs = svg.selectAll("img").data([0]);
                imgs.enter()
                .append("svg:img")
                .attr("xlink:href", "file:///D:/d3js_projects/refresh.png")
                .attr("x", "60")
                .attr("y", "60")
                .attr("width", "20")
                .attr("height", "20");

        </script>
    </body>
</html>

【问题讨论】:

您能否通过在常规 HTML 中演示该问题来排除此问题的 ASP.NET 方面? 在 SVG 中,我相信你想要“image”而不是“img”作为元素。或许可以试试。 @cmonkey - 效果很好,如果您将此作为答案发布,我会将其标记为正确答案。 如果您将多个图像推进到多个 SVG 元素中,请建立一个模式,如此处所示。 ***.com/questions/19202450/… 【参考方案1】:

在 SVG(与 HTML 对比)中,您需要使用 &lt;image&gt; 而不是 &lt;img&gt; 来表示元素。

尝试更改您的最后一个块:

var imgs = svg.selectAll("image").data([0]);
            imgs.enter()
            .append("svg:image")
            ...

【讨论】:

如果您显示 xlink:href 标签会更好,用户需要使用您的解决方案。此外,不需要数据。 请同时添加xlink-href,width,height 部分【参考方案2】:
nodeEnter.append("svg:image")
.attr('x', -9)
.attr('y', -12)
.attr('width', 20)
.attr('height', 24)
.attr("xlink:href", "resources/images/check.png")

【讨论】:

【参考方案3】:
 var svg = d3.select("body")
        .append("svg")
        .style("width", 200)
        .style("height", 100)

【讨论】:

【参考方案4】:

我的团队还想在 d3 绘制的圆圈内添加图像,并提出以下 (fiddle):

index.html:

<!doctype html>
<html>
<head>
  <link rel="stylesheet" type="text/css" href="timeline.css">
  <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.js"></script>
  <script src="https://code.jquery.com/jquery-2.2.4.js"
    integrity="sha256-iT6Q9iMJYuQiMWNd9lDyBUStIq/8PuOW33aOqmvFpqI="
    crossorigin="anonymous"></script>
  <script src="./timeline.js"></script>
</head>
  <body>
    <div class="timeline"></div>
  </body>
</html>

时间线.css:

.axis path,
.axis line,
.tick line,
.line 
  fill: none;
  stroke: #000000;
  stroke-width: 1px;

timeline.js:

// container target
var elem = ".timeline";

var props = 
  width: 1000,
  height: 600,
  class: "timeline-point",

  // margins
  marginTop: 100,
  marginRight: 40,
  marginBottom: 100,
  marginLeft: 60,

  // data inputs
  data: [
    
      x: 10,
      y: 20,
      key: "a",
      image: "https://unsplash.it/300/300",
      id: "a"
    ,
    
      x: 20,
      y: 10,
      key: "a",
      image: "https://unsplash.it/300/300",
      id: "b"
    ,
    
      x: 60,
      y: 30,
      key: "a",
      image: "https://unsplash.it/300/300",
      id: "c"
    ,
    
      x: 40,
      y: 30,
      key: "a",
      image: "https://unsplash.it/300/300",
      id: "d"
    ,
    
      x: 50,
      y: 70,
      key: "a",
      image: "https://unsplash.it/300/300",
      id: "e"
    ,
    
      x: 30,
      y: 50,
      key: "a",
      image: "https://unsplash.it/300/300",
      id: "f"
    ,
    
      x: 50,
      y: 60,
      key: "a",
      image: "https://unsplash.it/300/300",
      id: "g"
    
  ],

  // y label
  yLabel: "Y label",
  yLabelLength: 50,

  // axis ticks
  xTicks: 10,
  yTicks: 10



// component start
var Timeline = ;

/***
*
* Create the svg canvas on which the chart will be rendered
*
***/

Timeline.create = function(elem, props) 

  // build the chart foundation
  var svg = d3.select(elem).append('svg')
      .attr('width', props.width)
      .attr('height', props.height);

  var g = svg.append('g')
      .attr('class', 'point-container')
      .attr("transform",
              "translate(" + props.marginLeft + "," + props.marginTop + ")");

  var g = svg.append('g')
      .attr('class', 'line-container')
      .attr("transform", 
              "translate(" + props.marginLeft + "," + props.marginTop + ")");

  var xAxis = g.append('g')
    .attr("class", "x axis")
    .attr("transform", "translate(0," + (props.height - props.marginTop - props.marginBottom) + ")");

  var yAxis = g.append('g')
    .attr("class", "y axis");

  svg.append("text")
    .attr("class", "y label")
    .attr("text-anchor", "end")
    .attr("y", 1)
    .attr("x", 0 - ((props.height - props.yLabelLength)/2) )
    .attr("dy", ".75em")
    .attr("transform", "rotate(-90)")
    .text(props.yLabel);

  // add placeholders for the axes
  this.update(elem, props);
;

/***
*
* Update the svg scales and lines given new data
*
***/

Timeline.update = function(elem, props) 
  var self = this;
  var domain = self.getDomain(props);
  var scales = self.scales(elem, props, domain);

  self.drawPoints(elem, props, scales);
;


/***
*
* Use the range of values in the x,y attributes
* of the incoming data to identify the plot domain
*
***/

Timeline.getDomain = function(props) 
  var domain = ;
  domain.x = props.xDomain || d3.extent(props.data, function(d)  return d.x; );
  domain.y = props.yDomain || d3.extent(props.data, function(d)  return d.y; );
  return domain;
;


/***
*
* Compute the chart scales
*
***/

Timeline.scales = function(elem, props, domain) 

  if (!domain) 
    return null;
  

  var width = props.width - props.marginRight - props.marginLeft;
  var height = props.height - props.marginTop - props.marginBottom;

  var x = d3.scale.linear()
    .range([0, width])
    .domain(domain.x);

  var y = d3.scale.linear()
    .range([height, 0])
    .domain(domain.y);

  return x: x, y: y;
;


/***
*
* Create the chart axes
*
***/

Timeline.axes = function(props, scales) 

  var xAxis = d3.svg.axis()
    .scale(scales.x)
    .orient("bottom")
    .ticks(props.xTicks)
    .tickFormat(d3.format("d"));

  var yAxis = d3.svg.axis()
    .scale(scales.y)
    .orient("left")
    .ticks(props.yTicks);

  return 
    xAxis: xAxis,
    yAxis: yAxis
  
;


/***
*
* Use the general update pattern to draw the points
*
***/

Timeline.drawPoints = function(elem, props, scales, prevScales, dispatcher) 
  var g = d3.select(elem).selectAll('.point-container');
  var color = d3.scale.category10();

  // add images
  var image = g.selectAll('.image')
    .data(props.data)

  image.enter()
    .append("pattern")
    .attr("id", function(d) return d.id)
    .attr("class", "svg-image")
    .attr("x", "0")
    .attr("y", "0")
    .attr("height", "70px")
    .attr("width", "70px")
    .append("image")
      .attr("x", "0")
      .attr("y", "0")
      .attr("height", "70px")
      .attr("width", "70px")
      .attr("xlink:href", function(d) return d.image)

  var point = g.selectAll('.point')
    .data(props.data);

  // enter
  point.enter()
    .append("circle")
      .attr("class", "point")
      .on('mouseover', function(d) 
        d3.select(elem).selectAll(".point").classed("active", false);
        d3.select(this).classed("active", true);
        if (props.onMouseover) 
          props.onMouseover(d)
        ;
      )
      .on('mouseout', function(d) 
        if (props.onMouseout) 
          props.onMouseout(d)
        ;
      )

  // enter and update
  point.transition()
    .duration(1000)
    .attr("cx", function(d) 
      return scales.x(d.x); 
    )
    .attr("cy", function(d)  
      return scales.y(d.y); 
    )
    .attr("r", 30)
    .style("stroke", function(d) 
      if (props.pointStroke) 
        return d.color = props.pointStroke;
       else 
        return d.color = color(d.key);
      
    )
    .style("fill", function(d) 
      if (d.image) 
        return ("url(#" + d.id + ")");
      

      if (props.pointFill) 
        return d.color = props.pointFill;
       else 
        return d.color = color(d.key);
      
    );

  // exit
  point.exit()
    .remove();

  // update the axes
  var axes = this.axes(props, scales);
  d3.select(elem).selectAll('g.x.axis')
    .transition()
    .duration(1000)
    .call(axes.xAxis);

  d3.select(elem).selectAll('g.y.axis')
    .transition()
    .duration(1000)
    .call(axes.yAxis);
;


$(document).ready(function() 
  Timeline.create(elem, props);
)

【讨论】:

【参考方案5】:

我不知道为什么,但图像不应该被复制、三倍等......应该删除前一个并再次加载它,但使用另一个旋转数据。这是我的代码:

数据.csv enter image description here

d3.csv("data/data.csv").then(function(data)
//console.log(data);
// Clean data
formattedData = data.map(function(id)
id.rot_1 = +id.rot_1;
        id.trans_1 = +id.trans_1;
        return id;
);
// First run of the visualization
update(formattedData[0]);)

$("#play-button")
.on("click", function()
    var button = $(this);
    if (button.text() == "Play")
        button.text("Pause");
        interval = setInterval(step, 1000);            
    
    else 
        button.text("Play");
        clearInterval(interval);
    )

function step()
// At the end of our data, loop back
time = (time < 76) ? time+1 : 0
update(formattedData[time]); 

function update(data) 
// Standard transition time for the visualization
var t = d3.transition()
    .duration(1000);

//console.log(d3.selectAll(data));    
//console.log(data)

// original
var imgs1 = g.append("image") // en vez de g es svg
.attr("xlink:href", "img/picturetest.png");

// EXIT old elements not present in new data.
imgs1.exit()
    .attr("class", "exit")
    .selectAll("svg:image")
    .remove();

//console.log(data)

// ENTER new elements present in new data.
imgs1.enter()
    .append("svg:image") // svg:image
    //.attr("xlink:href", "img/picturetest.png")
    .attr("class", "enter")
    .merge(imgs1)
    .transition(t)
        .attr("x", 0) // 150
        .attr("y", 0) // 80
        .attr("width", 200)
        .attr("height", 200)
        .attr("transform", "rotate("+data.rot_1+") translate("+data.trans_1+")" ); `

【讨论】:

以上是关于如何使用 D3.js 将图像添加到 svg 容器的主要内容,如果未能解决你的问题,请参考以下文章

d3.js - 当鼠标悬停在 SVG 容器上的这些元素上时,如何将光标设置为手?

d3.js Map (<svg>) 自动适应父容器并随窗口调整大小

d3.js 将图例添加到多线系列图表

使用D3.js插入本地SVG图像

使用 d3 旋转 SVG 图像元素

使用 D3 JS 将鱼眼添加到轴