在d3中转换圆半径以取消缩放转换 - 工作但是时间关闭

时间:2014-09-28 22:40:20

标签: javascript svg d3.js transition

以下是一些示例代码及其中的一小部分:

var w = 400;
var h = 400;
var r = 20;
var factor = 5;

var svg = d3.select("body").append("svg")
    .attr("width", w)
    .attr("height", h)
    .append("g")
    .attr("transform", "translate(" + w/2 + "," + h/2 + ")");

svg.append("circle")
    .attr("cx", 0)
    .attr("cy", 0)
    .attr("r", r)
    .style("fill", "black");

svg.append("circle")
    .attr("cx", 150)
    .attr("cy", 150)
    .attr("r", r)
    .style("fill", "red");

svg.selectAll("circle")
    .transition()
    .duration(2000)
    .attr("transform", "scale(" + 1/factor +")")
    .attr("r", r*factor);

http://jsfiddle.net/o1wzfas7/2/

在这个例子中,我将两个圆圈缩小了5倍(这也缩放了它们的位置,从而使它们彼此“靠近”)并同时将圆圈的半径按比例缩放了5倍。我们的想法是,它们似乎在不改变尺寸的情况下彼此靠近(就好像我正在改变它们的“cx”和“cy”属性),但由于某种原因,尺度转换和半径转换看起来有不同的速度,所以你看到圆圈变大,然后恢复到初始尺寸。

有没有人知道如何使用比例和半径转换来做这件事,但让两个相互抵消,这样圆圈的大小似乎没有变化?

1 个答案:

答案 0 :(得分:1)

首先,解释发生了什么:

问题在于您所做的更改会成倍地抵消,但转换会以相加的方式进行。

因此,对于您的简单示例,其中radius(r)从20开始,比例(隐式)开始为1,并且您将转换因子5,圆的有效半径为r * s :

在过渡开始时:

  • r = 20
  • s = 1
  • r * s = 20

转型结束时:

  • r = 4
  • s = 5
  • r * s = 20

现在,你脑海中想到它的方式是因子应该从1过渡到5,但这不会发生什么。默认的过渡函数看不到您的因子,他们只看到半径从20过渡到4,并且比例从1过渡到5.

因此,在转换的中点,每个属性将处于其起始值和结束值的中点(平均值):

  • r =(20 + 4)/ 2 = 12
  • s =(1 + 5)/ 2 = 3
  • r * s = 36

为了做你想做的事,你将不得不创建一个自定义补间,直接转换因子,然后从那里计算半径和比例:

svg.selectAll("circle")
    .transition()
    .duration(2000)
    .tween("factor", function(d,i){
        /* create an interpolator for your factor */
        var f = d3.interpolateNumber(1,factor);

        /* store the selected element in a variable for easy modification */
        var c = d3.select(this);

        /* return the function which will do the updates at each tick */
        return function(t) {
           var f_t = f(t);
           c.attr("transform", "scale(" + 1/f_t + ")" );
           c.attr("r", r*f_t );
        };
     });

请注意,在您的实际应用程序中,您需要在全局变量或每个数据对象中存储因子转换的“开始”值,因为当您转换到不同的缩放因子时它不会自动为1 。

var w = 400;
var h = 400;
var r = 20;
var factor = 5;

var svg = d3.select("body").append("svg")
    .attr("width", w)
    .attr("height", h)
    .append("g")
    .attr("transform", "translate(" + w/2 + "," + h/2 + ")");

svg.append("circle")
    .attr("cx", 0)
    .attr("cy", 0)
    .attr("r", r)
    .style("fill", "black");

svg.append("circle")
    .attr("cx", 150)
    .attr("cy", 150)
    .attr("r", r)
    .style("fill", "red");


svg.selectAll("circle")
    .transition()
    .duration(2000)
    .tween("factor", function(d,i){
        /* create an interpolator for your factor */
        var f = d3.interpolateNumber(1,factor);

        /* store the selected element in a variable for easy modification */
        var c = d3.select(this);

        /* return the function which will do the updates at each tick */
        return function(t) {
           var f_t = f(t);
           c.attr("transform", "scale(" + 1/f_t + ")" );
           c.attr("r", r*f_t );
        };
     });
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>