WPF特效-绘图

本文分享了使用WPF创建炫酷UI的过程,包括不同色块交叉效果、交互式面板设计等内容。作者逐步介绍了从基础到复杂的算法优化,以及如何利用简单的Shape元素绘制出复杂的界面效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    

 

      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();
            }
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DuelCode

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值