英文:
Smarter way of chaining SVG operations in this example?
问题
在以下代码中,我创建文本SVG元素,对其进行自动换行,然后基于新的(换行后的)文本尺寸和矩形创建边界框。我只能以以下方式完成它,我想知道是否可以简化代码,以便可能在同一个函数中创建文本和边界框?
var a = settings.g.selectAll(".axislabel")
.data(featureData)
.join(
enter => enter.append("text")
.attr("x", d => d.label_coord.x)
.attr("y", d => d.label_coord.y)
.text(d => d.name)
.attr("dy", "0.35em")
.call(wrap, 60)
);
var x = settings.g.selectAll("text");
// 获取刚刚创建的元素
for (let a of x['_groups'][0]) {
// 遍历并创建边界框
var v = a.getBBox();
// 基于边界框创建矩形
settings.g.selectAll("labelBack")
.data([a.textContent])
.join(
enter => enter.append("rect")
.attr("x", v.x)
.attr("y", v.y)
.attr("width", v.width)
.attr("height", v.height)
.attr("fill", "white")
.attr("opacity", 1)
.attr("stroke-width", 1)
.attr("stroke", "black")
);
}
英文:
In the following code, I create text svg elements, word-wrap it and then create bounding boxes based on the new (wrapped) text dimensions and recntagles. I was only able do it in the following way, and I would like to know if the code could not be simplified so that maybe the boxes are created in the same function as the text?
var a=settings.g.selectAll(".axislabel")
.data(featureData)
.join(
enter => enter.append("text")
.attr("x", d => d.label_coord.x)
.attr("y", d => d.label_coord.y)
.text(d => d.name)
.attr("dy", "0.35em")
.call(wrap, 60)
)
var x=settings.g.selectAll("text")
/getting the elements I just created
for(let a of x['_groups'][0])
{
//iterating through and create bounding boxes
var v=a.getBBox();
//createing rectangles based on the boundinx boxes
settings.g.selectAll("labelBack")
.data([a.textContent])
.join(
enter => enter.append("rect")
.attr("x", v.x)
.attr("y", v.y)
.attr("width", v.width)
.attr("height", v.height)
.attr("fill", "white")
.attr("opacity", 1)
.attr("stroke-width", 1)
.attr("stroke", "black")
);
答案1
得分: 2
当你使用 selection.join
时搭配一个函数参数,传入的函数会构建你想要的 DOM 元素。因此,你在构建时有很大的灵活性。特别是,你可以做如下操作:
enter => {
let g = enter.append("g");
let rect = g.append("rect");
let text = g.append("text");
// ...设置文本的代码
rect
.attr('height', ...设置高度的代码)
.attr('width', ...设置宽度的代码)
这可以工作是因为在设置矩形大小时文本已经被设置。你可以在下面的代码中看到它实际运行的效果。
Credit: 尽管有些许不同,但部分代码基于 Gerardo 在这里的回答。
英文:
When you use selection.join
with a functional argument, the function that you pass in constructs the dom element you want. Thus, you've got a lot of flexibility in what you build. In particular, you can do something like
enter => {
let g = enter.append("g");
let rect = g.append("rect");
let text = g.append("text")
...Code to set the text
rect
.attr('height', ...Code to set the height)
.attr('width', ...Code to set the width)
This can work because the text was set by the time the rect is sized. You can see this in action in the code below.
Credit: While this is a little bit different, I based the code partly on Gerardo's answer here.
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
let pad = 2;
let w = 200;
let h = 100;
let svg = d3
.select('#viz')
.append("svg")
.attr("viewBox", [0, 0, w, h])
.style("max-width", `${640}px`)
.style('border', 'solid 1px black');
let data = [
{ x: 10, y: 20, text: "This" },
{ x: 56, y: 50, text: "should" },
{ x: 120, y: 20, text: "work" }
];
svg
.append("g")
.selectAll(null)
.data(data)
.join((enter) => {
let g = enter.append("g");
let rect = g.append("rect");
let text = g
.append("text")
.attr("x", (d) => d.x)
.attr("y", (d) => d.y)
.text((d) => d.text)
.call(wrap);
rect
.attr("x", (d) => d.x - pad)
.attr("y", (d) => d.y - d.bbox.height + 2 * pad)
.attr("width", (d) => d.bbox.width + 2 * pad)
.attr("height", (d) => d.bbox.height + 2 * pad)
.attr("fill", "#ddd")
.attr("stroke", "black");
});
function wrap(selection) {
selection.each(function (d) {
d.bbox = this.getBBox();
});
}
<!-- language: lang-html -->
<div id="viz"></div>
<script src="https://d3js.org/d3.v7.min.js"></script>
<!-- end snippet -->
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论