运行 为每个过渡帧自定义计算
Run custom calculation for each frame of a transition
作为 d3.treemap()
的一部分:
在一个 svg
元素中嵌套了 groups
。每组有一个 rect
和 text
以及一个 tspan
作为 child。
像这样:
<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}; };
如果矩形的 height
和 width
不变,则此方法工作正常。但是用户可以选择不同的选择来改变矩形的大小并触发这个 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 应用程序的响应速度较慢。我可以不使用setInterval
在transition
函数中实现文本居中吗?
在过渡中使用 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>
这里有不同的解决方案。计算机密集度较低的可能只是将新的 x
和 y
值传递给文本,您可以轻松计算这些值(因为您有矩形的新值)。
但是,如果您想使用过渡本身,您可以使用 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>
作为 d3.treemap()
的一部分:
在一个 svg
元素中嵌套了 groups
。每组有一个 rect
和 text
以及一个 tspan
作为 child。
像这样:
<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}; };
如果矩形的 height
和 width
不变,则此方法工作正常。但是用户可以选择不同的选择来改变矩形的大小并触发这个 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 应用程序的响应速度较慢。我可以不使用setInterval
在transition
函数中实现文本居中吗?
在过渡中使用 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>
这里有不同的解决方案。计算机密集度较低的可能只是将新的 x
和 y
值传递给文本,您可以轻松计算这些值(因为您有矩形的新值)。
但是,如果您想使用过渡本身,您可以使用 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>