生成的svg对象在D3

时间:2016-06-06 18:08:42

标签: javascript math d3.js svg

让我先从上下文开始:

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

我已经能够成功构建我需要的所有对象,并且随机化刷新,这对于我的需求来说是完美的,但是我在对齐方面遇到了麻烦。

这是(有点凌乱)的例子:CodePen - Montessori Number Generator

该概念基于this example

我更喜欢列在空间中居中,但是我还没有能够计算出它的数学结果(我最终将数字向左对齐,单位为右)。

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

对于Tens组:

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

单位的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的倍数(即单位组不能为零。)

感谢您的任何想法!

2 个答案:

答案 0 :(得分:0)

我会这样做。

//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/

答案 1 :(得分:0)

为什么不使用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>

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