2

我正在尝试获得一个可以在我的移动应用程序中实现的通用平滑滚动机制。

我希望它足够通用,以便可以移植到任何平台,但我目前正在.net Compact Framework 上使用 C#。

我现在正在做的是:

  • 创建一个秒表对象(在面板的 ctor 中)
  • 在鼠标按下时启动秒表并保存当前鼠标点_lastMouse
  • 在鼠标移动时,停止秒表并存储,velocity = (_lastMouse - curMouse) / Stopwatch.TotalSeconds然后重置秒表并重新启动
    • 大多数情况下Stopwatch.TotalSeconds介于 0.02 和 0.03 之间
  • 在鼠标向上时,我将velocity值传递给平滑滚动函数,并且该函数继续滚动面板,直到到达末端或增加的摩擦力导致速度 == 0

我的问题是在最后一步。这些velocity值通常在 2,000-3,000 像素范围内。速度以每秒像素为单位,因此这是意料之中的。我拿起秒表(它应该仍在运行),停止它,我找到从最后一次鼠标移动经过的时间并乘以velocityStopwatch.TotalSeconds得到那个距离,然后重置并启动秒表,然后循环回来并重新开始。

预期的结果是刷新之间的经过时间乘以速度应该给我应该滚动的像素数(根据最后一次鼠标移动)。我的实际结果是,有时面板会飞起来,有时会移动!逐渐减速很好,只是开始速度关闭

逻辑上有缺陷吗?我应该做点别的吗?

谢谢你的帮助!

4

1 回答 1

1

在我看来,这里有三个可能的不准确来源。首先,正如“AR”所说,如果你的计时器的粒度不够好,你就会遇到问题。你检查IsHighResolutionFrequency确保它没问题吗?

其次,即使您的计时器是完美的,您的位置测量也可能存在一些不准确,如果您连续快速连续两次,那么它可能会伤害您。我的猜测是这没什么大不了的,但是例如,如果您使用的是电容式触摸屏,那么当手指抬起时,随着接触面积的减小,您的位置可能会发生变化。

第三,手指的物理运动(或手写笔或鼠标或任何你有做实际输入的东西;我猜是手指)可能不是那么好。在手势结束时,它可能会从大部分水平变为大部分垂直。

通过使用更长的采样周期并且可能(尝试两种方式)忽略最后一个或两个样本,所有这些问题都将得到显着缓解。因此,为最近的样本保留一个小循环缓冲区,当您将鼠标向上看时(例如)100 毫秒左右,并使用它来决定您的速度。或者,如果您不想保留那么多历史记录,请使用简单的 IIR 过滤器:每次获得样本时,请执行以下操作

filtered_dt = filtered_dt + SMALL*(latest_dt-filtered_dt);
filtered_dx = filtered_dx + SMALL*(latest_dx-filtered_dx);
filtered_dy = filtered_dy + SMALL*(latest_dy-filtered_dy);

其中 SMALL 应该在 0.2 左右;然后使用filtered_dx/filtered_dtfiltered_dy/filtered_dt作为你的速度估计。(我认为这比每次计算速度并过滤它要好,因为例如,如果你得到一个虚假的小速度,后者仍然会爆炸dt。如果有疑问,请尝试两种方式。)

如果你使用 IIR 方法,你可能仍然想让它忽略最后一个样本,如果结果不可靠的话;如果你记得最新的,dt你可以通过撤消最后的更新来做到这一点:使用等。dxdy(filtered_dt-SMALL*latest_dt)/(1-SMALL)

这是一个现成的建议,可能有效,也可能无效。你提到当手势结束时有一个“轻弹”时,你会得到更不稳定的结果。也许您可以利用它来发挥自己的优势:例如,查看估计的速度在手势结束时的变化速度,如果变化非常迅速,则稍微增加您使用的速度。

于 2011-03-25T22:15:59.520 回答