移动设备上的滚动势头
我正在尝试获得一个可以在我的移动应用程序中实现的通用平滑滚动机制。
我希望它足够通用,以便可以移植到任何平台,但我目前正在 .net Compact Framework 上使用 C# 工作。
我现在正在做的是:
- 在鼠标按下时创建一个秒表对象(在面板的ctor中)
- 启动秒表并
- 在鼠标移动时保存当前鼠标点
_lastMouse
,停止秒表并存储< code>velocity = (_lastMouse - curMouse) / Stopwatch.TotalSeconds 然后重置秒表并再次启动- 大多数情况下,
Stopwatch.TotalSeconds
介于 0.02 到 0.03 之间
- 大多数情况下,
- ,我将
velocity
值传递给平滑滚动函数,并且该函数继续滚动面板直到末端被击中或增加的摩擦导致速度 == 0
我的问题是在最后一步。 velocity
值通常在 2,000-3,000 像素范围内。速度以每秒像素为单位,因此这是可以预料的。我拿起秒表(它应该仍在运行),将其停止,然后找到自上次鼠标移动以来经过的时间,并将velocity
乘以Stopwatch.TotalSeconds
,得到该距离并然后重置并启动秒表,然后循环返回并重新开始。
预期的结果是刷新之间经过的时间乘以速度应该给我应该滚动的像素数(根据最后一次鼠标移动)。我的实际结果是,有时面板飞起来,有时却不动!逐渐减速没问题,只是开始速度不对,
逻辑上有缺陷吗?我应该做点别的事吗?
感谢您的帮助!
I'm trying to get a general smooth scrolling mechanism that I can implement in my mobile applications.
I want it to be generic enough so that it can port to any platform, but I am currently working in C# on the .net Compact Framework.
What I'm doing right now is:
- Create a Stopwatch object (in the panel's ctor)
- on mouse down start the Stopwatch and save the current mouse point
_lastMouse
- on mouse move, stop the Stopwatch, and store
velocity = (_lastMouse - curMouse) / Stopwatch.TotalSeconds
then reset the Stopwatch and start it again- In most cases
Stopwatch.TotalSeconds
is between 0.02 and 0.03
- In most cases
- on mouse up, I pass the
velocity
value into a smooth scrolling function, and that function continues to scroll the panel until either the end is hit or the increasing friction causes the velocity to be == 0
My problem is in the final step. The velocity
values are generally int the 2,000-3,000 pixel range. The velocity is in pixels per second, so this is to be expected. I take the Stopwatch (which should be still running), stop it and I find the elapsed time from last mous move and multiply velocity
by Stopwatch.TotalSeconds
, get that distance and then reset and start the Stopwatch, then loop back and start all over again.
The expected result is that the elapsed time between refreshes multiplied by the velocity should give me the number of pixels (according to the last mouse move) that I should scroll. My actual result is that sometimes the panel goes flying and sometimes it bearly moves! the gradual slowdown is fine, it's just the beginning velocity that is off
Is there a flaw in the logic? Should I be doing something else?
Thanks for any help!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
在我看来,这里存在三个可能的不准确性来源。首先,正如“AR”所说,如果你的计时器的粒度不够好,你就会遇到问题。您是否检查了
IsHighResolution
和Frequency
以确保确定没问题?其次,即使你的计时器是完美的,你的位置测量也可能会有些不准确,如果你连续快速地连续测量两次,那么可能会伤害你。我的猜测是,这不是什么大问题,但例如,如果您使用的是电容式触摸屏,那么当手指抬起时,随着接触面积的下降,您的位置可能会发生变化。
第三,手指(或手写笔、鼠标或任何进行实际输入的东西;我猜是手指)的物理运动可能表现得不太好。在手势结束时,它可能从主要水平变为主要垂直。
通过使用更长的采样周期并且也许(尝试两种方法)忽略最后一个或两个样本,所有这些问题都可以得到显着缓解。因此,保留最近样本的一点循环缓冲区,当您抬起鼠标时,向后查看(例如)100 毫秒或左右,并使用它来决定您的速度。或者,如果您不想保留那么多历史记录,请使用简单的 IIR 滤波器:每次获得样本时,执行类似
SMALL 应该在哪里的操作,猜测约为 0.2 左右;然后使用
filtered_dx/filtered_dt
和filtered_dy/filtered_dt
作为速度估计。 (我认为这比每次计算速度并对其进行过滤要好,因为例如,如果您得到一个虚假的小dt
,后者仍然会爆炸。如果有疑问,请尝试两种方法。)如果您使用 IIR 方法,您可能仍然希望让它忽略最后一个样本(如果结果不可靠);如果您还记得最新的
dt
、dx
和dy
,您可以通过撤消上次更新来做到这一点:使用(filtered_dt-SMALL* latest_dt)/(1-SMALL)
等。这是一个古怪的建议,可能有效,也可能无效。您提到当手势结束时出现“轻弹”时,您会得到更不稳定的结果。也许您可以利用这一点来发挥自己的优势:例如,看看手势结束时估计速度的变化速度有多快,如果变化非常快,则稍微增加您使用的速度。
It seems to me that there are three possible sources of inaccuracy here. Firstly, as "A.R." said, you'll have problems if the granularity of your timer isn't good enough. Have you checked
IsHighResolution
andFrequency
to make sure it's OK?Secondly, even if your timer is perfect, there may be some inaccuracy in your position measurements, and if you're taking two in very quick succession then it may hurt you. My guess is that this isn't a big deal, but e.g. if you're on a capacitive touchscreen then as the finger lifts off you may get variation in position as the contact area goes down.
Thirdly, the physical motion of the finger (or stylus or mouse or whatever you've got doing the actual input; I'm going to guess a finger) may not be all that well behaved. At the end of a gesture, it may change from being mostly horizontal to being mostly vertical.
All these problems would be substantially mitigated by using a longer sampling period and maybe (try it both ways) ignoring the very last sample or two. So, keep a little circular buffer of recent samples, and when you get the mouse-up look back (say) 100ms or thereabouts and use that to decide your velocity. Or, if you don't want to keep that much history, use a simple IIR filter: every time you get a sample, do something like
where SMALL should be, at a guess, somewhere around 0.2 or so; then use
filtered_dx/filtered_dt
andfiltered_dy/filtered_dt
as your velocity estimate. (I think this is better than calculating a velocity every time and filtering that, because e.g. the latter will still blow up if you ever get a spuriously smalldt
. If in doubt, try both ways.)If you use the IIR approach, you may still want to make it ignore the very last sample if that turns out to be unreliable; if you remember the latest
dt
,dx
anddy
you can do that by undoing the last update: use(filtered_dt-SMALL*latest_dt)/(1-SMALL)
, etc.Here's an off-the-wall suggestion that may or may not work. You mentioned that you get more erratic results when there's a "flick" at the end of the gesture. Perhaps you can use that to your advantage: look at, say, how rapidly the estimated velocity is changing right at the end of the gesture, and if it's changing very rapidly then increase the velocity you use somewhat.