我正在尝试实现列表对象的动态滚动,但我在根据速度确定要应用的摩擦量(持续时间)时遇到问题。
我的 applyFriction()方法根据duration属性均匀地降低滚动对象的速度。但是,对于每个动作使用相同的持续时间(IE:1秒)似乎并不自然。
对于具有少量速度的运动(IE:5-10像素),1秒的持续时间看起来很好,但是对于具有大量速度(IE:100+像素)的运动,在1秒的持续时间内施加摩擦力滚动对象似乎会慢下来并停止更快。
基本上,我试图确定每个动作的适当持续时间,以便小的和大的速度将共享匹配的摩擦力,因此移动的物体看起来总是具有恒定的“重量”。是否存在基于不同速度确定动能运动持续时间的通用算法?
注意:我正在使用ActionScript 3.0进行编程,并使用Tween类在一段时间内降低移动对象的速度。
答案 0 :(得分:6)
更好的摩擦模型是摩擦力与速度成正比。你需要一个常数来确定力和加速度之间的关系(质量,或多或少)。将关系写成差分方程,
F[n] = -gamma * v[n-1]
a[n] = F[n]/m
v[n] = v[n-1] + dt * a[n]
= v[n-1] + dt * F[n] / m
= v[n-1] - dt * gamma * v[n-1] / m
= v[n-1] * (1 - dt*gamma/m)
因此,如果您希望减速看起来平滑自然,而不是线性降低速度,则需要选择稍微小于1的常数,并将速度反复乘以此常数。当然,这只是渐近逼近零,所以你可能想要一个阈值,低于该阈值你只需将速度设置为零。
所以,例如:
v_epsilon = <some minimum velocity>;
k_frict = 0.9; // play around with this a bit
while (v > v_epsilon) {
v = v * k_frict;
usleep(1000);
}
v = 0;
我想你会发现这看起来更自然。
如果你想通过线性减速来近似这个,那么你会想要减慢与初始速度的自然对数成比例的减速时间。这看起来不太合适,但它看起来会比你现在的好一些。
(为什么自然对数?因为与速度成比例的摩擦力建立了一阶微分方程,它给出了exp(-t / tau)类型的响应,其中tau是系统的特征。衰变的时间在这样的系统中,从任意速度到给定极限与ln(v_init)成正比。)
答案 1 :(得分:4)
之前我曾调查过这个问题:为什么Android动力滚动感觉和iPhone一样好?
幸运的是,a guy already got out a video camera, recorded an iPhone scrolling, and figured out what it does:
当我开始工作时......我没有仔细注意滚动在iPhone上的工作方式。我只是假设减速是基于Newton’s law of motion,即一个接受摩擦的移动体,一段时间后被迫停止。过了一段时间,我意识到这实际上是不 iPhone(以及后来的iOS设备,如iPad)如何做到这一点。使用相机并捕获各种iOS应用程序的几十个滚动动作,我发现所有滚动将在相同的时间后停止,无论列表的大小或轻弹的速度如何。您轻弹的速度(确定滚动的初始速度)仅确定列表将停止的 ,而时。
这导致他大大简化了数学:
amplitude = initialVelocity * scaleFactor;
step = 0;
ticker = setInterval(function() {
var delta = amplitude / timeConstant;
position += delta;
amplitude -= delta;
step += 1;
if (step > 6 * timeConstant) {
clearInterval(ticker);
}
}, updateInterval);
实际上,这就是Apple自己的PastryKit库(现在part of iAd)中减速的实现方式。对于每个动画节拍(16.7毫秒,目标为60 fps),它会将滚动速度降低0.95
倍。这对应于325毫秒的时间常数。如果你是一个数学极客,那么显然你意识到滚动速度的指数性质将产生位置的指数衰减。通过一点点涂鸦,最终你会发现
325 = -16.7 / ln(0.95)
提出动议:
您的问题与持续时间有关。我喜欢iPhone的感觉(与Android相反)。我认为你应该使用1,950 ms:
- (1000 ms / 60) / ln(0.95) * 6 = 1950 ms