我怎样才能在两点之间相对缩放一些东西? [英] How can I relatively scale something between two points?

查看:31
本文介绍了我怎样才能在两点之间相对缩放一些东西?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

This is more of a math question, and I couldn't seem to find any answers online.

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

So here is what I am trying to accomplish:

假设我有一个圆圈,从 Ay 开始.现在当这个圆圈向 By 移动时,我希望它按比例缩放到某个大小.

Lets say I have a circle, starting at Ay. Now as this circle moves towards By, I want it to scale proportionally to a certain size.

例如,如果圆的直径在 Ay 处为 5,我如何在它到达 By 时将其缩放为 52.2.

So for example, if the circle's diameter was 5 at Ay, how could I scale it to become 52.2 by the time it reaches By.

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

And bonus question: could I achieve this same thing with a square?

推荐答案

位置补间.

键和关键帧

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

In animation we define known positions and states as key frames, normally we index the key frames in terms of time.

// 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 的值)并将其乘以其他状态之间的差异,然后将其添加到开始状态.

To get the state of an object at any time t between the key frames we find the normalised time ( a value from 0 to 1) between the times and multiply that to the difference between other states then add that to the beginning state.

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

So say the time is 50 first we get the normalised time

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

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

Now you have the normalised time you can easily calculate any of the states

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

这有点啰嗦,但这是为了让您轻松了解正在发生的事情.完整的键控功能应该是这样的.

That is all a little long winded but that is to ease you into what is happening. the complete keying function would look like.

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
}

这就是关键帧的基础知识,您可以在此答案中找到更多相关信息我将如何设置动画...

Thats the basics of keyframing and you can find more on it in this answer How would I animate... ?

在空间而不是时间

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

All good but for your problem this has not helped, you are not using time you are using position to determine the current state of the object. Well it does not matter what we use to find our current state, any of the values in the key frame can be used to determine that state of all the others. All we need to do is find the normalised difference and then apply that like we did normalised time to all the other values.

标准化位置

所以让我们看看位置

考虑两点 p1 和 p2,定义为

Consider two points p1 and p2, defined as

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

并代表您的位置 A、B

And representing your positions A,B

如果我们有第三个点 C

If we have a 3rd point C

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

2D 平面上的某处.我们想要一个公式,当 C 在点 p1 时返回 0,当点 c 在点 p2 时返回 1.这将是我们用于获取当前状态的标准化位置.

somewhere on the 2D plane. We want a formula that will return a 0 when C is at point p1 and 1 when the point c is at point p2. This will be our normalised position used to get the current state.

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

As position is 2d we need to involve both the x and y in the calculations. We get the distance from p1 to point c and the divide that by the distance between point p1 and p2. that will give us the value we want. To find the distance we use the pythag solution. root of the sum of the squares

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) 应用到所有关键状态.

Then apply that (normDist) to all the key states.

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

定位问题

好的,你说谢谢,对不起,但这不是解决方案,如果你知道点 c 总是在 p1,p2 之间的线上,但情况并非总是如此,并且在严格的检查下它是几乎从来没有,因为计算机存储数字信息,因此任何需要非常精细细节的计算都会出现一点错误.此外,上述方法将返回 1 表示任何距离 p2 距离 p1 的距离的归一化距离,该点描述了围绕点 p1 的圆.我们需要更多地限制这个值.此外,如果 c 在点 p1 之前或在点 p2 之后,知道会很方便.因此,我们可以使用以下方法来做到这一点.

Ok you say thanks, sorry but that is not the solution, it would be if you knew that the point c is always on the line between p1, p2 but that is not always the case, and under a strict examination it is hardly ever because computers store digital information so there will be a little error in any calculation that requires very fine detail. Also the above method will return 1 for normalized distance for any point that is distance to p2 away from p1, that describes a circle around the point p1. We need to constrain this value a bit more. Also if c is befor the point p1 or after the point p2 it would be handy to know. Thus we can use the following to do so.

// 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);
}

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

Now we can do the tweening and get your scale

// 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 确实偏离了键之间的线,那么上述函数也会返回它应该位于的位置.

That is the answer. As a side benefit if the point c does stray away from the line between the keys then the above function also return the position it should be.

如果需要更多

您可能想要扩展它以适应许多关键帧.通常对于两个以上的关键帧和使用时间,通过查找时间大于第一个键且小于下一个键的位置,很容易找到我们想要的键.但是,如果您使用该位置来确定您处于哪个关键位置,则这并不那么简单.所以为了帮助一个更复杂的解决方案,你会发现这个功能很方便

You may want to extend this to adapt to many key frames. Normally for more than two key frames and using time it is easy to find the keys that we want by finding where time is greater than the first key and less than the next key. But this is not as simple if you are using the position to work out at which key you are at. So to help a more complex solution you will find this function handy

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

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

You can then find the two keys you need by finding the keys that return the smallest distance from the line between them. You and then define a complicated line by defining many key frames and where ever you put an object you can calculate where it should be and in what state.

希望这会有所帮助,但不要过度.如果阅读的人有任何不清楚的地方,请在评论中说出来,我会澄清.

Hope this helps and did not go over the top. If anything is unclear to anyone that reads please do say so in the comments and I will clarify.

这篇关于我怎样才能在两点之间相对缩放一些东西?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆