代码之家  ›  专栏  ›  技术社区  ›  Chunky Chunk

基于速度的动态滚动(动量)持续时间?

  •  1
  • Chunky Chunk  · 技术社区  · 14 年前

    我试图实现列表对象的动态滚动,但我在根据速度确定要应用的摩擦量(持续时间)时遇到问题。

    我的 应用程序定价() 方法基于持续时间属性均匀地降低滚动对象的速度。然而,对每一个动作使用相同的持续时间(即:1秒)看起来并不自然。

    对于具有少量速度(即:5-10像素)的运动,1秒的持续时间看起来不错,但是对于具有大量速度(即:100+像素)的运动,在1秒的持续时间内应用摩擦力,滚动对象将看起来慢得多,停止得快得多。

    本质上,我试图为每一个运动确定合适的持续时间,这样无论是小速度还是大速度都会共享一个匹配的摩擦力,所以运动的物体看起来总是有一个恒定的“重量”。

    有没有根据不同的速度确定运动持续时间的通用算法?


    注意:我在ActionScript3.0中编程,并使用Tween类在一段时间内降低移动对象的速度。

    2 回复  |  直到 14 年前
        1
  •  6
  •   kwantam    14 年前

    一个更好的摩擦模型是摩擦力与速度成正比。你需要一个常数来确定力和加速度(质量,或多或少)之间的关系。把关系写成差分方程,

    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)成正比。)

        2
  •  4
  •   Community holdenweb    7 年前

    我以前研究过这个问题:为什么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(以及后来的iPad等iOS设备)是如何做到的。我用相机拍摄了几十个不同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);
    

    事实上,这就是减速是如何在苹果中实现的 PastryKit 图书馆(现在 part of iAd ). 它将滚动速度降低一倍 0.95 对于每个动画刻度(16.7毫秒,目标为60 fps)。这对应于325毫秒的时间常数。如果你是一个数学怪人,那么很明显你会意识到,涡旋速度的指数特性会在这个位置产生指数衰减。稍加涂鸦,最终你会发现

    325 = -16.7 / ln(0.95)
    

    提出动议:

    enter image description here


    你的问题是关于 期间 使用。我喜欢iPhone的感觉(与Android相反)。我觉得你应该用 1,950 ms :

    - (1000 ms / 60) / ln(0.95) * 6 = 1950 ms