javascript - 为 SVG 圆圈添加笑脸
问题描述
我想使用 d3 将笑脸(或皱眉)脸添加到现有的 SVG 中,包含许多circle
元素。
到目前为止,我已经能够通过将元素直接附加到 SVG 根来实现这一点。它有效,但只是因为它们的坐标恰好以正确的方式设置。
我想扩展它以便能够为任意数量的圆圈添加笑脸,无论它们在哪里。
我曾尝试选择圆圈并附加到它们,但它不起作用。
这是我到目前为止所取得的成就:
let svg = d3.select("#mySvg");
let appendedTo = svg;
//let appendedTo = svg.select(".mainCircle");
appendedTo.append("circle")
.attr("cx",13)
.attr("cy",15)
.attr("r",5);
appendedTo.append("circle")
.attr("cx",37)
.attr("cy",15)
.attr("r",5);
var arc = d3.svg.arc()
.innerRadius(10)
.outerRadius(11)
.startAngle(3*(Math.PI/2)) //converting from degs to radians
.endAngle(5 * (Math.PI/2)) //just radians
appendedTo.append("path")
.attr("d", arc)
.attr("transform", "translate(25,40)");
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<svg width = 50 height = 50 id="mySvg">
<circle class="mainCircle" cx=25 cy=25 r=25 fill="red"></circle>
</svg>
问题是 HTML 页面上圆圈的位置发生了变化,笑脸将无法正确定位。
你能给我一些指示,将笑脸“锚定”到圆形元素吗?
编辑:
SVG 的一个例子:
<svg width = 500 height = 500 id="mySvg">
<circle class="mainCircle" cx=25 cy=25 r=25 fill="red"></circle>
<circle class="mainCircle" cx=125 cy=65 r=50 fill="red"></circle>
<circle class="mainCircle" cx=200 cy=12 r=10 fill="red"></circle>
<circle class="mainCircle" cx=210 cy=300 r=90 fill="red"></circle>
<circle class="mainCircle" cx=320 cy=25 r=5 fill="red"></circle>
<circle class="mainCircle" cx=400 cy=120 r=50 fill="red"></circle>
<circle class="mainCircle" cx=410 cy=230 r=25 fill="red"></circle>
</svg>
解决方案
我认为这里的一个好的解决方案是创建一个函数,您可以将圆圈的属性(cx
和cy
)传递给该函数,该函数r
将仅基于这些值创建笑脸。
自己创建圈子
因此,例如,假设我们圈子的数据具有x
、y
和r
作为这些属性。我们可以创建一个函数,这里命名为makeSmileys
,它在容器组中绘制圆圈和路径:
function makeSmileys(group, xPos, yPos, radius) {
//left eye
group.append("circle")
.attr("cx", xPos - radius / 3)
.attr("cy", yPos - radius / 3)
.attr("r", radius / 8)
.style("fill", "black");
//right eye
group.append("circle")
.attr("cx", xPos + radius / 3)
.attr("cy", yPos - radius / 3)
.attr("r", radius / 8)
.style("fill", "black");
arc.innerRadius(radius / 2)
.outerRadius(radius / 2.2);
//mouth
group.append("path")
.attr("d", arc)
.attr("transform", "translate(" + xPos + "," + yPos + ")");
}
如您所见,两只眼睛(圆圈)和嘴巴(路径)的位置仅基于参数。您可以按照自己的方式调整这些位置。
为了使这个函数起作用,我们必须创建容器组,然后在这些各自的选择上调用它:
circlesGroup.each(function(d) {
d3.select(this).call(makeSmileys, d.x, d.y, d.r)
})
因为我使用的是selection.call
,所以第一个参数(即group
)是选择本身。作为替代方案,如果您不想使用selection.call
,只需将该函数作为普通 JavaScript 函数调用,并将容器传递给它。
这是一个演示,有 10 个随机生成的圆圈:
const svg = d3.select("svg");
const data = d3.range(10).map(function(d) {
return {
x: 50 + Math.random() * 500,
y: 50 + Math.random() * 300,
r: Math.random() * 50
}
});
const arc = d3.arc()
.startAngle(1 * (Math.PI / 2))
.endAngle(3 * (Math.PI / 2));
const circlesGroup = svg.selectAll(null)
.data(data)
.enter()
.append("g");
circlesGroup.each(function(d) {
d3.select(this).append("circle")
.attr("cx", d => d.x)
.attr("cy", d => d.y)
.attr("r", d => d.r)
.style("fill", "yellow")
.style("stroke", "black")
})
circlesGroup.each(function(d) {
d3.select(this).call(makeSmileys, d.x, d.y, d.r)
})
function makeSmileys(group, xPos, yPos, radius) {
group.append("circle")
.attr("cx", xPos - radius / 3)
.attr("cy", yPos - radius / 3)
.attr("r", radius / 8)
.style("fill", "black");
group.append("circle")
.attr("cx", xPos + radius / 3)
.attr("cy", yPos - radius / 3)
.attr("r", radius / 8)
.style("fill", "black");
arc.innerRadius(radius / 2)
.outerRadius(radius / 2.2);
group.append("path")
.attr("d", arc)
.attr("transform", "translate(" + xPos + "," + yPos + ")");
}
<script src="https://d3js.org/d3.v5.min.js"></script>
<svg width="600" height="400"></svg>
使用预先存在的圈子
如果您有现有的 SVG(正如您在已编辑的问题中明确指出的那样),您可以使用选择器选择所有圆圈...
const circles = svg.selectAll("circle");
...然后获取它们的属性,最后调用函数:
circles.each(function() {
const x = +d3.select(this).attr("cx");
const y = +d3.select(this).attr("cy");
const r = +d3.select(this).attr("r");
makeSmileys(x, y, r)
});
请注意这里的一元加号,因为 getter 返回这些属性的字符串。
这是演示:
const svg = d3.select("svg");
const arc = d3.arc()
.startAngle(1 * (Math.PI / 2))
.endAngle(3 * (Math.PI / 2));
const circles = svg.selectAll("circle");
circles.each(function() {
const x = +d3.select(this).attr("cx");
const y = +d3.select(this).attr("cy");
const r = +d3.select(this).attr("r");
makeSmileys(x, y, r)
})
function makeSmileys(xPos, yPos, radius) {
svg.append("circle")
.attr("cx", xPos - radius / 3)
.attr("cy", yPos - radius / 3)
.attr("r", radius / 8)
.style("fill", "black");
svg.append("circle")
.attr("cx", xPos + radius / 3)
.attr("cy", yPos - radius / 3)
.attr("r", radius / 8)
.style("fill", "black");
arc.innerRadius(radius / 2)
.outerRadius(radius / 2.2);
svg.append("path")
.attr("d", arc)
.attr("transform", "translate(" + xPos + "," + yPos + ")");
}
<script src="https://d3js.org/d3.v5.min.js"></script>
<svg width="500" height="500" id="mySvg">
<circle class="mainCircle" cx=25 cy=25 r=25 fill="yellow"></circle>
<circle class="mainCircle" cx=125 cy=65 r=50 fill="yellow"></circle>
<circle class="mainCircle" cx=200 cy=12 r=10 fill="yellow"></circle>
<circle class="mainCircle" cx=210 cy=300 r=90 fill="yellow"></circle>
<circle class="mainCircle" cx=320 cy=25 r=5 fill="yellow"></circle>
<circle class="mainCircle" cx=400 cy=120 r=50 fill="yellow"></circle>
<circle class="mainCircle" cx=410 cy=230 r=25 fill="yellow"></circle>
</svg>
推荐阅读
- r - R数据框(> 2列)到命名列表
- reactjs - 当孩子卸载时,父母的状态会发生什么
- c++ - CreateProcess 运行带有参数的 jar
- c# - 为什么 netcoreapp2.1 和 netcoreapp3.1 的 TimeSpan 值不同?
- node.js - 异步从nodeJS中的sqlite数据库获取数据
- python - 在多线程环境中调试 python 多进程
- python - python balance bracket explanation
- javascript - 标记的 onClick 事件后,弹出窗口不会在 react-leaflet 中保持打开状态
- javascript - Vue CLI - 从本地 JSON 解析组件中的嵌套数据
- android - 如何从 Android Linux 内核获取当前工作目录?