计算滚动惯量/动量?

时间:2011-04-29 14:30:26

标签: c user-interface scroll inertia

如何计算滚动事件的滚动动量

据我所知,在结尾滚动的开头必须有两个时间戳。还必须有一个“轴变化”变量,它基本上是无惯性滚动的量。

这是我当前负责结束滚动的代码:

if ((type == kMXTEnd || type == kMXTMovedOut) && _isScrolling)
    {
        long int finishTime = MXTimestamp();
        printf("SCEnd: Ending scroll at %ld\n",finishTime-_beginTime);

        /* scrollX is the change in X axis */
        /* finishTime is time from touch down to touch up */

        printf("  * Time: %ld ChangeX: %f\n",finishTime,scrollX);

        _isScrolling = FALSE;
        _originalScrollPoint = _scrollPoint;
    }

是否有可能为此计算“惯性加法”?就像惯性所获得的额外偏移量一样,除了主要滚动值之外我还可以滚动。或者我需要获得其他变量吗?

我需要这个,因为我正在编写自己的UI工具包,它实际上并不基于任何东西。

2 个答案:

答案 0 :(得分:5)

您可以使用“最近的轴更改”队列来模拟此情况。

如果您使用相应的时间戳存储说明最后半秒的更改,则可以测试队列是否长于值N(即,如果用户拖动它比平时更快) 。你知道最后半秒的时间,也就是你可以获得速度的时间。

将速度缩放到合理的位置(比如说...对于15px / .5秒,映射到~25px / sec)并应用负加速度(也适用于上面的例子,比如-20px / sec),每两次毫秒(或者你的系统可以轻松处理它的速度快,不要用它来过度压缩它。)

然后运行计时器,更新每个刻度(speed+=accel*time_scale)的速度,然后更新位置(position+=speed*time_scale)。当速度达到0(或低于它)时,杀死计时器。

答案 1 :(得分:5)

我所取得的成绩如下:

在每个鼠标拖动事件(或触摸事件)上,存储速度(因此移动量除以自上一帧以来的时间)和时间戳。你只需要最后一个,所以这只是两个变量。

当释放鼠标/触摸时,检查上一个时间戳是否足够近(我使用0.3秒)。如果是这样,将变量inertialVelocity设置为最后计算的速度;否则将其设置为0以防止在用户仔细选择位置时滚动。

然后在每次更新时(通过计时器或每个渲染调用,取决于你如何渲染),滚动惯性速度* INERTIA_SCROLL_FACTOR(我使用0.9)并将惯性速度乘以INERTIA_ACCELERATION(我使用0.98)。

您可能想要添加一个阈值,因此如果inertialVelocity变得太小,滚动就会停止。我使用1作为阈值,因为我的渲染库使用浮点数作为坐标。如果坐标是积分,它将自己降为零。

要记住的一点是,惯性速度可以是正的也可以是负的,具体取决于方向。

所以,在伪代码中:

OnMouseMove:
    inertialVelocity = moveDistance / (now - timeOfLastEvent)
    timeOfLastEvent = now

OnMouseUp:
    if (now - timeSinceLastEvent > 0.3f)
        inertialVelocity = 0

OnTimer/OnRender:
    // timeDelta is needed only when doing this on render events, just to make
    // it independent of the render speed. It is the time since the previous render
    scrollPosition += inertialVelocity * INERTIA_SCROLL_FACTOR * timeDelta
    inertialVelocity *= INERTIA_ACCELERATION * timeDelta
    // Keep in mind that velocity can be negative as well, hence the abs
    if (abs(inertialVelocity) < INERTIA_THRESHOLD)
        inertialVelocity = 0