【问题标题】:Relative positioning of generated svg objects in D3D3中生成的svg对象的相对定位
【发布时间】:2016-06-06 18:08:42
【问题描述】:

让我从上下文开始:

我正在使用 D3 为我妻子的学生生成随机工作表,这些学生正在学习从 11 到 99 的数字。它创建了 10 个圆圈的列来代表十位,并创建一个 1-9 个圆圈的列来代表单位地点。

我已经能够成功构建我需要的所有对象,并且它会在刷新时随机化,这非常适合我目前的需求,但是我在对齐方面遇到了问题。

这是(有点混乱的)示例:CodePen - Montessori Number Generator

概念基于this example

我希望列在空间中居中,但我还无法计算出它(我最终将十位对齐到左侧,单位对齐到对)。

我要弄清楚的关键等式是生成的圆圈上的cx 值。

对于十组:

var tens = d3.range(10).map(function(r, d) {return {
  radius: 15,
  cx: svgWidth / 2 - r*50 + 9 * (r + 4),
  cy: d * 30 + 72 }})

Units 的 cx 值是使用 Tens 组的宽度计算得出的:

var w = d3.select('#tens')[0][0].getBBox().width;

for (var j = 0; j < units.length; j++) {
  gUnits.append("circle")
    .attr("cx", ( (svgWidth + w ) / 2 + w/45 ) )
    .attr("cy", units[j].cy)
    .attr("r", units[j].radius)
    .attr("fill", "white")
    .style("stroke", "black")
    .style("stroke-width", 3);
}

该示例适用于中等范围的数字(40-60 范围),但较小的数字会导致重叠,并且较大的数字会被推离画布的一侧,并且实际上没有一个完全居中。

也有可能我做错了,还有一个更简单的解决方案,我只是没有看到。

如果有的话,我对老婆需要满足的另外两大要求是:十位组和个位组之间要有一定的空间,最后的数字不能是10的倍数(即单位组不能为零)。

感谢您的任何想法!

【问题讨论】:

  • 所以你想得到一个 0-99 之间的随机数,并在 10 的部分中显示左边的数字,右边还有什么?这两行是什么?
  • 这两行是给孩子写十位和个位的数字-它们与手头的问题没有实际关系
  • 尽管我很喜欢 D3,但我想我会用 flexbox 来解决这个问题。我会有十个 div;其中九个用于“十几岁”、“二十多岁”、“三十岁”等,其中一个用于一岁。然后,我会根据有问题的数字有条件地将这些 div 设置为“显示:块”或“显示:无”。类似的事情需要在 div 中为 one 的列完成。 Flexbox 将帮助您调整它们的大小和间距,而无需计算出特定的坐标。只是在这里集思广益......

标签: javascript math d3.js svg


【解决方案1】:

我会这样做。

//generate a number between 11 and 99 
var  randNumber = Math.round(Math.random() * 90 + 11)

//Get difference when divide rand number by 10
var circleUnits = Math.round(randNumber%10);

//get how many 10's you can fit in what is left over
var circleTens = Math.round(randNumber-circleUnits)/10 ;

//use this data to generate arrays to work with
var circleUnitsData = d3.range(circleUnits).map(function(j, i) {return {
count : i,
    radius: 15
}})

var circleTensData = d3.range(circleTens).map(function(j, i) {return {
count : i+1,
    radius: 15
}})

这是我为十位生成圆圈的方法。因为每列总是会有 10 个圆圈(这就是我在上面将它除以 10 的原因):

 var nodeRadius = 15;
 var tensContainer = gTens.selectAll('circle') //put this outside the foreach so you don't overwrite any other circles
 var difference = 5; //gap between circles

circleTensData.forEach(function(d ,i){
tensContainer.data([1,2,3,4,5,6,7,8,9,10])
  .enter()
  .append('circle')
  .attr("cx",function(e,j) {
//  console.log(e)
  return 100 +  i * nodeRadius*2 + (i*difference) //use i here to get what set of 10 it is in
}) 
  .attr("cy", function(e,j) {
  return 100 +  j * nodeRadius*2  + (j*difference) //use j here to increment downwards 
})
  .attr("r", function(e) {
  return nodeRadius //hard coded radius above
}) 
  .attr("fill", "white")
  .style("stroke", "black")
  .style("stroke-width", 3);
})

那么单位就更容易了。为了计算出 x 位置,我将 10 的数据集的大小乘以节点的宽度加上差值,然后加上 40 来给它一个偏移量。所以每次刷新,单位都是向右40个单位:

 var circlesUnits = gUnits.selectAll('circle')
   .data(circleUnitsData)
   .enter()
   .append('circle')
    .attr("cx", function(d) {
    var i = circleTensData.length;
    console.log(400 +  i * nodeRadius*2 + (i*difference))
     return 140 +  i * nodeRadius*2 + (i*difference)
   })
   .attr("cy", function(d, i) {
     return 100 +  i * nodeRadius*2  + (i *difference)
   })
   .attr("r", function(d) {
     return d.radius
   }) 
   .attr("fill", "white")
    .style("stroke", "black")
    .style("stroke-width", 3);

在刷新时,我添加了一个警报来显示随机数。

如果我误读了问题,或者有任何问题,请随时发表评论,我会调整。希望这可以帮助你:)

更新小提琴:https://jsfiddle.net/thatOneGuy/8hc0sfbg/1/

编辑

您说您不希望数字以 0 结尾。以下将起作用:

function getRand() {

  var randNumber = Math.round(Math.random() * 90 + 11) // number between 0-99
  if (randNumber % 10 == 0) { //if ends in 0 run again
    return getRand();
  }
  return randNumber;
}

我已将代码包装在一个函数中,以便您可以在需要时调用它。所以上面的代码会在调用新函数时运行。单击“单击我”按钮运行该功能

更新小提琴:https://jsfiddle.net/thatOneGuy/8hc0sfbg/5/

编辑

您希望使圆圈居中。我会删除您设置的任何起始动作,即像这样设置 cx 和 cy 时:

return 100 +  i * nodeRadius*2 + (i*difference)

只需去掉重置它的 100 即可。然后我会将它翻译一半的宽度并删除十位和单位边界框的一半宽度。哪个应该居中。像这样:

var circleContainer = d3.select('#circleContainer')  
    var circleTensContainer = d3.select('#tens')
    var circleUnitsContainer = d3.select('#units')
var tensBBox = circleTensContainer.node().getBBox();
var unitsBBox = circleUnitsContainer.node().getBBox();
var containerSize = 40 + tensBBox.width + unitsBBox.width; 

var thisMovX = svgWidth/2 - containerSize/2; 

circleContainer.attr('transform', 'translate(' + thisMovX + ',0)' )

如果你也想让 y 居中,你可以做类似的事情。我也改变了你画线的地方。希望这会有所帮助:)

更新小提琴:https://jsfiddle.net/thatOneGuy/8hc0sfbg/8/

【讨论】:

  • 这是一个比我的更优雅的生成圆圈的解决方案 - 谢谢!唯一需要做的就是让整个东西水平居中 - 有什么想法吗?
  • @DarrellNevers 添加到答案中。为冗长的答案道歉,但它应该可以帮助您更容易理解:)
  • 我很欣赏你的冗长 - D3 对我来说是新的,但它似乎是我妻子所要求的最佳工具。这绝对有帮助 - 谢谢!
【解决方案2】:

为什么不使用 D3 强大的数据管理功能?

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js" charset="utf-8"></script>
<style>
svg {
    width:480px;
    height:600px;
    margin:auto 50%;
}
circle { stroke:#ccc;stroke-width:1px;    }
.units {    fill:#09C;    }
.tens {     fill:#F00;    }
</style>
</head>

<body>
<script>
var tens = [], units=[], radio=40;

for (i=0;i<Math.random()* 90 + 10;i++) tens.push(i); // [10-99]
for (i=0;i<Math.random()* 10;i++) units.push(i);     // [0-9]

var svg = d3.select("body").append("svg")

var units = svg.selectAll("circle.units")            // draw units
                        .data(units, function (d) {return d; })
    units.enter().append("circle")
                .attr("class", "units")
                .attr("cx", function (d) {return radio + Math.floor( d / 10 )*radio })
                .attr("cy", function (d) {return radio + d % 10 * radio} )
                .attr("r",  radio/2)


var tens = svg.selectAll("circle.tens")             // draw tens
                        .data(tens, function (d) {return d; })
    tens.enter().append("circle")
                .attr("class", "tens")
                .attr("cx", function (d) {return radio + Math.floor( d / 10 )*radio })
                .attr("cy", function (d) {return radio + d % 10 * radio} )
                .attr("r",  radio/2)
                .attr("transform","translate("+radio*2+", 0)")

svg.append('line')            //draw lines
  .attr('x1', 0)
  .attr('y1', 480)
  .attr('x2', 80)
  .attr('y2', 480)
  .attr('stroke', 'black')
  .attr('stroke-width', 3);
svg.append('line')
  .attr('x1', 110)
  .attr('y1', 480)
  .attr('x2', 280)
  .attr('y2', 480)
  .attr('stroke', 'black')
  .attr('stroke-width', 3);

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

这里是 jsfiddle:https://jsfiddle.net/50j9wv2w/

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-07-13
    • 2023-04-08
    • 2012-05-07
    • 1970-01-01
    • 2013-12-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多