计算滚动惯性/动量?

发布于 2024-11-03 18:58:09 字数 725 浏览 1 评论 0原文

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

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

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

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 工具包,它并不是真正基于任何东西。

How would I calculate the scrolling momentum for a scroll event?

I understand that there have to be two timestamps at the beginning of the scrolling at the end. There also has to be an "axis change" variable which is basically the amount to scroll by without inertia.

This is my current code responsible for ending of scrolling:

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

Is it possible to calculate an "inertia addition" for that? Like an additional offset gained by inertia which I can scroll by in addition to the primary scroll value. Or do I need to get additional variables?

I need this because I'm writing my own UI toolkit, which isn't really based on anything.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

假面具 2024-11-10 18:58:09

我所做的并取得良好结果的如下。

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

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

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

您可能需要添加一个阈值,以便在惯性速度变得太小时滚动停止。我使用 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

What I've done with good results is the following.

On each mouse drag event (or touch event), you store the velocity (so the amount of movement divided by the time since the last frame) and a timestamp. You only need the last one, so that's just two variables.

When the mouse/touch is released, check to see if the last timestamp is recent enough (I use 0.3 seconds). If so, set a variable inertialVelocity to the last calculated velocity; otherwise set it to 0 to prevent scrolling if the user carefully selected a position.

Then on every update (either through a timer, or each render call, depending on how you're rendering), scroll by inertialVelocity * INERTIA_SCROLL_FACTOR (I use 0.9) and multiply inertialVelocity by INERTIA_ACCELERATION (I use 0.98).

You'll probably want to add a threshold, so scrolling stops if inertialVelocity becomes too small. I use 1 as a threshold, as my rendering lib uses floats as coordinates. If coordinates are integrals, it'll drop to zero by itself.

One thing to keep in mind is that inertialVelocity can be either positive or negative, depending on the direction.

So, in pseudo code:

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
念﹏祤嫣 2024-11-10 18:58:09

您可以使用“最近的轴更改”队列来模拟这一点。

如果您使用相应的时间戳存储最后半秒的更改,则可以测试队列是否长于值N(即,用户是否比平时更快地将其拖动到末尾) 。你知道最后半秒内行驶的总距离、时间,从中你可以得到速度。

将速度缩放到合理的水平(例如 15 像素/.5 秒,映射到 ~25 像素/秒)并每隔几毫秒(或只要您的系统可以轻松处理它,不要对此施加过大的压力)。

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

You could simulate this with a "recent axis changes" queue.

If you store say the last half a second of changes with the corresponding timestamps, you can then test if the queue is longer than a value N (ie if the user dragged it quicker than usual towards the end). You know the total distance traveled in the last half a second, the time, from those you can get a speed.

Scale the speed to something reasonable (say.. for 15px/.5sec, map to ~25px/sec) and apply a negative acceleration (also appropiately scaled, for the example above, say -20px/sec) every couple of milliseconds (or as fast as your system can easily handle it, don't overstress it with this).

Then run a timer, updating the speed at each tick (speed+=accel*time_scale), then the position (position+=speed*time_scale). When the speed reaches 0 (or goes below it) kill the timer.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文