WINUI——实现点在直线上随意移动

开发环境

VS2022

.net core6

需求

需要在一条直线上可随意移动一个点,这个点一定要在这条直线上,不可移出直线。也就是说A点到B点的直线就是这个点的移动范围。

需求深入分析

这个需求看上去,感觉完全可以使用Slider实现,因为Slider上已经将可移动的那个点(Slider的控制柄)完全限制在Slider上了。但深入沟通交流后发现,这条线的两个端点是需要绑定一个平面坐标的,并且两个端点可能会实时变化,那么用slider实现后,要改变它在平面上的位置,就不太方便了,需要通过复杂的计算,才能准确放置它的位置。

转而打算用Line + Ellipse来实现,Line由两个端点A(X1,Y1)B(X2,Y2)就决定了,要改变它的平面位置,只要将两个坐标点绑定给A、B两点即可。那么就只需要实现控制点无论怎么移动就在这条直线上即可,即是将不在直线上的光标位置转化为在直线上的x、y坐标值,然后将求出的坐标值赋值给点(注意是点的圆心在直线上,这个是隐藏需求)即可。相对Slider要简单很多。

注:文中提到的x1、y1均表示Line的A点坐标(x2、y2同理)。

转化为数学问题

因直线的AB两点已经定下来,也就是说(x1,y1)与(x2,y2)为已知,那么根据直线方程:y=kx+b,k为斜率(要注意直线为x轴垂线时需要特殊考虑),b为截距(即x=0时与y轴的交点)。

那么k=(y2-y1)/(x2-x1),b=y1- k x1。

同时直线X的范围限制在[x1,y1]这个闭区间即可,当斜率k存在时,Y就完全不需要限制它的区间(X的区间就限制了Y的区间);当斜率不存在时,即直线为x轴的垂线时,则需要限制y的区间在[y1,y2]的闭区间。开发中可以将X与Y均限制在直线区间内,然后再求对应直线点的值。

开发中b可以不求它,因为在这条直线段上的点的y值可以通过y=y1+k(x-x1), x为通过获取光标位置坐标x值;而斜率不存在时,b也不存在。

UI与代码

通过分析,这个功能仅需要Line与Ellipse即可,而为了实现Ellipse在后续移动,将这两个控件作为canvas的子控件,这样Ellipse后续位置的变动就可以很好的设置,通过Canvas.SetLeft和Canvas.SetTop即可将点位置重新设置。

UI端Xaml代码如下:

        <Canvas><Linex:Name="line"Stroke="Black"StrokeThickness="2"X1="10"X2="400"Y1="{Binding ElementName=line, Path=X1}"Y2="400" /><EllipseName="movingCircle"Canvas.Left="0"Canvas.Top="0"Width="20"Height="20"Fill="Blue"Opacity="0.6" /></Canvas>

注意:

上述Line暂未用绑定,此可按自己需求进行相应的位置数据绑定。

另Ellipse的坐标设置的是(0,0),以便初始时Line的A点与Ellipse的圆心重合。

WINUI后台代码如下:

            movingCircle.PointerPressed += (sender, e) =>{var ellipse = sender as Ellipse;var startPoint = e.GetCurrentPoint(ellipse).Position;ellipse.CapturePointer(e.Pointer);ellipse.PointerMoved += (s, me) =>{if (ellipse.CapturePointer(e.Pointer)){if (line.X1 == line.X2 && line.Y1 == line.Y2)//这就是一个点了,一个点计算个啥?{return;}var currentPosition = me.GetCurrentPoint(canvas).Position;var x = Math.Max(line.X1, Math.Min(line.X2, currentPosition.X));double y = Math.Max(line.Y1, Math.Min(line.Y2, currentPosition.Y));if (line.X1 == line.X2 || line.Y1 == line.Y2){//do nothing}else{var slope = (line.Y2 - line.Y1) / (line.X2 - line.X1);//X2==X1时,需要特殊考虑var slope1 = (line.X2 - line.X1) / (line.Y2 - line.Y1);//y2==y1时,需要特殊处理if (Math.Abs(slope) > Math.Abs(slope1)){x = line.X1 + slope1 * (y - line.Y1);}elsey = line.Y1 + slope * (x - line.X1);}var r = ellipse.Width / 2;Canvas.SetLeft(ellipse, x - r);Canvas.SetTop(ellipse, y - r);}};ellipse.PointerReleased += (s, me) =>{ellipse.ReleasePointerCapture(e.Pointer);ellipse.PointerMoved -= (s, me2) => { };ellipse.PointerReleased -= (s, me3) => { };};};

WPF后台代码如下(UI代码完全一样):

    public  class MainWindow : Window{public MainWindow(){InitializeComponent();movingCircle.MouseLeftButtonDown += (sender, e) =>{var ellipse = sender as Ellipse;var b = ellipse.CaptureMouse();ellipse.MouseMove += (s, me) =>{if (ellipse.IsMouseCaptured){if (line.X1 == line.X2 && line.Y1 == line.Y2)//这就是一个点了,一个点计算个啥?{return;}var currentPosition = me.GetPosition(canvas);var x = Math.Max(line.X1, Math.Min(line.X2, currentPosition.X));double y = Math.Max(line.Y1, Math.Min(line.Y2, currentPosition.Y));if (line.X1 == line.X2 || line.Y1 == line.Y2){//do nothing}else{var slope = (line.Y2 - line.Y1) / (line.X2 - line.X1);//X2==X1时,需要特殊考虑var slope1 = (line.X2 - line.X1) / (line.Y2 - line.Y1);//y2==y1时,需要特殊处理if (Math.Abs(slope) > Math.Abs(slope1)){x = line.X1 + slope1 * (y - line.Y1);}elsey = line.Y1 + slope * (x - line.X1);}var r = ellipse.Width / 2;Canvas.SetLeft(ellipse, x - r);Canvas.SetTop(ellipse, y - r);}};ellipse.MouseLeftButtonUp += (s, me) =>{ellipse.ReleaseMouseCapture();ellipse.MouseMove -= (s, me2) => { };ellipse.MouseLeftButtonUp -= (s, me3) => { };};};}}

注:后台代码中控件获取指针焦点后,即WIN的CapturePointer与WPF的CaptureMouse,一定要Release,WINUI中通过ReleasePointerCapture方法,WPF通过ReleaseMouseCapture。

特别注意

斜率K过大时,X移动过小的距离,Y就可能到达直线的端点。也就是说变化率过大会导致调节变得异常困难。

为了避免这个问题,在处理的过程中分别求了斜率slope和它的倒数slope1,然后比较它们绝对值(若斜率是-50,那么它的倒数是-1/50,-1/50的是大于-50的,但调节变化率则应该是-1/50是小于-50的)的大小,用较小的一个来计算当前位置对应在直线上的点位置。

|slope|>|1/slope|时, x = X1 + 1/slope * (y - Y1)  y为节能到的当前光标位置的y坐标值。

|slope|<=|1/slope|时,y = Y1 + slope * (x - X1)    x为获取到的当前光标位置的x坐标值。

注意事项

  1. 后台代码中控件获取指针焦点后,即WIN的CapturePointer与WPF的CaptureMouse,一定要释放;WINUI中通过ReleasePointerCapture方法,WPF通过ReleaseMouseCapture。
  2. 在后台代码中一定要将在光标移动时绑定的事件移除,同个控件多次订阅相同事件是无益的,性能会有一定下降,还可能导致对象回收不及时或长期持有对象而出现内存相关bug。
  3. 要注间线上移动点Ellipse的圆心位置是否在直线上。
  4. 注意斜率导致的变化过大问题,同时处理斜率过大的边界条件x1=x2要考虑,y1=y2的情况也要考虑。x1=x2 && y1=y2的极端情况也要一并考虑进去。
  5. 注意获取光标位置时通过获取点的容器的方式来获取,直接通过获取点的光标位置,则会因点在快速移动,move事件处理不及时可能导致出现点跳动问题,在测试时斜率过大(绝对值过大)越容易出现点在直线上跳动。
  6. 要注意边界条件的考虑,如直线方程中很可能不会考虑到直线为x轴垂线的情况。
  7. 以上代码若有必要可以封装为一个UserControl,以供后续的复用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/46452.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

RPC与服务的注册发现

文章目录 1. 什么是远程过程调用(RPC)?2. RPC的流程3. RPC实践4. RPC与REST的区别4.1 RPC与REST的相似之处4.2 RPC与REST的架构原则4.3 RPC与REST的主要区别 5. RPC与服务发现5.1 以zookeeper为服务注册中心5.2 以etcd为服务注册中心 6. 小结参考 1. 什么是远程过程调用(RPC)?…

(自用)网络编程

OSI七层协议模型 (open system interconnection) 应用层————为应用数据提供服务 表示层————数据格式转化&#xff0c;数据加密 会话层————建立、维护和管理会话 传输层————建立、维护和管理端到端的链接&#xff0c;控制数据传输的方式 网络层————数据…

昇思25天学习打卡营第19天|基于MobileNetv2的垃圾分类

国内城市生活垃圾一般分类为以下四大类&#xff1a; 可回收垃圾&#xff1a;主要包括废纸、塑料、玻璃、金属和布料五大类。废纸如报纸、期刊、图书、包装纸等&#xff1b;塑料如各种塑料袋、塑料包装物、一次性塑料餐盒等&#xff1b;玻璃如玻璃瓶、碎玻璃片等&#xff1b;金…

Windows图形界面(GUI)-DLG-C/C++ - 图形界面程序结构

公开视频 -> 链接点击跳转公开课程博客首页 -> ​​​​​​链接点击跳转博客主页 目录 Windows图形界面程序结构 Windows 程序结构 窗口类和窗口过程函数 创建和显示窗口 消息循环和消息处理机制 Windows图形界面程序结构 Windows 程序结构 WinMain 函数WinMain …

【2024最新】C++读写优化超详细解析(cin优化+普通快读+fread)_输入输出优化_快读快写_算法竞赛

在算法竞赛中&#xff0c;读入速度和输出速度一直是卡常的重要手段。也有不少人经历过被题目卡 cin 的情况。今天我给大家介绍一下算法竞赛中常用的读写方法及其优化。 声明&#xff1a;大部分情况下&#xff0c;只用读入优化就行。对于输出量大的题目再考虑使用输出优化。 一…

Base64实现传输图片

1. Base64简介 Base64是一种基于64个可打印字符来表示二进制数据的表示方法。通常用于在URL、Cookie和邮件等场景中&#xff0c;将二进制数据转换为字符串形式进行传输。 代码实现 public class ImageUtils {/*** 得到图片的Base64编码* param imagePath 图片路径* return …

手机删除的文件能恢复吗?删除不等于永别,3个技巧助你找回

安卓手机中的文件&#xff0c;就像是数字世界里的繁星&#xff0c;记录着我们的点点滴滴。然而&#xff0c;有时我们可能会不小心删除了某些重要的文件&#xff0c;让我们感到惋惜和困惑。删除的文件能恢复吗&#xff1f;别担心&#xff0c;删除并不等于永别&#xff0c;我们也…

Maven报错 NullPointerException as JavaCompiler.readSourceFile

1. 报错详情 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project xxx-web: Fatal error compiling: CompilerException: NullPointerException -> [Help 1] org.apache.maven.lifecycle.Lifecycl…

CentOS 停服后,服务器 OS 路在何方?

2024 年 6 月 30 日&#xff0c;CentOS Linux 7 终止其生命周期&#xff08;EOL&#xff09;&#xff0c;至此 CentOS 全系列版本也已停止维护&#xff0c;属于 CentOS 的时代彻底终结。CentOS 停止维护后&#xff0c;用户将无法获得包括问题修复和功能更新在内的任何软件维护和…

小程序里面使用vant ui中的vant-field组件,如何使得输入框自动获取焦点

//.wxml <van-fieldmodel:value"{{ userName }}"placeholder"请输入学号"focus"{{focusUserName}}"/>// .js this.setData({focusUserName: true});vant-field

LLMs之RAG:GraphRAG(本质是名词Knowledge Graph/Microsoft微软发布)的简介、安装和使用方法、案例应用之详细攻略

LLMs之RAG&#xff1a;GraphRAG(本质是名词Knowledge Graph/Microsoft微软发布)的简介、安装和使用方法、案例应用之详细攻略 导读&#xff1a;2024年7月3日&#xff0c;微软正式开源发布GraphRAG。GraphRAG可以提高大型语言模型在私有数据集上的推理能力。 背景痛点&#xff1…

MSVC2017+Qt 打包

在环境变量下配置好 QT 和 MSVC 的路径 相关搜索&#xff1a; 找不到msvcp140.dll 1.搜索 Qt 选择在编译器路径下打开 2. Windeployqt 生成打包&#xff0c;正常情况下生成 VC 相关package&#xff0c; 即 msvcp140.dll 等MSVC 相关 但是lz尝试没有生成 解决办法 先将生成…

数据库作业d8

要求&#xff1a; 一备份 1 mysqldump -u root -p booksDB > booksDB_all_tables.sql 2 mysqldump -u root -p booksDB books > booksDB_books_table.sql 3 mysqldump -u root -p --databases booksDB test > booksDB_and_test_databases.sql 4 mysql -u roo…

MySQL 中的几种锁

MySQL 中的锁 #按锁粒度如何划分? 按锁粒度划分的话&#xff0c;MySQL 的锁有&#xff1a; 表锁&#xff1a;开销小&#xff0c;加锁快&#xff1b;锁定力度大&#xff0c;发生锁冲突概率高&#xff0c;并发度最低;不会出现死锁。行锁&#xff1a;开销大&#xff0c;加锁慢…

电脑压缩软件哪个好?WinRAR、7-Zip、Bandizip 还是360压缩

文件压缩软件已成为我们日常工作中不可或缺的一部分&#xff0c;它不仅能够帮助我们节省存储空间&#xff0c;还能提高文件传输效率。本文简鹿办公小编将对四款主流的电脑压缩软件进行对比&#xff0c;它们分别是 WinRAR、7-Zip、Bandizip 和 360 压缩。 一、WinRAR WinRAR 是…

Qt中文个数奇数时出现问号解决

Qt中文个数奇数时出现问号解决 目录 Qt中文个数奇数时出现问号解决问题背景问题场景解决方案 问题背景 最近在开发一个小工具&#xff0c;涉及到一些中文注释自动打印&#xff0c;于是摸索如何把代码里面的中文输出到csv文件中&#xff0c;出现了乱码&#xff0c;按照网上的攻…

vue2-Django3-iframe解决方案,处理安全策略,事件拦截,处理iframe重载等

目录 简介 实现iframe 后端安全策略 通过Ngnix代理实现SAMEORIGIN iframe的事件拦截&#xff0c;自定义处理 iframe的状态保持&#xff08;解决vue中iframe重载&#xff09; 解决方法 简介 Iframe&#xff08;内联框架&#xff09;是一种HTML元素&#xff0c;用于在网页…

Nginx配置缺少导致CSS不起作用

引言 在Web开发中&#xff0c;确保样式表正确加载是前端显示正常工作的关键。然而&#xff0c;有时候即使CSS文件的路径和代码本身没有问题&#xff0c;CSS样式也可能无法正确应用。本文将分享一个常见的问题——Nginx配置缺少导致的CSS不起作用&#xff0c;以及如何解决这个问…

周鸿祎为什么建议Java、前端、大数据、PHP开发都要学一下大模型?_ai大模型全栈工程师跟java有关吗

ChatGPT的出现在全球掀起了AI大模型的浪潮&#xff0c;2023年可以被称为AI元年&#xff0c;AI大模型以一种野蛮的方式&#xff0c;闯入你我的生活之中。 从问答对话到辅助编程&#xff0c;从图画解析到自主创作&#xff0c;AI所展现出来的能力&#xff0c;超出了多数人的预料&…

智慧公厕系统实现人性化与节能化的完美结合

在当今社会&#xff0c;科技的飞速发展正不断改变着我们的生活方式&#xff0c;公厕也不例外。智慧公厕系统的出现&#xff0c;不仅提升了人们的使用体验&#xff0c;更实现了人性化与节能化的完美结合&#xff0c;为城市公共服务带来了全新的变革。 一、人性化&#xff0c;是智…