对过渡的每一帧运行自定义计算

时间:2018-06-30 20:47:09

标签: javascript d3.js transition

作为d3.treemap()的一部分:

svg元素中嵌套了groups。每个组都有一个recttext,其中一个孩子是tspan。 像这样:

<g><rect></rect><text><tspan>Text here</tspan></text></g>

treemap具有动画效果,并且随着值的改变矩形的大小也随之变化。要求tspan中的文本始终居中。文本元素的中心的计算如下:

const calculateCenterOfTextInRectangle = (tspanNode) => {
const rectangleNode = tspanNode.parentElement.previousSibling.previousSibling; // tspan is in text element and 2 nodes above is the rectangle
const centerX = (rectangleNode.getAttribute('width') / 2) - (tspanNode.getComputedTextLength() / 2);
const centerY = (rectangleNode.getAttribute('height') / 2) + (19 / 2); // 19 is the font-size in pixel
return {centerX: centerX,
    centerY: centerY}; };

如果矩形的heightwidth不变,则可以正常工作。但是用户可以选择其他选项,该选项会更改矩形的大小并触发此d3.transition()

treemap(root.sum(sum)); // new values
cell.transition()
    .duration(750)
    .attr('transform', (d) => 'translate(' + d.x0 + ',' + d.y0 + ')')
    .selectAll('rect')
    .attr('width', (d) => d.x1 - d.x0)
    .attr('height', (d) => d.y1 - d.y0);

这不会更改需要更改的文本的位置-因此,我提出了一个临时解决方案:

setInterval(
        () => {
            cell.selectAll('tspan')
                .attr('x', function() { return calculateCenterOfTextInRectangle(this).centerX; }) // center x and y. Not using Es6 function because of this context which is the tspan element.
                .attr('y', function() { return calculateCenterOfTextInRectangle(this).centerY; });
        },
        0.1
    );

问题是使用setInterval方法时Web应用程序的响应速度较慢。是否可以在不使用transition的情况下在setInterval函数中实现文本居中?

1 个答案:

答案 0 :(得分:2)

在过渡过程中使用calculateCenterOfTextInRectangle的问题在于,即使在为矩形设置新值后调用它,也会得到 initial 值,即预期的行为。

让我们在此演示中看到它,检查控制台:

var svg = d3.select("svg");
var g = svg.append("g");
var rect = g.append("rect")
  .attr("stroke", "black")
  .attr("fill", "white")
  .attr("x", 0)
  .attr("y", 0)
  .attr("width", 50)
  .attr("height", 150);

var text = g.append("text")
  .text("foo")
  .attr("x", function() {
    return calculateCenterOfTextInRectangle(this).centerX
  })
  .attr("y", function() {
    return calculateCenterOfTextInRectangle(this).centerY
  });

var gTransition = g.transition()
  .duration(5000);

gTransition.select("rect")
  .attr("width", 150)
  .attr("height", 40);

gTransition.select("text")
  .attr("x", function() {
    console.log(calculateCenterOfTextInRectangle(this).centerX)
    return calculateCenterOfTextInRectangle(this).centerX
  })
  .attr("y", function() {
    console.log(calculateCenterOfTextInRectangle(this).centerY)
    return calculateCenterOfTextInRectangle(this).centerY
  })

function calculateCenterOfTextInRectangle(tspanNode) {
  const rectangleNode = tspanNode.previousSibling;
  const centerX = (rectangleNode.getAttribute('width') / 2) - (tspanNode.getComputedTextLength() / 2);
  const centerY = (rectangleNode.getAttribute('height') / 2) + (19 / 2); // 19 is the font-size in pixel
  return {
    centerX: centerX,
    centerY: centerY
  };
};
<script src="https://d3js.org/d3.v5.min.js"></script>
<svg></svg>

这里有不同的解决方案。可能不太需要计算机的工作只是将新的xy值传递给文本,您可以轻松地计算出这些文本(因为您具有矩形的新值)。

但是,如果您想使用转换本身,则可以使用tween(这会占用更多的计算机资源),如下所示:

transition.select("text")
    .tween("positioning", function() {
        var self = this;
        var rectangleNode = this.previousSibling;
        return function() {
            d3.select(self)
                .attr("x", (rectangleNode.getAttribute('width') / 2) - (self.getComputedTextLength() / 2))
                .attr("y", (rectangleNode.getAttribute('height') / 2) + (19 / 2))
        }
    })

这是一个演示:

var svg = d3.select("svg");
var g = svg.append("g");
var rect = g.append("rect")
  .attr("stroke", "black")
  .attr("fill", "white")
  .attr("x", 0)
  .attr("y", 0)
  .attr("width", 50)
  .attr("height", 150);
var text = g.append("text")
  .attr("x", function() {
    return calculateCenterOfTextInRectangle(this).centerX
  })
  .attr("y", function() {
    return calculateCenterOfTextInRectangle(this).centerY
  })
  .text("foo");

var gTransition = g.transition()
  .duration(5000);

gTransition.select("rect")
  .attr("width", 150)
  .attr("height", 40);

gTransition.select("text")
  .tween("positioning", function() {
    var self = this;
    var rectangleNode = this.previousSibling;
    return function() {
      d3.select(self)
        .attr("x", (rectangleNode.getAttribute('width') / 2) - (self.getComputedTextLength() / 2))
        .attr("y", (rectangleNode.getAttribute('height') / 2) + (19 / 2))
    }
  })

function calculateCenterOfTextInRectangle(tspanNode) {
  const rectangleNode = tspanNode.previousSibling;
  const centerX = (rectangleNode.getAttribute('width') / 2) - (tspanNode.getComputedTextLength() / 2);
  const centerY = (rectangleNode.getAttribute('height') / 2) + (19 / 2); // 19 is the font-size in pixel
  return {
    centerX: centerX,
    centerY: centerY
  };
};
<script src="https://d3js.org/d3.v5.min.js"></script>
<svg></svg>