WPF+Canvas实现平滑笔迹的示例代码
目录
- 实现思路
- 实现效果
- 实现代码
实现思路
收集路径点集。
平均采样路径点集。
将路径点集转为 LineB。
把 LineB 数据传给 Path。
实现效果
实现代码
1)Vector2D.cs 代码如下
usingSystem; usingSystem.Collections.Generic; usingSystem.Linq; usingSystem.Text; namespacewpFDevelopers.Samples.ExampleViews.CanvasHandwriting { publicclassVector2D { publicdoubleX{get;set;}=0; publicdoubleY{get;set;}=0; ///<summary> ///向量的模 ///</summary> publicdoubleMold { get { //自身各分量平方运算. doubleX=this.X*this.X; doubleY=this.Y*this.Y; returnMath.Sqrt(X+Y);//开根号,最终返回向量的长度/模/大小. } } ///<summary> ///单位向量 ///</summary> publicVector2DUnitVector { get { doublesumSquares=(X*X)+(Y*Y); returnnewVector2D(X/Math.Sqrt(sumSquares),Y/Math.Sqrt(sumSquares)); } } publicVector2D() { } publicVector2D(doublex,doubley) { X=x; Y=y; } publicVector2D(System.Windows.Pointpoint) { X=point.X; Y=point.Y; } publicvoidOffset(doubleangle,doubledistance,AngleTypeangleType=AngleType.Radian) { varvector2D=Vector2D.CalculateVectorOffset(this,angle,distance,angleType); X=vector2D.X; Y=vector2D.Y; } publicvoidRotate(doubleangle,Vector2DvectorCenter=null,AngleTypeangleType=AngleType.Radian) { vectorCenter=vectorCenter==null?this:vectorCenter; varvector2D=Vector2D.CalculateVectorRotation(this,vectorCenter,angle,angleType); X=vector2D.X; Y=vector2D.Y; } #region静态方法 ///<summary> ///计算两个向量之间的距离 ///</summary> publicstaticdoubleCalculateVectorDistance(Vector2Dvector2DA,Vector2Dvector2DB) { Vector2Dvector2D=vector2DA-vector2DB; returnvector2D.Mold; } ///<summary> ///计算两点夹角,右侧X轴线为0度,向下为正,向上为负 ///</summary> publicstaticdoubleIncludedAngleXAxis(Vector2Dvector2DA,Vector2Dvector2DB,AngleTypeangleType=AngleType.Radian) { doubleradian=Math.Atan2(vector2DB.Y-vector2DA.Y,vector2DB.X-vector2DA.X);//弧度:1.1071487177940904 returnangleType==AngleType.Radian?radian:ComputingHelper.RadianToAngle(radian); } ///<summary> ///计算两点夹角,下侧Y轴线为0度,向右为正,向左为负 ///</summary> publicstaticdoubleIncludedAngleYAxis(Vector2Dvector2DA,Vector2Dvector2DB,AngleTypeangleType=AngleType.Radian) { doubleradian=Math.Atan2(vector2DB.X-vector2DA.X,vector2DB.Y-vector2DA.Y);//弧度:0.46364760900080609 returnangleType==AngleType.Radian?radian:ComputingHelper.RadianToAngle(radian); } ///<summary> ///偏移向量到指定角度,指定距离 ///</summary> publicstaticVector2DCalculateVectorOffset(Vector2Dvector2D,doubleangle,doubledistance,AngleTypeangleType=AngleType.Radian) { Vector2DpointVector2D=newVector2D(); if(angleType==AngleType.Angle) { angle=angle/(180/Math.PI);//角度转弧度 } doublewidth=Math.Cos(Math.Abs(angle))*distance; doubleheight=Math.Sin(Math.Abs(angle))*distance; if(angle<=Math.PI&&angle>=0) //if(angleis<=Math.PIand>=0) { pointVector2D.X=vector2D.X-width; pointVector2D.Y=vector2D.Y-height; } if(angle>=(-Math.PI)&&angle<=0) //if(angleis>=(-Math.PI)and<=0) { pointVector2D.X=vector2D.X-width; pointVector2D.Y=vector2D.Y+height; } returnpointVector2D; } ///<summary> ///围绕一个中心点,旋转一个向量,相对旋转 ///</summary> publicstaticVector2DCalculateVectorRotation(Vector2Dvector2D,Vector2DvectorCenter,doubleradian,AngleTypeangleType=AngleType.Radian) { radian=angleType==AngleType.Radian?radian:ComputingHelper.RadianToAngle(radian); doublex1=(vector2D.X-vectorCenter.X)*Math.Sin(radian)+(vector2D.Y-vectorCenter.Y)*Math.Cos(radian)+vectorCenter.X; doubley1=-(vector2D.X-vectorCenter.X)*Math.Cos(radian)+(vector2D.Y-vectorCenter.Y)*Math.Sin(radian)+vectorCenter.Y; returnnewVector2D(x1,y1); } publicstaticVector2DCalculateVectorCenter(Vector2Dvector2DA,Vector2Dvector2DB) { returnnewVector2D((vector2DA.X+vector2DB.X)/2,(vector2DA.Y+vector2DB.Y)/2); } ///<summary> ///判断坐标点是否在多边形区域内,射线法 ///</summary> publicstaticboolIsPointPolygonalArea(Vector2Dvector2D,List<Vector2D>aolygonaArrayList) { varN=aolygonaArrayList.Count; varboundOrVertex=true;//如果点位于多边形的顶点或边上,也算做点在多边形内,直接返回true varcrossNumber=0;//x的交叉点计数 varprecision=2e-10;//浮点类型计算时候与0比较时候的容差 Vector2Dp1,p2;//neighbourboundvertices varp=vector2D;//测试点 p1=aolygonaArrayList[0];//leftvertex for(vari=1;i<=N;++i) { //checkallrays if(p.X.Equals(p1.X)&&p.Y.Equals(p1.Y)) { returnboundOrVertex;//pisanvertex } p2=aolygonaArrayList[i%N];//rightvertex if(p.X<Math.Min(p1.X,p2.X)||p.X>Math.Max(p1.X,p2.X)) { //rayisoutsideofourinterests p1=p2; continue;//nextrayleftpoint } if(p.X>Math.Min(p1.X,p2.X)&&p.X<Math.Max(p1.X,p2.X)) { //rayiscrossingoverbythealgorithm(commonpartof) if(p.Y<=Math.Max(p1.Y,p2.Y)) { //xisbeforeofray if(p1.X==p2.X&&p.Y>=Math.Min(p1.Y,p2.Y)) { //overliesonahorizontalray returnboundOrVertex; } if(p1.Y==p2.Y) { //rayjsisvertical if(p1.Y==p.Y) { //overliesonaverticalray returnboundOrVertex; } else { //beforeray ++crossNumber; } } else { //crosspointontheleftside varxinters= (p.X-p1.X)*(p2.Y-p1.Y)/(p2.X-p1.X)+ p1.Y;//crosspointofY if(Math.Abs(p.Y-xinters)<precision) { //overliesonaray returnboundOrVertex; } if(p.Y<xinters) { //beforeray ++crossNumber; } } } } else { //specialcasewhenrayiscrossingthroughthevertex if(p.X==p2.X&&p.Y<=p2.Y) { //pcrossingoverp2 varp3=aolygonaArrayList[(i+1)%N];//nextvertex if(p.X>=Math.Min(p1.X,p3.X)&&p.X<=Math.Max(p1.X,p3.X)) { //p.Xliesbetweenp1.X&p3.X ++crossNumber; } else { crossNumber+=2; } } } p1=p2;//nextrayleftpoint } if(crossNumber%2==0) { //偶数在多边形外 returnfalse; } else { //奇数在多边形内 returntrue; } } ///<summary> ///判断一个点是否在一条边内 ///</summary> publicstaticboolIsPointEdge(Vector2Dpoint,Vector2DstartPoint,Vector2DendPoint) { return(point.X-startPoint.X)*(endPoint.Y-startPoint.Y)==(endPoint.X-startPoint.X)*(point.Y-startPoint.Y) &&Math.Min(startPoint.X,endPoint.X)<=point.X&&point.X<=Math.Max(startPoint.X,endPoint.X) &&Math.Min(startPoint.Y,endPoint.Y)<=point.Y&&point.Y<=Math.Max(startPoint.Y,endPoint.Y); } #endregion静态方法 #region运算符重载 ///<summary> ///重载运算符,和运算,可以用来计算两向量距离 ///</summary> publicstaticVector2Doperator+(Vector2Dvector2DA,Vector2Dvector2DB) { Vector2Dvector2D=newVector2D(); vector2D.X=vector2DA.X+vector2DB.X; vector2D.Y=vector2DA.Y+vector2DB.Y; returnvector2D; } ///<summary> ///重载运算符,差运算,可以用来计算两向量距离 ///</summary> publicstaticVector2Doperator-(Vector2Dvector2DA,Vector2Dvector2DB) { Vector2Dvector2D=newVector2D(); vector2D.X=vector2DA.X-vector2DB.X; vector2D.Y=vector2DA.Y-vector2DB.Y; returnvector2D; } ///<summary> ///重载运算符,差运算,可以用来计算两向量距离 ///</summary> publicstaticVector2Doperator-(Vector2Dvector2D,double_float) { returnnewVector2D(vector2D.X-_float,vector2D.Y-_float); } ///<summary> ///重载运算符,点积运算,可以用来计算两向量夹角 ///</summary> publicstaticdoubleoperator*(Vector2Dvector2DA,Vector2Dvector2DB) { return(vector2DA.X*vector2DB.X)+(vector2DA.Y*vector2DB.Y); } publicstaticdoubleoperator*(Vector2Dvector2D,double_float) { return(vector2D.X*_float)+(vector2D.Y*_float); } ///<summary> ///重载运算符,点积运算,可以用来计算两向量夹角 ///</summary> publicstaticdoubleoperator/(Vector2Dvector2D,doublepara) { return(vector2D.X/para)+(vector2D.Y/para); } ///<summary> ///重载运算符 ///</summary> publicstaticbooloperator>=(Vector2Dvector2D,doublepara) { if(vector2D.Mold>=para) { returntrue; } else { returnfalse; } } publicstaticbooloperator<=(Vector2Dvector2D,doublepara) { if(vector2D.Mold<=para) { returntrue; } else { returnfalse; } } publicstaticbooloperator>(Vector2Dvector2D,doublepara) { if(vector2D.Mold>para) { returntrue; } else { returnfalse; } } publicstaticbooloperator<(Vector2Dvector2D,doublepara) { if(vector2D.Mold<para) { returntrue; } else { returnfalse; } } #endregion运算符重载 #region隐式转换 ///<summary> ///重载隐式转换,可以直接使用Point ///</summary> ///<paramname="v"></param> publicstaticimplicitoperatorVector2D(System.Windows.Pointv)//隐式转换 { returnnewVector2D(v.X,v.Y); } ///<summary> ///重载隐式转换,可以直接使用Point ///</summary> ///<paramname="v"></param> publicstaticimplicitoperatorSystem.Windows.Point(Vector2Dv)//隐式转换 { returnnewSystem.Windows.Point(v.X,v.Y); } ///<summary> ///重载隐式转换,可以直接使用double ///</summary> ///<paramname="v"></param> publicstaticimplicitoperatorVector2D(doublev)//隐式转换 { returnnewVector2D(v,v); } #endregion隐式转换 #regionToString publicoverridestringToString() { returnX.ToString()+","+Y.ToString(); } publicstringToString(stringsymbol) { returnX.ToString()+symbol+Y.ToString(); } publicstringToString(stringsender,stringsymbol) { returnX.ToString(sender)+symbol+Y.ToString(sender); } #endregion } publicenumAngleType { Angle, Radian } }
2)ComputingHelper.cs 代码如下
usingSystem; usingSystem.Collections.Generic; usingSystem.Linq; usingSystem.Text; namespaceWPFDevelopers.Samples.ExampleViews.CanvasHandWriting { publicstaticclassComputingHelper { publicstaticdoubleAngleToRadian(doubleangle) { returnangle*(Math.PI/180); } publicstaticdoubleRadianToAngle(doubleradian) { returnradian*(180/Math.PI); } ///<summary> ///将一个值从一个范围映射到另一个范围 ///</summary> publicstaticdoubleRangeMapping(doubleinputValue,doubleenterLowerLimit,doubleenterUpperlimit,doubleoutputLowerLimit,doubleOutputUpperLimit,CurveTypecurveType=CurveType.None) { varpercentage=(enterUpperLimit-inputValue)/(enterUpperLimit-enterLowerLimit); switch(curveType) { caseCurveType.Sine: percentage=Math.Sin(percentage); break; caseCurveType.CoSine: percentage=Math.Cos(percentage); break; caseCurveType.Tangent: percentage=Math.Tan(percentage); break; caseCurveType.Cotangent: percentage=Math.Atan(percentage); break; default: break; } doubleoutputValue=OutputUpperLimit-((OutputUpperLimit-outputLowerLimit)*percentage); returnoutputValue; } publicstaticstringByteToKB(double_byte) { List<string>unit=newList<string>(){"B","KB","MB","GB","TB","P","PB"}; inti=0; while(_b编程客栈yte>1024) { _byte/=1024; i++; } _byte=Math.Round(_byte,3);//保留三位小数 return_byte+unit[i]; } ///<summary> ///缩短一个数组,对其进行平均采样 ///</summary> publicstaticdouble[]AverageSampling(double[]sourceArray,intnumber) { if(sourceArray.Length<=number) { returnsourceArray; //thrownewException("新的数组必须比原有的要小!"); } double[]arrayList=newdouble[number]; doublestride=(double)sourceArray.Length/number; for(inti=0,jIndex=0;i<number;i++,jIndex++) { doublestrideIncrement=i*stride; strideIncrement=Math.Round(strideIncrement,6); doublesum=0; intfirstIndex=(int)(strideIncrement); doublefirstDecimal=strideIncrement-firstIndex; inttailIndex=(int)(strideIncrement+stride); doubletailDecimal=(strideIncrement+stride)-tailIndex; if(firstDecimal!=0) sum+=sourceArray[firstIndex]*(1-firstDecimal); if(tailDecimal!=0&&tailIndex!=sourceArray.Length) sum+=sourceArray[tailIndex]*(tailDecimal); intstartIndex=firstDecimal==0?firstIndex:firstIndex+1; intendIndex=tailIndex; for(intj=startIndex;j<endIndex;j++) sum+=sourceArray[j]; arrayList[jIndex]=sum/stride; } returnarrayList; } publicstaticList<Vector2D>AverageSampling(List<Vector2D>sourceArray,intnumber) { if(sourceArray.Count<=number-2) { returnsourceArray; } double[]x=newdouble[sourceArray.Count]; double[]y=newdouble[sourceArray.Count]; for(inti=0;i<sourceArray.Count;i++) { x[i]=sourceArray[i].X; y[i]=sourceArray[i].Y; } double[]X=AverageSampling(x,number-2); double[]Y=AverageSampling(y,number-2); List<Vector2D>arrayList=newList<Vector2D>(); for(inti=0;i<number-2;i++) { arrayList.Add(newVector2D(X[i],Y[i])); } arrayList.Insert(0,sourceArray[0]);//添加首 arrayList.Add(sourceArray[sourceArray.Count-1]);//添加尾 returnarrayList; } } publicenumCurveType { Sine, CoSine, Tangent, Cotangent, None } }
3)LineB.cs 代码如下
usingSystem; usingSystem.Collections.Generic; usingSystem.Linq; usingSystem.Text; usingSystem.Windows.Media; namespaceWPFDevelopers.Samples.ExampleViews.CanvasHandWriting { publicclassLineB { privateList<Vector2D>_vector2DList=newList<Vector2D>(); publicList<Vector2D>Vector2DList { get{return_vector2DList;} set { _vector2DList=value; } } privateList<BezierCurve>_bezierCurveList=newList<BezierCurve>(); publicList<BezierCurve>BezierCurveList { get{return_bezierCurveList;} privateset{_bezierCurveList=value;} } privatedouble_tension=0.618; publicdoubleTension { get{return_tension;} set { _tension=value; if(_tension>10) _tension=10; if(_tension<0) _tension=0; } } privatebool_isClosedCurve=true; publicboolIsClosedCurve { get{return_isClosedCurve;} set{_isClosedCurve=value;} } privatestring_pathData=string.Empty; publicstringPathData { get { if(_pathData==string.Empty) { _pathData=Vector2DToBezierCurve(); } return_pathData; } } privatestringVector2DToBezierCurve() { if(Vector2DList.Count<3) returnstring.Empty; BezierCurveList.Clear(); for(inti=0;i<Vector2DList.Count;i++) { intpointTwoIndex=i+1<Vector2DList.Count?i+1:0; intpointThreeIndex=i+2<Vector2DList.Count?i+2:i+2-Vector2DList.Count; Vector2Dvector2D1=Vector2DList[i]; Vector2Dvector2D2=Vector2DList[pointTwoIndex]; Vector2Dvector2D3=Vector2DList[pointThreeIndex]; Vector2DstartVector2D=Vector2D.CalculateVectorCenter(vector2D1,vector2D2); doublestartAngle=Vector2D.IncludedAngleXAxis(vector2D1,vector2D2); doublestartDistance=Vector2D.CalculateVectorDistance(startVector2D,vector2D2)*(1-Tension); Vector2DstartControlPoint=Vector2D.CalculateVectorOffset(vector2D2,startAngle,startDistance); Vector2DendVector2D=Vector2D.CalculateVectorCenter(vector2D2,vector2D3); doubleendAngle=Vector2D.IncludedAngleXAxis(endVector2D,vector2D2); doubleendDistance=Vector2D.CalculateVectorDistance(endVector2D,vector2D2)*(1-Tension); Vector2DendControlPoint=Vector2D.CalculateVectorOffset(endVector2D,endAngle,endDistance); BezierCurvebezierCurve=newBezierCurve(); bezierCurve.StartVector2D=startVector2D; bezierCurve.StartControlPoint=startControlPoint; bezierCurve.EndVector2D=endVector2D; bezierCurve.EndControlPoint=endControlPoint; BezierCurveList.Add(bezierCurve); } if(!IsClosedCurve) { BezierCurveList[0].StartVector2D=Vector2DList[0]; BezierCurveList.RemoveAt(BezierCurveList.Count-1); BezierCurveList[BezierCurveList.Count-1].EndVector2D=Vector2DList[Vector2DList.Count-1]; BezierCurveList[BezierCurveList.Count-1].EndControlPoint=BezierCurveList[BezierCurveList.Count-1].EndVector2D; } stringpath=$"M{BezierCurveList[0].StartVector2D.ToString()}"; foreach(variteminBezierCurveList) { path+=$"C{item.StartControlPoint.ToString("")},{item.EndControlPoint.ToString("")},{item.EndVector2D.ToString("")}"; } returnpath; } publicLineB() { } publicLineB(List<Vector2D>verVector2DList,boolisClosedCurve=true) { this.Vector2DList=verVector2DList; this.IsClosedCurve=isClosedCurve; } ///<summary> ///重载隐式转换,可以直接使用Point ///</summary> ///<paramname="v"></param> publicstaticimplicitoperatorGeometry(LineBlineB)//隐式转换 { returnGeometry.Parse(lineB.PathData); } } publicclassBezierCurve { privateVector2D_startVector2D=newVector2D(0,0); publicVector2DStartVector2D { get{return_startVector2D;} set{_startVector2D=value;} } privateVector2D_startControlPoint=newVector2D(0,100); publicVector2DStartControlPoint { get{return_startControlPoint;} set{_startControlPoint=value;} } privateVector2D_endControlPoint=newVector2D(100,0); publicVector2DEndControlPoint { get{return_endControlPoint;} set{_endControlPoint=value;} } privateVector2D_endVector2D=newVector2D(100,100); publicVector2DEndVector2D { get{return_endVector2D;} set{_endVector2D=value;} } } }
4)CanvasHandWritingExample.xaml 代码如下
<UserControlx:Class="WPFDevelopers.Samples.ExampleViews.CanvasHandWriting.CanvasHandWritingExample" 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:local="clr-namespace:WPFDevelopers.Samples.ExampleViews.CanvasHandWriting" mc:Ignorable="d" d:DesignHeight="450"d:DesignWidth="800"> <UserControl.Resources> <StyleTargetType="{x:TypeTextblock}"> <SetterProperty="Foreground"Value="{StaticResourcePrimaryTextSolidColorBrush}"/> </Style> </UserControl.Resources> <Grid> <Grid.RowDefinitions> <RowDefinitionHeight="auto"/> <RowDefinition/> </Grid.RowDefinitions> <StackPanelOrientation="Horizontal"Margin="4"> <TextBlockText="张力:"VerticalAlignment="Center"/> <TextBoxText="{BindingTension,RelativeSource={RelativeSourceAncestorType=local:CanvasHandWritingExample}}"/> <SandroidliderWidth="100"SmallChange="0.01" Value="{BindingTension,RelativeSource={RelativeSourceAncestorType=local:CanvasHandWritingExample}}"Maximum="1" VerticalAlignment="Center" Margin="5,0"/> <TextBlockText="平滑采样:"VerticalAlignment="Center"/> <TextBoxText="{BindingSmoothSampling,RelativeSource={RelativeSourceAncestorType=local:CanvasHandWritingExample}}" Margin="5,0"/> <SliderValue="{BindingSmoothSampling,RelativeSource={RelativeSourceAncestorType=local:CanvasHandWritingExample}}" Width="100" VerticalAlignment="Center" SmallChange="0.01"Maximum="1" TickFrequency="0.1"/> <CheckBoxContent="橡皮擦" VerticalAlignment="Center" Margin="5,0" IsChecked="{BindingIsEraser,RelativeSource={RelativeSourceAncestorType=local:CanvasHandWritingExample}}"/> <ButtonContent="清空画布"Click="btnClertCanvas_Click"/> </StackPanel> <Canvasx:Name="drawingCanvas" Grid.Row="1"Background="Black" PreviewMouseLeftButtonDown="DrawingCanvas_PreviewMouseLeftButtonDown" PreviewMouseMove="DrawingCanvas_PreviewMouseMove" PreviewMouseLeftButtonUp="DrawingCanvas_PreviewMouseLeftButtonUp"/> </Grid> </UserControl&gpythont;
5)CanvasHandWritingExample.xaml.cs 代码如下
usingSystem; usingSystem.Collections.Generic; usingSystem.Threading; usingSystem.Threading.Tasks; usingSystem.Windows; usingSystem.Windows.Controls; usingSystem.Windows.Input; usingSystem.Windows.Media; usingSystem.Windows.Shapes; namespaceWPFDevelopers.Samples.ExampleViews.CanvasHandWriting { ///<summary> ///CanvasHandWritingExample.xaml的交互逻辑 ///</summary> publicpartialclassCanvasHandWritingExample:UserControl { publicstaticreadonlyDependencyPropertyTensionProperty= DependencyProperty.Register("Tension",typeof(double),typeof(CanvasHandWritingExample), newPropertyMetadata(0.618)); publicstaticreadonlyDependencyPropertySmoothSamplingProperty= DependencyProperty.Register("SmoothSampling",typeof(double),typeof(CanvasHandWritingExample), newUIPropertyMetadata(OnSmoothSamplingChanged)); publicstaticreadonlyDependencyPropertyIsEraserProperty= DependencyProperty.Register("IsEraser",typeof(bool),typeof(CanvasHandWritingExample), newPropertyMetadata(false)); privatereadonlyDictionary<Path,List<Vector2D>>_PathVector2DDictionary; volatilebool_IsStart=false; Path_DrawingPath=default; privatestaticvoidOnSmoothSamplingChanged(DependencyObjectd,DependencyPropertyChangedEventArgse) { varmWindow=(CanvasHandWritingExample)d; foreach(variteminmWindow._PathVector2DDictionary.Keys) { mWindow.DrawLine(item); } } publicCanvasHandWritingExample() { InitializeComponent(); _PathVector2DDictionary=newDictionary<Path,List<Vector2D>>(); SmoothSampling=0.8; } privatevoidDrawingCanvas_PreviewMouseLeftButtonDown(objectsender,MouseButtonEventArgse) { _IsStart=true; _DrawingPath=newPath() { StrokeDashCap=PenLineCap.Round, StrokeStartLineCap=PenLineCap.Round, StrokeEndLineCap=PenLineCap.Round, StrokeLineJoin=PenLineJoin.Round, }; if(IsEraser) { _DrawingPath.Stroke=newSolidColorBrush(Colors.Black); _DrawingPath.StrokeThickness=40; } else { varrandom=newRandom(); varstrokeBrush=newSolidColorBrush(Color.FromRgb((byte)random.Next(200,255),(byte)random.Next(0,255),(byte)random.Next(0,255))); _DrawingPath.Stroke=strokeBrush; _DrawingPath.StrokeThickness=10; } _PathVector2DDictionary.Add(_DrawingPath,newList<Vector2D>()); drawingCanvas.Children.Add(_DrawingPath); } privatevoidDrawingCanvas_PreviewMouseLeftButtonUp(objectsender,MouseButtonEventArgse) { _IsStart=false; _DrawingPath=default; } privatevoidDrawingCanvas_PreviewMouseMove(objectsender,MouseEventArgse) { if(!_IsStart) return; if(_DrawingPathisnull) return; Vector2DcurrenPoint=e.GetPosition(drawingCanvas); if(currenPoint.X<0||currenPoint.Y<0) return; if(currenPoint.X>drawingCanvas.ActualWidth||currenPoint.Y>drawingCanvas.ActualHeight) return; if(_PathVector2DDictionary[_DrawingPath].Count>0) { if(Vector2D.CalculateVectorDistance(currenPoint,_PathVector2DDictionary[_Dra开发者_C学习wingPath][_PathVector2DDictionary[_DrawingPath].Count-1])>1) _PathVector2DDictionary[_DrawingPath].Add(e.GetPosition(drawingCanvas)); } else _PathVector2DDictionary[_DrawingPath].Add(e.GetPosition(drawingCanvas)); DrawLine(_DrawingPath); } publicdoubleTension { get=>(double)GetValue(TensionProperty); set=>SetValue(TensionProperty,value); } publicdoubleSmoothSampling { get=>(double)GetValue(SmoothSamplingProperty); set=>SetValue(SmoothSamplingProperty,value); } publicboolIsEraser { get=>(bool)GetValue(IsEraserProperty); set=>SetValue(IsEraserProperty,value); } privatevoidDrawLine(Pathpath) { if(_PathVector2DDictionary[path].Count>2) { varpathVector2Ds=_PathVector2DDictionary[path]; varsmoothNum=(int)(_PathVector2DDictionary[path].Count*SmoothSampling); if(smoothNum>1) pathVector2Ds=ComputingHelper.AverageSampling(_PathVector2DDictionary[path],smoothNum); varlineB=newLineB(pathVector2Ds,false); lineB.Tension=Tension; path.Data=lineB; } } privatevoidbtnClertCanvas_Click(objectsender,RoutedEventArgse) { drawingCanvas.Children.Clear(); _PathVector2DDictionary.Clear(); } } }
到此这篇关于WPF+Canvas实现平滑笔迹的示例代码的文章就介绍到这了,更多相关WPF Canvas平滑笔迹内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!
精彩评论