如何使用 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 对比)中,您需要使用 <image>
而不是 <img>
来表示元素。
尝试更改您的最后一个块:
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 容器上的这些元素上时,如何将光标设置为手?