我正在尝试使用我可以在移动应用程序中实现的通用平滑滚动机制。
我希望它足够通用,以便它可以移植到任何平台,但我目前正在使用.net Compact Framework上的C#。
我现在正在做的是:
_lastMouse
velocity = (_lastMouse - curMouse) / Stopwatch.TotalSeconds
,然后重置秒表并重新启动
Stopwatch.TotalSeconds
介于0.02和0.03之间
velocity
值传递给平滑滚动功能,该功能继续滚动面板,直到结束或者增加摩擦导致速度为== 0 我的问题是最后一步。 velocity
值通常在2,000-3,000像素范围内。速度以每秒像素为单位,因此可以预期。我拿秒表(它应该仍在运行),停止它,我找到从最后一次移动开始经过的时间并将velocity
乘以Stopwatch.TotalSeconds
,得到那个距离,然后重置并启动秒表,然后循环回来,重新开始。
预期的结果是刷新之间经过的时间乘以速度应该给出我应该滚动的像素数(根据最后一次鼠标移动)。我的实际结果是,有时面板会飞行,有时它会有很大的移动!逐渐放缓是好的,它只是开始的速度关闭
逻辑上有缺陷吗?我应该做些什么吗?
感谢您的帮助!
答案 0 :(得分:1)
在我看来,这里有三种可能的不准确来源。首先,作为“A.R.”说,如果计时器的粒度不够好,你会遇到问题。您是否已选中IsHighResolution
和Frequency
以确保确定可以吗?
其次,即使您的计时器是完美的,您的位置测量可能会有一些不准确,如果您在非常快速的连续中取两个,那么它可能会伤害您。我的猜测是,这不是什么大问题,但是如果您使用的是电容式触摸屏,那么当手指抬起时,接触区域会下降,可能会导致位置变化。
第三,手指的物理运动(或手写笔或鼠标或你做过实际输入的任何东西;我猜一个手指)可能不是那么好的表现。在手势结束时,它可能会从大多数水平变为大多数垂直。
通过使用更长的采样周期和可能(两种方式尝试)忽略最后一个或两个样本,所有这些问题都会大大减轻。因此,保留最近样本的一个小循环缓冲区,当你让鼠标向上看(比方说)100ms左右,并使用它来决定你的速度。或者,如果您不想保留那么多历史记录,请使用简单的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_dt
和filtered_dy/filtered_dt
作为速度估算值。 (我认为这比每次计算一个速度并过滤它更好,因为如果你得到一个虚假的小dt
,后者仍然会爆炸。如果有疑问,请尝试两种方式。)
如果您使用IIR方法,如果结果不可靠,您可能仍希望忽略最后一个样本;如果您还记得最新的dt
,dx
和dy
,则可以通过撤消上次更新来执行此操作:使用(filtered_dt-SMALL*latest_dt)/(1-SMALL)
等。
这是一个可能会或可能不会起作用的离墙建议。你提到当手势结束时出现“轻弹”时,你会得到更不稳定的结果。也许你可以利用它来获得优势:比如说,估计速度在手势结束时的变化速度有多快,如果它变化得非常快,那就增加你使用的速度。