我怎样才能相对地缩放两点之间的东西?

How can I relatively scale something between two points?

这更像是一道数学题,我似乎无法在网上找到任何答案。

所以这就是我想要完成的事情:

假设我有一个圆圈,从 Ay 开始。现在随着这个圆圈向 By 移动,我希望它按比例缩放到一定大小。

因此,例如,如果圆的直径在 Ay 处为 5,那么当它达到 By.

时,我如何将其缩放为 52.2

还有一个额外的问题:我可以用正方形实现同样的效果吗?

如果向量v=(By-Ay)AyBy之间的直线可以定义为l(t)=Ay+vt。因此 l(t) 上带有参数 t 的任何点的比例因子为 s=47.5t+5。例如,在 t=0 时,直线上的点 Ay 具有比例因子 s=5。如果你输入 t=1,你会得到 By and scale s= 52.5。对于您的奖励问题,比例因子是相同的,但您不能简单地将正方形的坐标乘以比例因子。您需要使用 l(t) 将正方形平移到原点并缩放坐标并将其平移回 l(t)

对于线性缩放 if

  • D[Ay] 是 circle/square 的 diameter/side 当它在 Ay
  • D[By] 是 circle/square 的 diameter/side 当它在 By
  • D[Cy] 是 circle/square 的 diameter/side 当它在 Cy
  • Ay <= Cy <= By

然后

D[Cy] = D[Ay] + (Cy - Ay) * (D[By] - D[Ay]) / (By - Ay)

根据位置补间。

键和关键帧

在动画中我们将已知的位置和状态定义为关键帧,通常我们根据时间对关键帧进行索引。

// an array of keys. The ? represents the applicable number value
var keys = [{ 
         time : 0, // the state of an object at time 0
         pos : {x : ? , y : ? }, // position
         scale : ?,
         rotation : ?,
         colour : [?,?,?],       // rgb colour, just for the hell of it
         // and whatever else you may want to animate
     },{
         time : 100, // the state of the object at time 100
         pos : {x : ? , y : ? },
         scale : ?,
         rotation : ?,
         colour : [?,?,?],
         // and whatever else you may want to animate
     }
]

归一化时间

为了在关键帧之间的任何时间 t 获取对象的状态,我们找到时间之间的归一化时间(从 0 到 1 的值)并将其乘以其他状态之间的差异,然后将其添加到开始状态。

所以说时间是 50 首先我们得到归一化时间

var currentTime = 50;
var timeDif = currentTime - keys[0].time; // difference from start time to current
// to get the normalised time divid by the differance
var normTime = timeDif / (keys[1].time - keys[0].time); // divide by the differance in time between keys

现在您有了标准化时间,您可以轻松计算任何状态

var scaleDif =  keys[1].scale - keys[0].scale; // get diff in scale
var scaleChange = scaleDif * normTime;  // multiply by the normalised time
var currentScale = keys[0].scale + scaleChange; // add to the starting scale

这有点啰嗦,但这是为了让您轻松了解正在发生的事情。完整的键控功能看起来像。

function tweenKeys(time,key1,key2){
    var nt = (time - key1.time) / (key2.time - key1.time); // get normalised time
    // because you can not divide by zero we need a little check. Javascript return infinity if we div by zero but we want the value 0 
    nt = nt < Infinity ? nt : 0; // zero if there was a divide by zero
    var ck = {}; // ck for current key. the key represents the state at time
    ck.scale = key1.scale + (key2.scale - key1.scale) * nt;
    ck.rotation = key1.rotation + (key2.rotation - key1.rotation ) * nt;
    ck.pos.x = key1.pos.x + (key2.pos.x- key1.pos.x) * nt;
    ck.pos.y = key1.pos.y + (key2.pos.y- key1.pos.y) * nt;
    ck.colour[0] = key1.colour[0] + (key2.colour[0] - key1.colour[0]) * nt;
    ck.colour[1] = key1.colour[1] + (key2.colour[1] - key1.colour[1]) * nt;
    ck.colour[2] = key1.colour[2] + (key2.colour[2] - key1.colour[2]) * nt;
    return ck; // return the newly create state
}

这就是关键帧的基础知识,您可以在此答案中找到更多相关信息

在space而不是时间

一切都很好,但对于您的问题这没有帮助,您没有使用时间而是使用位置来确定对象的当前状态。好吧,我们用什么来找到我们的当前状态并不重要,关键帧中的任何值都可以用来确定所有其他帧的状态。我们需要做的就是找到归一化差异,然后像对所有其他值进行归一化时间一样应用它。

归一化位置

所以让我们看看位置

考虑两个点 p1 和 p2,定义为

var p1 = {x : ?, y : ?}; // ? represent some number value
var p2 = {x : ?, y : ?}; // ? represent some number value

并代表你的仓位A,B

如果我们有第 3 个点 C

var c = {x : ?, y : ?}; // ? represent some number value

二维平面上的某处。我们想要一个公式,当 C 位于点 p1 时 return 为 0,当点 c 位于点 p2 时为 1。这将是我们用于获取当前状态的标准化位置。

由于position是2d,我们需要在计算中同时涉及x和y。我们得到从 p1 到点 c 的距离,并将它除以点 p1 和 p2 之间的距离。这会给我们想要的价值。为了找到距离,我们使用 pythag 解决方案。平方和的根

var dist = Math.sqrt( Math.pow( p2.x - p1.x, 2) + Math.pow( p2.y - p1.y, 2)); // for the twisted world of IE users and
var dist = Math.hypot(p2.x - p1.x, p2.y - p1.y); // for all good browsers

所以归一化距离是

var normDist = Math.hypot(c.x - p1.x, c.y - p1.y) / Math.hypot(p2.x - p1.x, p2.y - p1.y); 
// because you can not divide by zero we need a little check. Javascript returns infinity if we div by zero but we want the value 0 
normDist = normDist < Infinity ? normDist : 0; // zero if there was a divide by zero

然后将 (normDist) 应用于所有关键状态。

var currentScale = (keys[1].scale - keys[0].scale) * normDist + keys[0].scale;

定位问题

好的,谢谢,抱歉,但这不是解决方案,如果你知道点 c 总是在 p1、p2 之间的线上,但情况并非总是如此,并且在严格检查下几乎从来没有,因为计算机存储数字信息,所以在任何需要非常精细细节的计算中都会有一点错误。此外,上述方法将为距 p2 远离 p1 的任何点的归一化距离 return 1,它描述了点 p1 周围的圆。我们需要更多地限制这个值。另外,c 是在点 p1 之前还是在点 p2 之后,这很容易知道。因此我们可以使用下面的方法来做到这一点。

// get the unit distance on the line p1,p2 of point c representing 
// the distance along the line that is closest to c
function unitDistOfPoint(p1,p2,c){
    var v1 = {}; // working vectors
    var v2 = {}; 
    v1.x = p2.x - p1.x; // vector between p1,p2
    v1.y = p2.y - p1.y;
    v2.x = c.x - p1.x;  // vector to c from p1
    v2.y = c.y - p1.y;
    // a little math magic. Divide the dot product of the vectors v2, v1
    // by the square of line length
    return (v2.x * v1.x + v2.y * v1.y) / (v1.y * v1.y + v1.x * v1.x);
}

现在我们可以进行补间并获得您的比例

// return the state for a object at point c in terms of key1, to key2
function tweenKeysViaPos(c,key1,key2){
    // get the normalised distance of the point c between keys 1 and 2
    var nd = unitDistOfPoint(c, key1.pos, key2.pos); // nd for normalised distance
    // you may want to constrain the position to only between the points 
    // do that by clamping the value nd between 0 and 1 inclusive
    nd = Math.max(0, Math.min(1, nd)); // clamp the normalise distance
    var ck = {}; // ck for current key. the key represents the state at time
    ck.scale = key1.scale + (key2.scale - key1.scale) * nt;
    ck.rotation = key1.rotation + (key2.rotation - key1.rotation ) * nt;
    ck.pos.x = key1.pos.x + (key2.pos.x- key1.pos.x) * nt;
    ck.pos.y = key1.pos.y + (key2.pos.y- key1.pos.y) * nt;
    ck.colour[0] = key1.colour[0] + (key2.colour[0] - key1.colour[0]) * nt;
    ck.colour[1] = key1.colour[1] + (key2.colour[1] - key1.colour[1]) * nt;
    ck.colour[2] = key1.colour[2] + (key2.colour[2] - key1.colour[2]) * nt;
    return ck; // return the newly create state
}

这就是答案。作为一个附带的好处,如果点 c 确实偏离了键之间的线,那么上面的函数也会 return 它应该在的位置。

如果需要更多

您可能希望对其进行扩展以适应许多关键帧。通常对于两个以上的关键帧和使用时间,通过找到时间大于第一个关键帧且小于下一个关键帧的位置,很容易找到我们想要的关键帧。但是,如果您使用位置来确定您所在的键,这就不是那么简单了。因此,为了帮助解决更复杂的问题,您会发现此功能很方便

// returns the distance point c is from the line p1,p2. If on the line
// the the return value is 0. If befor point p1 or after p2 then the distance
// is the distance to p1, or p2 respectively
function distFromLine(p1,p2,c){
    var v1 = {}; // working vectors
    var v2 = {}; 
    v1.x = p2.x - p1.x; // vector between p1,p2
    v1.y = p2.y - p1.y;
    v2.x = c.x - p1.x;  // vector to c from p1
    v2.y = c.y - p1.y;
    // a little math magic. Divide the dot product of the vectors v2, v1
    // by the square of line length
    var u = (v2.x * v1.x + v2.y * v1.y) / (v1.y * v1.y + v1.x * v1.x);
    var v3 = {};
    if(u < 0){ // befor the start
        return Math.hypot(v2.x,v2.y); // distance to p1
    }
    if(u > 1){ // after end
        return Math.hypot(c.x - p2.x,c.y p2.y); // distance to p2
    }
    // get the point on the line that is closest
    v3.x = p1.x + v1.x * u;
    v3.y = p1.y + v1.y * u;
    // return the distance from that point to c
    return Math.hypot(c.x - v3.x,c.y - v3.y); // distance from line of c
}

然后,您可以通过找到距离它们之间的线 return 最小距离的键来找到您需要的两个键。你然后通过定义许多关键帧来定义一条复杂的线,无论你把一个对象放在哪里,你都可以计算它应该在哪里以及处于什么状态。

希望这对您有所帮助,不要过头了。如果任何阅读者不清楚,请在评论中说明,我会澄清。