移动设备上的滚动势头

发布于 2024-10-25 07:24:29 字数 839 浏览 8 评论 0原文

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

我希望它足够通用,以便可以移植到任何平台,但我目前正在 .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
  • 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 技术交流群。

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

发布评论

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

评论(1

倥絔 2024-11-01 07:24:30

在我看来,这里存在三个可能的不准确性来源。首先,正如“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 方法,您可能仍然希望让它忽略最后一个样本(如果结果不可靠);如果您还记得最新的 dtdxdy,您可以通过撤消上次更新来做到这一点:使用 (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 and Frequency 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

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

where SMALL should be, at a guess, somewhere around 0.2 or so; then use filtered_dx/filtered_dt and filtered_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 small dt. 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 and dy 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.

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