如何让 WPF UI 滑动动画更加真实?

4
如果我们想滑动屏幕,就需要一些事件。比如鼠标移动事件、鼠标按下事件、鼠标放开事件。但问题是,只有当手指碰触屏幕时才能进行动画。我的意思是:当手指碰触屏幕并移动时,它会移动;当我们放开手指时,它会停止(或给它一个位置以让它停在那里)。但我想要一些动画效果,就像 iPhone 主屏幕一样。如果我们的手指滑动得更快,动画效果也会更快(或者动画距离更远)。就像现在我们使用 Photoshop 一样,当图片缩小到非常大时,当我们移动手更快时,它会移动到很远的地方。另一个例子是它会缓慢减速,而不是立即停止。它还会知道我的手指滑动速度,然后它会相应地滑动缓慢或快速...

4
在网络上搜索“manipulation inertia”。 - Clemens
@DasDas,这是什么意思? - qakmak
@Clemens,我还没有找到关于这个的任何例子... - qakmak
@DasDas,朋友,你的意思我还是不太明白。 - qakmak
从MSDN网站开始阅读: Touch and Manipulation。特别关注ManipulationInertiaStarting事件。 - Clemens
2个回答

3

我在我的程序中使用了一个修改过的版本的代码,该代码可以在此处找到。使用方法很简单,因为它是一个附加的行为,并且可以应用于样式,这样所有的滚动视图都可以自动以这种方式运作。它通过使用您提到的相同的(隧道)事件(OnPreviewMouseDownOnPreviewMouseUpOnPreviewMouseMove)来工作。在处理OnPreviewMouseMove期间,惯性被计算,并且滚动条以模拟物理方式移动。还有一个摩擦属性,可以设置以改变滚动条“滑行”的时间。


2
这是一个我为测试惯性而做的控件示例,使用了ScrollViewer。
希望这能有所帮助。
public partial class HomeFeed : BaseControl
{
    public HomeFeed()
    {
        InitializeComponent();
    }

    private bool IsDragging
    {
        get { return _isDragging; }
        set
        {
            var start = _isDragging && !value;
            _isDragging = value;

            if (start)
            {
                new Thread(x =>
                {
                    var c = 0;
                    while (ApplyVelocity())
                    {
                        c++;
                        Thread.Sleep(15);
                    }

                }).Start();
            }
        }
    }

    private Point _mousePosition;
    private Velocity _velocity = new Velocity();
    private bool _isDragging;

    private void HomeScrollViewer_OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        IsDragging = true;
        _velocity.Reset();
        _mousePosition = e.GetPosition(this);
        e.Handled = true;
    }

    private void HomeScrollViewer_OnPreviewMouseMove(object sender, MouseEventArgs e)
    {
        if (!IsDragging) return;

        var pos = e.GetPosition(this);
        var y = pos.Y - _mousePosition.Y;

        if (y == 0)
        {
            return;
        }

        _velocity.TryUpdate(y);

        HomeScrollViewer.ScrollToVerticalOffset(HomeScrollViewer.VerticalOffset - y);
        _mousePosition = pos;
    }

    private void HomeScrollViewer_OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
    {
        if (!IsDragging) return;

        IsDragging = false;
        e.Handled = true;
    }

    private void HomeScrollViewer_OnMouseLeave(object sender, MouseEventArgs e)
    {
        if (!IsDragging) return;

        IsDragging = false;
        e.Handled = true;
    }

    private bool ApplyVelocity()
    {
        if (IsDragging || _velocity.Value == 0)
        {
            return false;
        }

        Dispatcher.BeginInvoke(new Action(() => HomeScrollViewer.ScrollToVerticalOffset(HomeScrollViewer.VerticalOffset - _velocity.Value)));

        var size = Math.Abs(_velocity.Value);
        var sign = size / _velocity.Value;

        _velocity.Value = sign * Math.Max(0, Math.Min(size*0.95, size - 1));
        return true;
    }
}

public class Velocity
{
    private readonly int _timespan;
    public double Value { get; set; }
    public DateTime SetAt { get; set; }

    public Velocity(int timespan = 1000)
    {
        _timespan = timespan;
        Value = 0;
        SetAt = DateTime.Now;
    }

    public void TryUpdate(double value)
    {
        if (value == 0)
        {
            return;
        }

        if (SetAt.Add(TimeSpan.FromMilliseconds(_timespan)) > DateTime.Now)
        {
            SetAt = DateTime.Now;
            Value = value;
            return;
        }

        if (value*Value < 0)
        {
            SetAt = DateTime.Now;
            Value = value;
            return;
        }

        if (Math.Abs(value) > Math.Abs(Value))
        {
            SetAt = DateTime.Now;
            Value = value;
            return;
        }
    }

    public void Reset()
    {
        Value = 0;
        SetAt = DateTime.Now;
    }
}

你能给一个完整的例子吗? - qakmak

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接