WPF玩起来还是挺炫酷的。我实现的效果:不同色块交叉,交叉部分颜色叠加显示。(叠加部分暂时用随机颜色代替)。单独色块点击弹出以色块颜色为主的附属面板。踩了一些坑,从简单一步步完善。
判断是否交叉,并创建交叉区域的算法比较费事。
完整过程代码较复杂,算法也从初始简单到复杂再到简单。
交汇区域: 主要使用Path绘制,根据色块获取交汇区域的Intersect geometry获得。
判断交叉:实现了可判断任意多个色块交叉,并绘制出所有叠加区域的算法,但是色块越多,判断过程越占用资源。造成程序卡顿。最终修改为最多三个色块可进行叠加组合。
算法描述起来比较复杂,可复用性不高,就不费文字阐述了。
最后一个模块比较有趣,仅用简单的Ellipse, Rectangle,Line等教简单的Shape即可随意绘制出炫酷的UI效果,部分源码如下:
<Ellipse x:Name="EllipseLoadZm" Stroke="{Binding Path=Brush}" StrokeThickness="60" Margin="-98"
StrokeDashArray="0.4 0.12" StrokeDashCap="Flat"
RenderTransformOrigin="0.5,0.5">
<Ellipse.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform/>
<TranslateTransform/>
</TransformGroup>
</Ellipse.RenderTransform>
<Ellipse.OpacityMask>
<RadialGradientBrush>
<GradientStop Color="#33FFFFFF" Offset="0.466"/>
<GradientStop Color="#FFFBFBFB" Offset="0.871"/>
<GradientStop Color="Transparent" Offset="1"/>
<GradientStop Color="#4EFFFFFF" Offset="0.778"/>
<GradientStop Color="#FFF1EDED" Offset="0.281"/>
<GradientStop Color="#19FFFFFF" Offset="0.003"/>
</RadialGradientBrush>
</Ellipse.OpacityMask>
</Ellipse>
<Grid Margin="-818,-99,0,-99" HorizontalAlignment="Left"
Width="927" RenderTransformOrigin="1,0.5">
<Path HorizontalAlignment="Right"
Data="{StaticResource KeyPathDataColorDescript}">
<Path.Fill>
<LinearGradientBrush StartPoint="0,0.5" EndPoint="1,0.5" >
<GradientStop Color="{Binding Path=Fill}"/>
<GradientStop Color="Transparent" Offset="1"/>
</LinearGradientBrush>
</Path.Fill>
</Path>
<Line Y2="418" StrokeThickness="25" StrokeDashArray="1 0.2">
<Line.Stroke>
<LinearGradientBrush EndPoint="0.5,1.2" StartPoint="0.5,0">
<GradientStop Color="{Binding Path=Fill}" Offset="0"/>
<GradientStop Color="Transparent" Offset="1"/>
</LinearGradientBrush>
</Line.Stroke>
</Line>
<Canvas Margin="21,0,206,0" >
<Canvas.Background>
<SolidColorBrush Color="{Binding Path=Fill}" Opacity="0.3"/>
</Canvas.Background>
</Canvas>
</Grid>
<CombinedGeometry x:Key="KeyPathDataColorDescript" GeometryCombineMode="Exclude">
<CombinedGeometry.Geometry1>
<CombinedGeometry GeometryCombineMode="Intersect">
<CombinedGeometry.Geometry1>
<RectangleGeometry Rect="0,0,200,418">
<RectangleGeometry.Transform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform AngleY="18"/>
<RotateTransform />
<TranslateTransform />
</TransformGroup>
</RectangleGeometry.Transform>
</RectangleGeometry>
</CombinedGeometry.Geometry1>
<CombinedGeometry.Geometry2>
<RectangleGeometry Rect="0,0,200,418">
<RectangleGeometry.Transform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform AngleY="-18"/>
<RotateTransform />
<TranslateTransform />
</TransformGroup>
</RectangleGeometry.Transform>
</RectangleGeometry>
</CombinedGeometry.Geometry2>
</CombinedGeometry>
</CombinedGeometry.Geometry1>
<CombinedGeometry.Geometry2>
<EllipseGeometry RadiusX="145" RadiusY="145"
Center="195,209"/>
</CombinedGeometry.Geometry2>
</CombinedGeometry>
private Color ColorMixing(Color c1, Color c2)
{
int r = Math.Min(c1.R + c2.R, 255);
int g = Math.Min(c1.G + c2.G, 255);
int b = Math.Min(c1.B + c2.B, 255);
return Color.FromArgb(Convert.ToByte(255), Convert.ToByte(r),
Convert.ToByte(g), Convert.ToByte(b));
}
#region Intersect Geometry And Brush Info
private Geometry GetElementGeometry(ColorElement oColorElement)
{
Geometry oResult = oColorElement.EllipseMainZm.RenderedGeometry;
TranslateTransform oTranslate1 = new TranslateTransform();
Binding oBindingX = new Binding("X") { Source = oColorElement, Mode = BindingMode.OneWay };
BindingOperations.SetBinding(oTranslate1, TranslateTransform.XProperty, oBindingX);
Binding oBindingY = new Binding("Y") { Source = oColorElement, Mode = BindingMode.OneWay };
BindingOperations.SetBinding(oTranslate1, TranslateTransform.YProperty, oBindingY);
oResult.Transform = oTranslate1;
return oResult;
}
private Geometry GetCombinationGeometry(List<ColorElement> ltElements)
{
if (ltElements == null || ltElements.Count < 1)
return null;
else if (ltElements.Count == 1)
return this.GetElementGeometry(ltElements[0]);
else
{
CombinedGeometry oCombinationGeo = new CombinedGeometry();
oCombinationGeo.Geometry1 = this.GetElementGeometry(ltElements[0]);
oCombinationGeo.Geometry2 = this.GetElementGeometry(ltElements[1]);
oCombinationGeo.GeometryCombineMode = GeometryCombineMode.Intersect;
if (ltElements.Count > 2)
{
for (int i = 2; i < ltElements.Count; i++)
{
Geometry oGeo = this.GetElementGeometry(ltElements[i]);
oCombinationGeo = new CombinedGeometry(GeometryCombineMode.Intersect, oCombinationGeo, oGeo);
}
}
return oCombinationGeo;
}
}
private SolidColorBrush GetIntersectColor(List<ColorElement> ltElements)
{
SolidColorBrush oBrush = ltElements[0].Brush;
Color oColor = oBrush.Color;
for (int i = 1; i < ltElements.Count; i++)
{
Color oElementColor = ltElements[i].Brush.Color;
oColor = this.ColorMixing(oColor, oElementColor);
}
return new SolidColorBrush(oColor);
}
private Path GetElementsIntersect(List<ColorElement> ltElements)
{
Geometry oGeo = this.GetCombinationGeometry(ltElements);
Path oPath = new Path();
oPath.Data = oGeo;
oPath.StrokeThickness = 10;
oPath.Stroke = new SolidColorBrush(Colors.White);
oPath.Fill = this.GetIntersectColor(ltElements);
//oPath.Fill = this.Brush;
Canvas.SetZIndex(oPath, ltElements.Count - 1);
return oPath;
}
#endregion
using PublicLibrary.Controls;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using VTTEduColorMap.Common;
using Windows7.Multitouch.Manipulation;
using Windows7.Multitouch.WPF;
namespace VTTEduColorMap.Main.Intersect
{
/// <summary>
/// ColorElement.xaml 的交互逻辑
/// </summary>
public partial class ColorElement : BaseAnim
{
public SolidColorBrush Brush
{
get { return (SolidColorBrush)GetValue(BrushProperty); }
set { SetValue(BrushProperty, value); }
}
public static readonly DependencyProperty BrushProperty =
DependencyProperty.Register("Brush", typeof(SolidColorBrush), typeof(ColorElement),
new UIPropertyMetadata(new SolidColorBrush(Colors.White)));
public double CenterX
{
get { return (double)GetValue(CenterXProperty); }
set { SetValue(CenterXProperty, value); }
}
public static readonly DependencyProperty CenterXProperty =
DependencyProperty.Register("CenterX", typeof(double), typeof(ColorElement),
new UIPropertyMetadata(0.0));
public double CenterY
{
get { return (double)GetValue(CenterYProperty); }
set { SetValue(CenterYProperty, value); }
}
public static readonly DependencyProperty CenterYProperty =
DependencyProperty.Register("CenterY", typeof(double), typeof(ColorElement),
new UIPropertyMetadata(0.0));
private int _ID = 0;
public int ID
{
get { return _ID; }
set { _ID = value; }
}
static ColorElement()
{
XProperty.OverrideMetadata(typeof(ColorElement),
new FrameworkPropertyMetadata(0.0, new PropertyChangedCallback(XChanged)));
YProperty.OverrideMetadata(typeof(ColorElement),
new FrameworkPropertyMetadata(0.0, new PropertyChangedCallback(YChanged)));
}
private static void XChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
ColorElement oColorElement = obj as ColorElement;
oColorElement.CenterX = oColorElement.X + oColorElement.Width / 2d;
}
private static void YChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
ColorElement oColorElement = obj as ColorElement;
oColorElement.CenterY = oColorElement.Y + oColorElement.Height / 2d;
}
private ColorDescript _Descript;
public ColorDescript Descript
{
get { return _Descript; }
set { _Descript = value; }
}
public ColorElement()
{
InitializeComponent();
this.DataContext = this;
}
private bool IsBorderVisible = false;
public void ShowBorder()
{
if (!this.IsBorderVisible)
{
this.IsBorderVisible = true;
Storyboard sbAnim = this.Resources["SbShowBorderAnimZm"] as Storyboard;
if (sbAnim != null)
sbAnim.Begin();
}
}
private int _IntersectCount = 0;
public int IntersectCount
{
get { return _IntersectCount; }
set
{
_IntersectCount = value;
if (_IntersectCount == 0)
this.ShowBorder();
}
}
public void HideBorder()
{
if (this.IsBorderVisible)
{
this.IsBorderVisible = false;
Storyboard sbAnim = this.Resources["SbHideBorderAnimZm"] as Storyboard;
if (sbAnim != null)
sbAnim.Begin();
}
}
public ColorElement(SolidColorBrush oBrush)
{
InitializeComponent();
this.Brush = oBrush;
this.InitManipulationProcess();
this.Loaded += ColorElement_Loaded;
this.DataContext = this;
}
private void ColorElement_Loaded(object sender, RoutedEventArgs e)
{
this.Loaded -= ColorElement_Loaded;
this.ShowBorder();
}
#region Drag And Move
private readonly ManipulationProcessor _processor = new ManipulationProcessor(ProcessorManipulations.ALL);
private void InitManipulationProcess()
{
_processor.ManipulationCompleted += (s, e) =>
{
this.UpdateVelocity((double)_processor.Velocity.X, (double)_processor.Velocity.Y);
};
}
public void ProcessDown(int id, Point location)
{
_processor.ProcessDown((uint)id, location.ToDrawingPointF());
}
public void ProcessMove(int id, Point location)
{
_processor.ProcessMove((uint)id, location.ToDrawingPointF());
}
public void ProcessUp(int id, Point location)
{
_processor.ProcessUp((uint)id, location.ToDrawingPointF());
}
private double _vX = 0;
private double _vY = 0;
private const double FICTION = 0.95;
private const double V_DECAY = 0.45;
private const double V_SPRINGNESS = 13.6;
private void UpdateVelocity(double VelocityX, double VelocityY)
{
double offestX = Math.Round(VelocityX, 2);
double offestY = Math.Round(VelocityY, 2);
_vX = Math.Round(offestX * V_SPRINGNESS + _vX * V_DECAY, 2);
_vY = Math.Round(offestY * V_SPRINGNESS + _vY * V_DECAY, 2);
CompositionTarget.Rendering -= new EventHandler(CompositionTarget_LocationRendering);
CompositionTarget.Rendering += new EventHandler(CompositionTarget_LocationRendering);
}
public event DelegateOfColorElement OnInertiaMoved;
private void CompositionTarget_LocationRendering(object sender, EventArgs e)
{
double offsetX = this.X;
double offsetY = this.Y;
this.X = offsetX + _vX;
this.Y = offsetY + _vY;
if (this.OnInertiaMoved != null)
this.OnInertiaMoved(this);
if (this.X > 3840 - this.Width)
_vX = -Math.Abs(_vX);
else if (this.X <= 0)
_vX = Math.Abs(_vX);
if (offsetY > 2160 - this.Height)
_vY = -Math.Abs(_vY);
else if (offsetY <= 0)
_vY = Math.Abs(_vY);
_vX *= FICTION;
_vY *= FICTION;
if (Math.Abs(_vX) < 0.1)
CompositionTarget.Rendering -= new EventHandler(CompositionTarget_LocationRendering);
}
#endregion
private void ShowBorderAnim_Completed(object sender, EventArgs e)
{
if (this.IsBorderVisible)
{
Storyboard sbAnim = this.Resources["SbBorderAnimZm"] as Storyboard;
if (sbAnim != null)
sbAnim.Begin();
}
}
}
}