WPF 基于Transform实现画布超出边界触发计算

有些场景需要对画布边界做界限控制,此时需要计算画布的四个方向的界限和极值

先看效果图:

 

画布在通过RenderTransform 做变换,由于在变换的过程中,实际的宽高没有改变,需要通过Transform实时记录变换的状态

1、图中用到了是对Canvas做 RenderTransform的变换,支持缩放和移动

2、最外层需要包一层Border,主要是限定画布的实际的宽高,因为Canvas在做变换的时候,实际的宽高并不改变,画布canvas的移动的偏移量是通过计算Canvas与Border的宽高相对的位置

3、

  <Border x:Name="canvasGrid">

        <Canvas x:Name="writeBorad" Width="1920" Height="1080" MouseDown="WriteBorad_OnMouseDown"  MouseWheel="UIElement_OnMouseWheel" Background="{StaticResource test}" MouseUp="WriteBorad_OnMouseUp"  MouseMove="UIElement_OnMouseMove" >
            <Canvas.RenderTransform>
                <TransformGroup>
                    <ScaleTransform x:Name="scale"></ScaleTransform>
                    <TranslateTransform x:Name="trans"></TranslateTransform>
                </TransformGroup>
            </Canvas.RenderTransform>

            <Button Width="60" Height="60" Canvas.Left="100" Canvas.Top="100"></Button>

        </Canvas>
    </Border>

 

后台计算逻辑:

 public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            Loaded += MainWindow_Loaded;

            this.writeBorad.Width = 3840;
            this.writeBorad.Height = 2160;
        }

        private void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            //取整设置成刚好的网格大小
            var drawBrush = this.writeBorad.Background as DrawingBrush;
            if (drawBrush != null)
            {
                var size = drawBrush.Viewport.Size;
                //横向判断取整
                var div = writeBorad.Width / size.Width;
                var rem = writeBorad.Width % size.Width;

                if (rem != 0)
                {
                    writeBorad.Width = size.Width *(Math.Floor(div) + 1);
                }
                //纵向判断取整
                div = writeBorad.Height / size.Height;
                rem = writeBorad.Height % size.Height;
                if (rem != 0)
                {
                    writeBorad.Height =size.Height* (Math.Floor(div) + 1);
                }
            }

            trans.X = -writeBorad.Width / 2;
            trans.Y = -writeBorad.Height / 2;
        }

        private void UIElement_OnMouseWheel(object sender, MouseWheelEventArgs e)
        {
            Point point = e.GetPosition(writeBorad);
            var delta = e.Delta * 0.001;
            DowheelZoom(null, point, delta);
        }

        double scaleSize = 1;
        private void DowheelZoom(System.Windows.Media.TransformGroup group, Point point, double delta)
        {
            //按住ctrl缩放
           // if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
            {
                if ((scale.ScaleX + delta) <= 1)
                {
                    //return;
                    scale.ScaleX = 1;
                    scale.ScaleY = 1;
                }
                else if (scale.ScaleX + delta > 5)
                {
                    scale.ScaleX = 5;
                    scale.ScaleY = 5;
                }
                else
                {
                    scale.ScaleX += delta;
                    scale.ScaleY += delta;
                }
                var targetX = point.X * (scale.ScaleX - 1);
                var targetY = point.Y * (scale.ScaleX - 1);

                trans.X = -targetX;
                trans.Y = -targetY;

                Console.WriteLine($"{trans.X}==={trans.Y}");
                scaleSize = scale.ScaleX;
            }
        }


        double tranX;
        double tranY;
        Point startPoint;

        //判断是否鼠标点击
        private bool isMouseDown;

        private void UIElement_OnMouseMove(object sender, MouseEventArgs e)
        {
            if(e.LeftButton!= MouseButtonState.Pressed || !isMouseDown) return;
            Point mousePoint = e.GetPosition(canvasGrid);

            var targetX = tranX + mousePoint.X - startPoint.X;
            var targetY = tranY + mousePoint.Y - startPoint.Y;

            trans.X = targetX;
            trans.Y = targetY;

            Console.WriteLine($"{trans.X}==={trans.Y}");

            //边界检测
            //缩放后的尺寸
            var scaleWidth = writeBorad.ActualWidth * scaleSize;
            var scaleHeight = writeBorad.ActualHeight * scaleSize;

            var gridWidth = canvasGrid.ActualWidth;
            var gridHeight = canvasGrid.ActualHeight;

            //x轴最小x坐标
            var minX = -(scaleWidth - gridWidth);

            //y轴最小坐标
            var minY = -(scaleHeight - gridHeight);


            double resetX = 0;
            double resetY = 0;
//计算边界值
//x轴左侧坐标越界,缓存到point点 if (targetX > 0) { ////拖拽前在可拖动区域 //if(trans.X<0) targetX = 0; resetX = targetX; isNeedResetX = true; Console.WriteLine("左侧越界了"); } if (targetX < minX) { targetX = minX; resetX = targetX; isNeedResetX = true; Console.WriteLine("右侧越界了"); } if (targetY > 0) { targetY = 0; resetY = targetY; isNeedResetY = true; Console.WriteLine("上侧越界了"); } if (targetY < minY) { targetY = minY; resetY = targetY; Console.WriteLine("下侧越界了"); isNeedResetY = true; } ResetPoint=new Point(resetX,resetY); } private Point ResetPoint; private bool isNeedResetX; private bool isNeedResetY; private void WriteBorad_OnMouseDown(object sender, MouseButtonEventArgs e) { isMouseDown = true; writeBorad.CaptureMouse(); startPoint = e.GetPosition(canvasGrid); tranX = trans.X; tranY = trans.Y; isNeedResetX = false; isNeedResetY = false; } private void AddElement(Point position) { Button btn=new Button(); btn.Height = 60; btn.Width = 60; Canvas.SetLeft(btn,position.X); Canvas.SetTop(btn, position.Y); writeBorad.Children.Add(btn); } private void WriteBorad_OnMouseUp(object sender, MouseButtonEventArgs e) { AddElement(e.GetPosition(writeBorad)); isMouseDown = false; writeBorad.ReleaseMouseCapture(); if (isNeedResetX) { trans.X = ResetPoint.X; } if (isNeedResetY) { trans.Y = ResetPoint.Y; } isNeedResetY = isNeedResetX = false; ResetPoint =new Point(); if (isNeedResetY || isNeedResetX) { Console.WriteLine($"{trans.X}==={trans.Y}"); } } }

 

总结:1、通过Border限定画布的实际宽高,画布做Transform做平移缩放,通过实时计算宽高来计算偏移量,从而计算是否触发边界

2、本次演示用到Canvas的 ScaleTransform 和  TranslateTransform 做的平移缩放的变换,后续修改为MatrixTransform,灵活性更高,支持多指触发 Manipulation 操作

3、后续增加超出边界触发回弹的效果,以及回弹动画的处理

 

posted @ 2021-11-20 10:56  wuty007  阅读(831)  评论(0编辑  收藏  举报