【WPF/C#滑动条动画】实现滑动条丝滑动画效果

该文章介绍了一个自定义的ScrollViewer类,通过重写鼠标滚动事件,利用DoubleAnimation创建平滑的滚动动画。代码中包含了一个DispatcherTimer来检测控件的加载状态,以及如何处理垂直滚动条的点击事件和鼠标滚轮事件,确保在滚动时能够根据上一次的位置进行平滑过渡。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

效果图

在这里插入图片描述
重载ScrollViewer类,重写鼠标滚动事件,根据上一次滑动所在位置和滑动位置之间的距离,使用DoubleAnimation做动画效果

代码部分

public class ScrollViewerAnimation : ScrollViewer
{
    //记录上一次的滚动位置
    private double _lastLocation;

    private DispatcherTimer _scrollTemplateTimer = null;
    public ScrollViewerAnimation() : base()
    {
        // 用定时器来判断当前控件的visualTree是否加载完成。
        _scrollTemplateTimer = new DispatcherTimer
        {
            Interval = TimeSpan.FromMilliseconds(100)
        };
        _scrollTemplateTimer.Tick += OnScrollTemplateLoaded;
        _scrollTemplateTimer.Start();
    }

    // 绑定垂直滚动条点击事件。
    // 滚动条被点击后,位置会更新,需要更新_lastLocation后,在下一次滚动时才会得到正确的滚动位置
    private void OnScrollTemplateLoaded(object sender, EventArgs e)
    {
        ScrollBar verScrollBar = (ScrollBar)this.Template.FindName("PART_VerticalScrollBar", this);
        if (verScrollBar != null)
        {
            verScrollBar.MouseMove += VerScrollBarMouseMove; ;
            _scrollTemplateTimer.Stop();
            _scrollTemplateTimer = null;
        }
    }

    private void VerScrollBarMouseMove(object sender, MouseEventArgs e)
    {
        _lastLocation = this.VerticalOffset;
    }

    // 重写回退到顶部的事件
    public new void ScrollToHome()
    {
        // 停止动画
        BeginAnimation(ScrollViewerBehavior.VerticalOffsetProperty, null);

        // 上一次滚动位置置0
        _lastLocation = 0;
        // 回到顶部
        base.ScrollToHome();
    }

    //重写鼠标滚动事件
    protected override void OnMouseWheel(MouseWheelEventArgs e)
    {
        double wheelChange = e.Delta;
        //可以更改一次滚动的距离倍数 (WheelChange可能为正负数!)
        var newOffset = _lastLocation - wheelChange * 2;
        //Animation并不会改变真正的VerticalOffset(只是它的依赖属性) 所以将VOffset设置到上一次的滚动位置 (相当于衔接上一个动画)
        ScrollToVerticalOffset(_lastLocation);
        //碰到底部和顶部时的处理
        if (newOffset < 0)
            newOffset = 0;
        if (newOffset > ScrollableHeight)
            newOffset = ScrollableHeight;

        AnimateScroll(newOffset);
        _lastLocation = newOffset;
        //告诉ScrollViewer我们已经完成了滚动
        e.Handled = true;
    }

    private void AnimateScroll(double toValue)
    {
        //为了避免重复,先结束掉上一个动画
        BeginAnimation(ScrollViewerBehavior.VerticalOffsetProperty, null);
        var animation = new DoubleAnimation
        {
            EasingFunction = new CubicEase { EasingMode = EasingMode.EaseOut },
            From = VerticalOffset,
            To = toValue,
            //动画速度
            Duration = TimeSpan.FromMilliseconds(800)
        };
        //考虑到性能,可以降低动画帧数
        //Timeline.SetDesiredFrameRate(Animation, 40);
        BeginAnimation(ScrollViewerBehavior.VerticalOffsetProperty, animation);
    }
}

源码传送门

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

离歌漠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值