制作一个用户头像选择器仿 WeGame

 制作一个用户头像选择器仿 WeGame

CropAvatar

作者:WPFDevelopersOrg - 驚鏵

原文链接:https://github.com/WPFDevelopersOrg/WPFDevelopers

  • 框架使用.NET40

  • Visual Studio 2019;

  • 制作一个用户头像选择Canvas为父控件所实现,展示图片使用ImagePath当作上方的蒙版;

  • Canvas:主要用途方便移动Image,设置ClipToBounds="True"裁剪为一个正方形200x200做为主要展示区域;

  • Image:展示需要裁剪的图片;

c9b07db74e5383aeeabc61af6135cfa7.png
  • Path:CombinedGeometry[1]绘制蒙版大小200x200效果如下;

bf0fe795785019aa7512bdf50942cf13.png
  • 当选择一个本地图片的时候判断宽与高谁更大,谁小就将它更改为200 ,另一边做等比缩放后给到DrawingVisual绘制一个新的BitmapFrame[2]Image控件做展示;

  • 当移动图片的时候右侧展示当前区域使用CroppedBitmap[3]进行裁剪并显示;

  • 源码Github[4]Gitee[5]

cd67c09d9712dfb1349e510628b4379c.gif1)CropAvatar.xaml 代码如下;

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:controls="clr-namespace:WPFDevelopers.Controls"><ResourceDictionary.MergedDictionaries><ResourceDictionary Source="Basic/ControlBasic.xaml"/></ResourceDictionary.MergedDictionaries><Style TargetType="controls:CropAvatar" BasedOn="{StaticResource ControlBasicStyle}"><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="{x:Type controls:CropAvatar}"><Canvas x:Name="PART_Canvas" ClipToBounds="True"><Image x:Name="PART_Image" Cursor="SizeAll" ></Image><Path x:Name="PART_Layout" Fill="{DynamicResource BlackSolidColorBrush}" Width="200" Height="200" Opacity=".5"><Path.Data><CombinedGeometry GeometryCombineMode="Xor"><CombinedGeometry.Geometry1><RectangleGeometry Rect="0,0,200,200"/></CombinedGeometry.Geometry1><CombinedGeometry.Geometry2><EllipseGeometry Center="100,100" RadiusX="100" RadiusY="100"/></CombinedGeometry.Geometry2></CombinedGeometry></Path.Data></Path><Grid x:Name="PART_Grid" Width="200" Height="200"><Button x:Name="PART_ReplaceButton" Style="{StaticResource PathButton}"HorizontalAlignment="Right"VerticalAlignment="Top"Width="40" Height="40" ToolTip="更换图片"Visibility="Collapsed"><Button.Content><Path Data="{StaticResource PathReplace}"Fill="{StaticResource PrimaryNormalSolidColorBrush}"Height="15"Width="15"Stretch="Fill" /></Button.Content></Button><Button x:Name="PART_AddButton" Style="{StaticResource PathButton}"Width="40" Height="40" ToolTip="选择图片"><Button.Content><Path Data="{StaticResource PathAdd}"Fill="{StaticResource PrimaryNormalSolidColorBrush}"Height="20"Width="20"Stretch="Fill" RenderTransformOrigin="0.5,0.5" IsHitTestVisible="False"><Path.RenderTransform><RotateTransform Angle="45"/></Path.RenderTransform></Path></Button.Content></Button></Grid></Canvas></ControlTemplate></Setter.Value></Setter></Style></ResourceDictionary>

2)CropAvatar.cs 代码如下;

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using WPFDevelopers.Helpers;namespace WPFDevelopers.Controls
{[TemplatePart(Name = CanvasTemplateName, Type = typeof(Canvas))][TemplatePart(Name = ImageTemplateName, Type = typeof(Image))][TemplatePart(Name = PathTemplateName, Type = typeof(Path))][TemplatePart(Name = GridTemplateName, Type = typeof(Grid))][TemplatePart(Name = ReplaceButtonTemplateName, Type = typeof(Button))][TemplatePart(Name = AddButtonTemplateName, Type = typeof(Button))]public partial class CropAvatar : Control{private const string CanvasTemplateName = "PART_Canvas";private const string ImageTemplateName = "PART_Image";private const string PathTemplateName = "PART_Layout";private const string GridTemplateName = "PART_Grid";private const string ReplaceButtonTemplateName = "PART_ReplaceButton";private const string AddButtonTemplateName = "PART_AddButton";private Point point;private const int _size = 200;private bool isDown;private bool isLeft;private CroppedBitmap crop;private Canvas canvas;private Image image;private Path path;private Grid grid;private Button replaceButton, addButton;private int initialX, initialY, voffsetX, voffsetY;private double vNewStartX, vNewStartY, _StartX, _StartY, centerX, centerY;private BitmapFrame bitmapFrame;public ImageSource OutImageSource{get { return (ImageSource)GetValue(OutImageSourceProperty); }set { SetValue(OutImageSourceProperty, value); }}public static readonly DependencyProperty OutImageSourceProperty =DependencyProperty.Register("OutImageSource", typeof(ImageSource), typeof(CropAvatar), new PropertyMetadata(null));static CropAvatar(){DefaultStyleKeyProperty.OverrideMetadata(typeof(CropAvatar), new FrameworkPropertyMetadata(typeof(CropAvatar)));}public override void OnApplyTemplate(){base.OnApplyTemplate();canvas = GetTemplateChild(CanvasTemplateName) as Canvas;canvas.Loaded += Canvas_Loaded;grid = GetTemplateChild(GridTemplateName) as Grid;image = GetTemplateChild(ImageTemplateName) as Image;image.MouseDown += Image_MouseDown;image.MouseMove += Image_MouseMove;image.MouseUp += Image_MouseUp;image.MouseLeave += Image_MouseLeave;path = GetTemplateChild(PathTemplateName) as Path;replaceButton = GetTemplateChild(ReplaceButtonTemplateName) as Button;replaceButton.Click += ReplaceButton_Click;addButton = GetTemplateChild(AddButtonTemplateName) as Button;addButton.Click += AddButton_Click;}private void Canvas_Loaded(object sender, RoutedEventArgs e){if (sender is Canvas canvas){var width = canvas.ActualWidth;var height = canvas.ActualHeight;centerX = (width - path.Width) / 2.0d;centerY = (height - path.Height) / 2.0d;canvas.Clip = new RectangleGeometry(new Rect(centerX, centerY, 200, 200)); Canvas.SetLeft(path, centerX);Canvas.SetTop(path, centerY);Canvas.SetLeft(grid, centerX);Canvas.SetTop(grid, centerY);}}private void Image_MouseLeave(object sender, MouseEventArgs e){isDown = false;if (isLeft)_StartX = Canvas.GetLeft(image);else_StartY = Canvas.GetTop(image);}private void Image_MouseUp(object sender, MouseButtonEventArgs e){if (isDown){var vPoint = e.GetPosition(this);if (isLeft){_StartX = Canvas.GetLeft(image);initialX = voffsetX;}else{_StartY = Canvas.GetTop(image);initialY = voffsetY;}}}private void Image_MouseMove(object sender, MouseEventArgs e){if (e.LeftButton == MouseButtonState.Pressed && isDown){var vPoint = e.GetPosition(this);if (isLeft){var voffset = vPoint.X - point.X;vNewStartX = _StartX + voffset;var xPath = Canvas.GetLeft(path);if (vNewStartX <= xPath && vNewStartX >= -(bitmapFrame.Width - 200 - xPath)){Canvas.SetLeft(image, vNewStartX);voffsetX = initialX - (int)voffset;voffsetX = voffsetX < 0 ? 0 : voffsetX;crop = new CroppedBitmap(bitmapFrame, new Int32Rect(voffsetX, 0, _size, _size));}}else{var voffset = vPoint.Y - point.Y;vNewStartY = _StartY + voffset;var yPath = Canvas.GetTop(path);if (vNewStartY <= yPath && vNewStartY >= -(bitmapFrame.Height - 200 - yPath)){Canvas.SetTop(image, vNewStartY);voffsetY = initialY - (int)voffset;voffsetY = voffsetY < 0 ? 0 : voffsetY;crop = new CroppedBitmap(bitmapFrame, new Int32Rect(0, voffsetY, _size, _size));}}OutImageSource = crop;}}private void Image_MouseDown(object sender, MouseButtonEventArgs e){isDown = true;point = e.GetPosition(this);}private void ReplaceButton_Click(object sender, RoutedEventArgs e){InitialImage();}private void AddButton_Click(object sender, RoutedEventArgs e){InitialImage();}void InitialImage(){vNewStartX = 0;vNewStartY = 0;var uri = ControlsHelper.ImageUri();if (uri == null) return;var bitmap = new BitmapImage(uri);if (bitmap.Height > bitmap.Width){double scale = (double)bitmap.Width / (double)path.Width;image.Width = _size;image.Height = (double)bitmap.Height / scale;isLeft = false;}else if (bitmap.Width > bitmap.Height){double scale = (double)bitmap.Height / (double)path.Height;image.Width = (double)bitmap.Width / scale;image.Height = _size;isLeft = true;}bitmapFrame = ControlsHelper.CreateResizedImage(bitmap, (int)image.Width, (int)image.Height, 0);image.Source = bitmapFrame;if (image.Source != null){replaceButton.Visibility = Visibility.Visible;addButton.Visibility = Visibility.Collapsed;}Canvas.SetLeft(grid, centerX);Canvas.SetTop(grid, centerY);_StartX = (canvas.ActualWidth - image.Width) / 2.0d;_StartY = (canvas.ActualHeight - image.Height) / 2.0d;Canvas.SetLeft(image, _StartX);Canvas.SetTop(image, _StartY);        if (isLeft){initialX = (int)(image.Width - 200) / 2;initialY = 0;crop = new CroppedBitmap(bitmapFrame, new Int32Rect(initialX, 0, _size, _size));}else{initialY = (int)(image.Height - 200) / 2;initialX = 0;crop = new CroppedBitmap(bitmapFrame, new Int32Rect(0, initialY, _size, _size));}OutImageSource = crop;}}
}

3)CropAvatarWindow.xaml使用如下;

<ws:Window x:Class="WPFDevelopers.Samples.ExampleViews.CropAvatarWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:wpfdev="https://github.com/WPFDevelopersOrg/WPFDevelopers"xmlns:ws="https://github.com/WPFDevelopersOrg.WPFDevelopers.Minimal"mc:Ignorable="d"  WindowStyle="ToolWindow" ResizeMode="NoResize"WindowStartupLocation="CenterScreen"Title="WPF 开发者-头像选择器" Height="450" Width="800"><Grid><Grid.ColumnDefinitions><ColumnDefinition/><ColumnDefinition/></Grid.ColumnDefinitions><Grid.RowDefinitions><RowDefinition/><RowDefinition Height="Auto"/></Grid.RowDefinitions><wpfdev:CropAvatar x:Name="MyCropAvatar"/><Image Grid.Column="1" Name="CropAvatarImage" Source="{Binding ElementName=MyCropAvatar,Path=OutImageSource}" Stretch="Fill" Width="200" Height="200"><Image.Clip><EllipseGeometry Center="100,100" RadiusX="100" RadiusY="100"/></Image.Clip></Image><UniformGrid Grid.Row="1" Grid.ColumnSpan="2" HorizontalAlignment="Center" VerticalAlignment="Center"><Button  Content="保存" Click="btnSave_Click" Style="{StaticResource PrimaryButton}" Margin="4,0"/><Button  Content="关闭" Click="btnClose_Click" Margin="4,0"/></UniformGrid></Grid>
</ws:Window>

4) CropAvatarWindow.xaml.cs 代码如下;

using System.Windows;namespace WPFDevelopers.Samples.ExampleViews
{/// <summary>/// CropAvatarWindow.xaml 的交互逻辑/// </summary>public partial class CropAvatarWindow {public CropAvatarWindow(){InitializeComponent();}private void btnSave_Click(object sender, RoutedEventArgs e){DialogResult = true;}private void btnClose_Click(object sender, RoutedEventArgs e){DialogResult = false;}}
}

5) CropAvatarExample.xaml 使用如下;

<UserControl x:Class="WPFDevelopers.Samples.ExampleViews.CropAvatarExample"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:wpfdev="https://github.com/WPFDevelopersOrg/WPFDevelopers"xmlns:local="clr-namespace:WPFDevelopers.Samples.ExampleViews"mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"><Grid><Grid.ColumnDefinitions><ColumnDefinition/><ColumnDefinition/></Grid.ColumnDefinitions><Button Content="图像选择器" VerticalAlignment="Center" HorizontalAlignment="Center" Click="Button_Click"/><Image Grid.Column="1" Name="MyImage"Stretch="Fill" Width="200" Height="200"><Image.Clip><EllipseGeometry Center="100,100" RadiusX="100" RadiusY="100"/></Image.Clip></Image></Grid>
</UserControl>

6) CropAvatarExample.xaml.cs 代码如下;

using System.Windows.Controls;namespace WPFDevelopers.Samples.ExampleViews
{/// <summary>/// CropAvatarExample.xaml 的交互逻辑/// </summary>public partial class CropAvatarExample : UserControl{public CropAvatarExample(){InitializeComponent();}private void Button_Click(object sender, System.Windows.RoutedEventArgs e){var cropAvatarWindow = new CropAvatarWindow();if (cropAvatarWindow.ShowDialog() == true){MyImage.Source = cropAvatarWindow.CropAvatarImage.Source;}}}
}

参考资料

[1]

CombinedGeometry: https://docs.microsoft.com/zh-cn/dotnet/api/system.windows.media.combinedgeometry?view=netframework-4.0

[2]

BitmapFrame: https://docs.microsoft.com/zh-cn/dotnet/api/system.windows.media.imaging.bitmapframe?view=windowsdesktop-6.0

[3]

CroppedBitmap: https://docs.microsoft.com/zh-cn/dotnet/api/system.windows.media.imaging.croppedbitmap?view=windowsdesktop-6.0

[4]

Github: https://github.com/WPFDevelopersOrg/WPFDevelopers

[5]

Gitee: https://gitee.com/WPFDevelopersOrg/WPFDevelopers

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

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

相关文章

网曝南方电网搞末位淘汰和裁员,给各下属单位强制规定辞退率和降岗降级率!...

电网作为垄断性国企&#xff0c;在人们心中一向是好单位的代名词&#xff0c;但最近却有网友曝光南方电网搞末位淘汰和裁员&#xff0c;给各单位下了辞退率和降岗降级率&#xff0c;每个单位都要开除一部分人&#xff0c;或者把一部分人岗级降下来。有南方电网员工马上跑出来辟…

二维数组中的查找

2019独角兽企业重金招聘Python工程师标准>>> 题目 在一个二维数组中&#xff0c;每一行中的数都按照从左到右、从上到下的递增顺序排列。要求输入一个整数&#xff0c;判断数组中是否存在该整数 实现代码 function find($matrix, $rows, $columns, $key) {//TODO 参…

蓝牙追踪_如何使用蓝牙追踪器跟踪您的东西

蓝牙追踪We’ve all done it: you misplace something important and you spend a lot of time (with a lot of stress) backtracking to locate it again. With Bluetooth tracking devices you can make the hunt a lot easier, less stressful, and even avoid losing the t…

vba发送邮件 签名_如何更改“从Windows 10的邮件发送”签名

vba发送邮件 签名The Windows 10 Mail app is a decent email client that allows you to add other email accounts in addition to your Microsoft accounts. You’ll notice, though, that any emails you write in the Mail app have a default signature. Windows 10 Mail…

JAVA_SE基础——24.面向对象的内存分析

黑马程序猿入学blog ... 接着上一章的代码&#xff1a; //车类 class Car{//事物的公共属性使用成员变量描写叙述。String name; //名字的属性 String color; //颜色属性 int wheel; //轮子数 //事物的公共行为使用函数描写叙述。 public void run(){ System.out.println(name&…

煮茶社区AVR开发板第二版[转]

原图:http://blossom.cnblogs.com/gallery/image/21891.html

[Kogel.Subscribe.Mssql]SQL Server增量订阅,数据库变更监听

此框架是SQL Server增量订阅&#xff0c;用来监听增删改数据库数据变更目前仅支持SQL Server&#xff0c;后续会支持MySQL和Oracle&#xff0c;Nuget上可以下载安装或者使用Nuget命令添加包dotnet add package Kogel.Subscribe.Mssql --version 0.0.0.1可以用来处理DB主从同步&…

MFC 单选按钮Radio使用注意

使用MFC Radio时遇到问题&#xff1a;数据交换时出现断言崩溃框 定位于&#xff1a; 解决方法&#xff1a; 1、按CTRLD&#xff0c;保证同一组内的radio的tab序号是连续的&#xff1b; 2、同一组内&#xff0c;设置 radio1的属性&#xff1a; group、tabstop、auto均为true&am…

C# 字符串操作:split、substring、Format

1.split split可以使用多个字符切割。 "test[12]"切割后是3个字符串&#xff0c;最后一个为空。 string testString "test[12]";string[] testData testString.Split([,]);int testLength testData.Length;Console.WriteLine(testLength.ToString());…

flac格式转换mp3格式_MP3,FLAC和其他音频格式之间有什么区别?

flac格式转换mp3格式Digital audio has been around a very long time so there’s bound to be a plethora of audio formats out there. Here are some of the more common ones, what differentiates them, and what to use them for. 数字音频已经存在了很长时间&#xff…

Kubernetes:Flomesh 服务网格与多集群通信

Kubernetes 成功普及了容器集群的概念。许多用户已经在多个集群中部署应用&#xff0c;组织需要运行多个 Kubernetes 集群可能来自以下原因&#xff08;并非详尽列表&#xff09;&#xff1a;• 位置• 延迟&#xff08;尽可能在靠近客户的地方运行应用程序&#xff09;• 管辖…

袁永福软件行业从业经历

简化版》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》 袁永福简历 袁永福&#xff0c;男&#xff0c;1980年生于江西省九江市都昌县&#xff0c;2001年南京东南大学动力工程系本科毕业。毕业后一直从事计算机软件开发工作&…

小程序之地图导航

同学们平常使用地图的时候应该都有注意到&#xff0c;当我们在一个应用中选择一个地址&#xff0c;打开一个地图&#xff0c;往往会有两种显示方式&#xff0c;一个是显示当前自己的位置&#xff1b;一个是显示对方&#xff0c;也就是目的地的位置&#xff1b;如下图&#xff1…

PS2019摄影后期处理(一)

高高手之路笔记 一、学习方法 内外兼修&#xff1a;技术会淘汰、更新&#xff0c;自己内在也要提高。 二、照片格式、色彩空间 JPEG TIFF RAW sRGB&#xff1a;互联网相关图片&#xff0c;电子设备 三、照片风格 自己定义照片格式&#xff0c;直接后期效果 相机自带工具&a…

mdnsresponder_什么是mDNSResponder.exe / Bonjour,如何卸载或删除它?

mdnsresponderYou are no doubt reading this article because you’ve noticed the mDNSResponder.exe process running in Task Manager, you don’t remember installing it, and it doesn’t show up in the Add/Remove programs in Control Panel. So what is it, and how…

Windows 10下,如何使用PowerShell批量重启局域网电脑

PowerShell 在Windows 10中越来越受到微软重视&#xff0c;甚至被微软安排在开始按钮超级菜单中替换了一直以来默认的命令提示符(当然还可以换回去)&#xff0c;这和该工具越来越强大密不可分。这次就介绍一个“群重启”命令&#xff0c;可让局域网内的电脑集体重启。1、单机重…

.NET MAUI学习指南

由于.NET MAUI这项技术出来不久相关的学习资源暂时除了官网以外没有太好的学习资源&#xff0c;这篇文章主要向大家分享几种学习.NET MAUI的学习途径&#xff0c;如果有好的学习资源欢迎大家私信给我然后更新到后续的文章里。&#xff08;以下推荐学习途径均为免费且无广告的资…

循序渐进DB2(第2版)——DBA系统管理、运维与应用案例

《循序渐进DB2(第2版)——DBA系统管理、运维与应用案例》基本信息作者&#xff1a; 牛新庄出版社&#xff1a;清华大学出版社ISBN&#xff1a;9787302323013上架时间&#xff1a;2013-7-3出版日期&#xff1a;2013 年7月开本&#xff1a;16开页码&#xff1a;612版次&#xff1…

PS2019摄影后期处理(二)

一、曲线 二、曲线与通道 三、HSL局部调整 色相、饱和度、亮度 a.色相&#xff1a;一个颜色&#xff0c;帽子是红色 b.饱和度&#xff1a;树木葱郁一点 c.饱和度&#xff1a;衣服连杆 便黑白&#xff1a; 1.调低所有饱和度 2.将某个颜色饱和度提高&#xff0c;再转灰度。…

管理员获得所有权_在Windows 7中获得注册表项的所有权

管理员获得所有权We have previously written about how to take ownership of files and folders in Windows 7, but there may be times when you need to take ownership of or assign full permission for certain registry keys. This article shows you how to do this. …