本文告诉大家一个有趣的动画,在鼠标点击的时候,在点击所在的点显示一个圆圈,然后这个圆圈做动画变大,但是颜色变淡的效果。本文的控件可以让大家将对应的容器放在自己应用里面就能实现这个效果

这个效果特别简单,属于入门级的动画,代码也很少,请看效果

本文的控件只是一个简单的 Canvas 控件,可以将本文的这个控件替换为你自己需要的控件。或者复制本文的代码,放在你自己的项目里面,只需要让你的项目里面有一个 Canvas 同时这个 Canvas 能接收鼠标事件就能作出本文效果

先在界面放一个 Canvas 控件

上面代码有一个细节是 Background="Transparent" 默认的 Canvas 的背景是 null 也就是不接收命中测试,也就是设置 MouseDown 没有反映。什么是命中测试?就是点击的时候,看命中到哪个元素,如果容器没有设置背景,那么这个容器就不能接收命中测试,也就是点击的时候不会判断点击到这个容器

在后台代码添加鼠标点击的代码

如何在 WPF 中显示一个圆圈? 在 WPF 可以通过 Ellipse 控件显示椭圆,如果设置他的宽度和高度相同,那么就是一个圆,添加一个 Ellipse 的代码请看下面

            var currentSize = 10;

            var ellipse = new Ellipse()
{
Width = currentSize,
Height = currentSize,
Fill = Brushes.Gray
};

上面代码的 Fill 是设置填充颜色,而要设置圆圈的边框颜色可以使用 Stroke 属性,设置边框粗细使用 StrokeThickness 属性

如何在鼠标点击的地方显示一个圆圈? 在 WPF 中,可以通过 GetPosition 方法拿到鼠标相对于某个元素的坐标,或者说鼠标点击到某个元素的坐标。通过 TranslateTransform 的方法可以设置某个元素的坐标

获取鼠标相对于 Canvas 的坐标的方法如下

    var point = e.GetPosition(Canvas);

为什么需要有鼠标获取的时候,是相对于某个控件?原因是不同的控件的坐标是不同的,鼠标点击的绝对坐标是屏幕,但是应用的控件一般都是相对于上一层容器,如窗口等。假设此时的鼠标点击屏幕坐标是 (100,100) 而应用窗口坐标是 (10,10) 那么窗口里面的 x 元素想要知道此时鼠标点击在哪,难道还需要 x 控件自己去拿到当前窗口坐标在哪,然后换算出鼠标点击到 x 空控件的哪里?这样的做法太渣了,所以 WPF 框架就提供了 GetPosition 拿到相对于某个元素的鼠标点击

在拿到鼠标点击到 Canvas 的坐标时如何设置刚才创建的圆圈的坐标,可以通过 TranslateTransform 方法,请看代码

            var translateTransform = new TranslateTransform(point.X, point.Y);
ellipse.RenderTransform = translateTransform;

注意 TranslateTransform 的作用是设置水平和垂直平移,需要设置到对应元素的 RenderTransform 里面。这些变换的方法包括了缩放和旋转等。用变换的方法做动画的效率相对会比较高

接下来就是动画的部分了,在 WPF 中的动画需要通过 Storyboard 故事板触发,而通过具体的 Animation 执行对不同的属性的更改。也就是一个 Storyboard 里面包含多个不同的动画,而每个动画都对特定的某个对象的某个属性的更改,通过更改属性的方式做到让某个对象做动画

本文需要做的动画包括让圆圈变大,修改圆圈透明度

让圆圈变大的方法就是修改 Ellipse 的宽度和高度,可以试试下面的方法

            var storyboard = new Storyboard();
var widthAnimation = new DoubleAnimation(toValue: toSize, new Duration(TimeSpan.FromSeconds(1)));
Storyboard.SetTargetProperty(widthAnimation, new PropertyPath("Width"));
Storyboard.SetTarget(widthAnimation, ellipse);
storyboard.Children.Add(widthAnimation); var heightAnimation = new DoubleAnimation(toValue: toSize, new Duration(TimeSpan.FromSeconds(1)));
Storyboard.SetTargetProperty(heightAnimation, new PropertyPath("Height"));
Storyboard.SetTarget(heightAnimation, ellipse);
storyboard.Children.Add(heightAnimation); storyboard.Begin();

上面代码使用 DoubleAnimation 作出连续的动画,在使用 DoubleAnimation 时将会从对应属性的当前值修改到指定值,修改的速度可以通过速度函数设置,默认使用匀速动画。动画的时间通过 Duration 设置

设置完成之后通过 Storyboard.SetTargetProperty 这个静态方法,将 Animation 和对应的元素的属性路径关联起来,也就是 PropertyPath 的作用。关联的时候需要关联属性路径和作用的元素,也就是下面两句代码

            Storyboard.SetTargetProperty(widthAnimation, new PropertyPath("Width"));
Storyboard.SetTarget(widthAnimation, ellipse);

将 Animation 添加到 storyboard 才能在 storyboard 开始的时候执行

通过相同的方法设置高度,然后尝试开启动画

            storyboard.Begin();

此时点击 Canvas 容器的时候,就可以看到在鼠标点击显示圆圈,然后圆圈不断变大

当然,还有下一步就是让圆圈变淡,在 WPF 中可以通过修改圆圈的透明度做动画,请看代码

            var opacityAnimation = new DoubleAnimation(toValue: 0, new Duration(TimeSpan.FromSeconds(1)));
Storyboard.SetTargetProperty(opacityAnimation, new PropertyPath("Opacity"));
Storyboard.SetTarget(opacityAnimation, ellipse);
storyboard.Children.Add(opacityAnimation);

在 WPF 中使用 Opacity 表示透明度,准确说是不透明度,使用 1 表示完全不透明,使用 0 表示全透明。小伙伴都知道,如果是全透明,也就是看不见

在 Animation 类提供了两个属性,一个是 From 另一个是 To 分别表示让属性从哪里什么值开始修改到哪个值。而 From 属性不设置的话就是从当前值开始

注意上面代码需要放在 storyboard.Begin(); 前面,不要在动画开始之后再添加 Animation 不然动画没有执行

此时运行代码大概可以看到本文的效果,但是还有一点细节是,刚才只是修改元素的大小,但是元素的左上角不变,也就是在做元素变大的动画时候,其实可以看到不是通过圆心开始变大的

一个优化的方法是在元素做变大的动画的时候,同时修改元素的左上角的坐标,修改左上角移动多少?可以修改移动变大的一半,如从 10 到 15 也就是移动 2.5 单位。在 WPF 中的单位不一定是像素,因为 WPF 和屏幕具体分辨率等有很复杂的关系,详细请看本文最后的参考文档

还记得刚才是如何修改元素的坐标?通过 TranslateTransform 方法修改圆圈的坐标,也就是动画也可以通过修改 TranslateTransform 的 X 和 Y 属性做动画

和上面代码相同,设置 DoubleAnimation 设置 X 和 Y 属性的值。只是这里的属性不是一级的,因为是通过 TranslateTransform 放到 RenderTransform 里面,此时的属性路径相对就长一点

            // ( ToWidth(15) - CurrentWidth(10) ) / 2 = 2.5
var translateTransformX = translateTransform.X - (toSize - currentSize) / 2;
var xAnimation = new DoubleAnimation(toValue: translateTransformX, new Duration(TimeSpan.FromSeconds(1)));
Storyboard.SetTargetProperty(xAnimation,
new PropertyPath("(UIElement.RenderTransform).(TranslateTransform.X)"));
Storyboard.SetTarget(xAnimation, ellipse);
storyboard.Children.Add(xAnimation);

如上文说的,设置 translateTransformX 的坐标为放大的宽度减去原先的一半,也就是从原先的 10 修改为 15 的一半

而PropertyPath的就是拿到对应的 RenderTransform 属性的值,强行转换为 TranslateTransform 然后拿到 X 属性

对另一个属性也做相同的动画

            var translateTransformY = translateTransform.Y - (toSize - currentSize) / 2;
var yAnimation = new DoubleAnimation(toValue: translateTransformY, new Duration(TimeSpan.FromSeconds(1)));
Storyboard.SetTargetProperty(yAnimation,
new PropertyPath("(UIElement.RenderTransform).(TranslateTransform.Y)"));
Storyboard.SetTarget(yAnimation, ellipse);

此时运行代码就能看到本文的效果了

但是点击了很多次之后,会在实时可视化树里面看到 Canvas 存在很多看不到的圆圈元素,原因是这些元素只是透明度是 0 看不到,但是依然在视觉树上面,可以在动画播放完成之后,删除这个元素,请看代码

            storyboard.Completed += (o, args) => { Canvas.Children.Remove(ellipse); };

本文鼠标点击的代码如下

        private void Canvas_OnMouseDown(object sender, MouseButtonEventArgs e)
{
var toSize = 15;
var currentSize = 10; var ellipse = new Ellipse()
{
Width = currentSize,
Height = currentSize,
Fill = Brushes.Gray
}; var point = e.GetPosition(Canvas);
var translateTransform = new TranslateTransform(point.X, point.Y);
ellipse.RenderTransform = translateTransform;
Canvas.Children.Add(ellipse); var storyboard = new Storyboard();
var widthAnimation = new DoubleAnimation(toValue: toSize, new Duration(TimeSpan.FromSeconds(1)));
Storyboard.SetTargetProperty(widthAnimation, new PropertyPath("Width"));
Storyboard.SetTarget(widthAnimation, ellipse);
storyboard.Children.Add(widthAnimation); var heightAnimation = new DoubleAnimation(toValue: toSize, new Duration(TimeSpan.FromSeconds(1)));
Storyboard.SetTargetProperty(heightAnimation, new PropertyPath("Height"));
Storyboard.SetTarget(heightAnimation, ellipse);
storyboard.Children.Add(heightAnimation); var opacityAnimation = new DoubleAnimation(toValue: 0, new Duration(TimeSpan.FromSeconds(1)));
Storyboard.SetTargetProperty(opacityAnimation, new PropertyPath("Opacity"));
Storyboard.SetTarget(opacityAnimation, ellipse);
storyboard.Children.Add(opacityAnimation); // ( ToWidth(15) - CurrentWidth(10) ) / 2 = 2.5
var translateTransformX = translateTransform.X - (toSize - currentSize) / 2;
var xAnimation = new DoubleAnimation(toValue: translateTransformX, new Duration(TimeSpan.FromSeconds(1)));
Storyboard.SetTargetProperty(xAnimation,
new PropertyPath("(UIElement.RenderTransform).(TranslateTransform.X)"));
Storyboard.SetTarget(xAnimation, ellipse);
storyboard.Children.Add(xAnimation); var translateTransformY = translateTransform.Y - (toSize - currentSize) / 2;
var yAnimation = new DoubleAnimation(toValue: translateTransformY, new Duration(TimeSpan.FromSeconds(1)));
Storyboard.SetTargetProperty(yAnimation,
new PropertyPath("(UIElement.RenderTransform).(TranslateTransform.Y)"));
Storyboard.SetTarget(yAnimation, ellipse);
storyboard.Children.Add(yAnimation); storyboard.Completed += (o, args) => { Canvas.Children.Remove(ellipse); };
storyboard.Begin();
}

如果有看不懂的,欢迎在下方评论

本文的全部代码放在github欢迎小伙伴访问

将 UWP 的有效像素(Effective Pixels)引入 WPF - 云+社区 - 腾讯云

支持 Windows 10 最新 PerMonitorV2 特性的 WPF 多屏高 DPI 应用开发 - walterlv


本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名林德熙(包含链接:http://blog.csdn.net/lindexi_gd ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系

WPF 动画实战 点击时显示圆圈淡出效果的更多相关文章

  1. JavaScript网站设计实践(五)编写photos.html页面,实现点击缩略图显示大图的效果

    一.photos.html页面,点击每一张缩略图,就在占位符的位置那里,显示对应的大图. 看到的页面效果是这样的: 1.实现思路 这个功能在之前的JavaScript美术馆那里已经实现了. 首先在页面 ...

  2. android 模仿大众点评团购卷列表多余3条时折叠,点击时显示剩余全部的功能

    要实现这样一个效果:加载一组数据,当这组数据的条数超过2条时,则这显示两条,其余的隐藏,当点击“展开全部时”在显示余下的部分.效果如下图所示: 展开前的效果: 展开后的效果 : 实现思路:控制数据而不 ...

  3. html页面多个a标签点击时显示不同的样式

    <!DOCTYPE HTML> <html lang="en-US"> <head> <meta charset="UTF-8& ...

  4. android高仿微信UI点击头像显示大图片效果

    用过微信的朋友朋友都见过微信中点击对方头像显示会加载大图,先贴两张图片说明下: 这种UI效果对用户的体验不错,今天突然有了灵感,试着去实现,结果就出来了.. 下面说说我的思路: 1.点击图片时跳转到另 ...

  5. js点击更多显示更多内容效果

    我写了一个简单的分段显示插件,用法很简单:1,把你要分面显示的内容的容器元素增加一个class=showMoreNChildren,并增加一个自定义属性pagesize="8" 这 ...

  6. 例题.点击按钮显示内容+弹窗效果+ajax

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  7. android高仿微信UI点击头像显示大图片效果, Android 使用ContentProvider扫描手机中的图片,仿微信显示本地图片效果

    http://www.cnblogs.com/Jaylong/archive/2012/09/27/androidUI.html http://blog.csdn.net/xiaanming/arti ...

  8. [转]Android UI:看看Google官方自定义带旋转动画的ImageView-----RotateImageView怎么写(附 图片淡入淡出效果)

    http://blog.csdn.net/yanzi1225627/article/details/22439119 众所周知,想要让ImageView旋转的话,可以用setRotation()让其围 ...

  9. Android Animation动画实战(一): 从布局动画引入ListView滑动时,每一Item项的显示动画

    前言: 之前,我已经写了两篇博文,给大家介绍了Android的基础动画是如何实现的,如果还不清楚的,可以点击查看:Android Animation动画详解(一): 补间动画 及 Android An ...

  10. [WPF]ComboBox.Items为空时,点击不显示下拉列表

    ComboBox.Items为空时,点击后会显示空下拉列表: ComboBox点击显示下拉列表,大概原理为: ComboBox存在ToggleButton控件,默认ToggleButton.IsChe ...

随机推荐

  1. 记录--两行CSS让页面提升了近7倍渲染性能!

    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 前言 对于前端人员来讲,最令人头疼的应该就是页面性能了,当用户在访问一个页面时,总是希望它能够快速呈现在眼前并且是可交互状态.如果页面加载 ...

  2. Advanced .Net Debugging 5:基本调试任务(线程的操作、代码审查、CLR内部的命令、诊断命令和崩溃转储文件)

    一.介绍 这是我的<Advanced .Net Debugging>这个系列的第五篇文章.今天这篇文章的标题虽然叫做"基本调试任务",但是这章的内容还是挺多的.上一篇我 ...

  3. Clang开发注意事项

    Clang tools need their builtin headers and search for them the same way Clang does. Thus, the defaul ...

  4. SpringBoot2使用hikari报 Failed to validate connection com.mysql.cj.jdbc.ConnectionImpl

    SpringBoot2配置文件有变化,需要更改配置 #datasource spring.datasource.driver-class-name=com.mysql.jdbc.Driver spri ...

  5. 2024 OI/VEX/啊啊啊? 赛季游记

    不定期更新,随便写. 中马建交80周年 CreateJR赛项 什么远古比赛,2024/01 的时间用 2023 赛季的规则(挺好). Day -4 1/24 在 破败不堪 的上海市安生学校集训. 点的 ...

  6. UE4中的C++编程简介

    对官方文档的学习链接 利用UE创建一个C++基类 在编辑器中可以选择父类,根据这个父类我们可以创建一个基类用于后续的蓝图类制作. 以Actor父类为例创建基类,其头文件会包含一个构造函数,一个Tick ...

  7. 使用sbt对Scala程序进行打包并运行(Spark单机运行)

    十.使用sbt对Scala程序进行打包并运行(Spark单机运行) 在./sparkapp 中新建文件 simple.sbt(vim ./sparkapp/simple.sbt),添加内容如下,声明该 ...

  8. .net core ECDsa

    ECDsa(Elliptic Curve Digital Signature Algorithm)是一种基于椭圆曲线密码学的数字签名算法.在.NET Core中,System.Security.Cry ...

  9. 03 jQuery属性控制

    03 jQuery属性控制 属性相关的控制主要有以下几个功能 val() => 处理value属性 text() => 处理innerText html() => 处理innerHT ...

  10. #SPFA#洛谷 4042 [AHOI2014/JSOI2014] 骑士游戏

    题目 分析 如果我想普通攻击1,那么必须干掉所有产生的其它怪兽,这不由得可以用一个不等式来表示, \(普攻+\sum need<法攻\) 但是所需要消灭的怪兽同样可以这样进行,所以它可能具有后效 ...