zoukankan      html  css  js  c++  java
  • C#图像中心缩放与移动

    C#中使用Graphics可以很方便的绘图,在绘完图后,往往需要对图进行缩放和移动。缩放时,将鼠标当前的位置作为缩放的中心来缩放,看效果图


    中心缩放的核心在于计算图形新的原点,请看代码

     public partial class Form1 : Form
        {
            #region 内部变量
            private Graphics _g = null;
            private Image _imageCache = null;
    
            /// <summary>
            /// 单元格的宽(100%)
            /// </summary>
            private int _cellWidth_px = 100;
            /// <summary>
            /// 单元格的高(100%)
            /// </summary>
            private int _cellHeight_px = 100;
    
            private float _zoomOld = 1.0f;
            private float _zoom = 1.0f;
            private float _zoomMin = 0.1f;
            private float _zoomMax = 1000f;
    
    
            /// <summary>
            /// 表格的左上角
            /// </summary>
            private PointF _gridLeftTop = new PointF(200, 200);
    
            private bool _leftButtonPress = false;
    
            private PointF _mousePosition = new PointF(0, 0);
            #endregion
    
            public Form1()
            {
                InitializeComponent();
    
                //设置Paint参数以便能更好的控制Paint.
                SetStyle(ControlStyles.AllPaintingInWmPaint |
                   ControlStyles.UserPaint | ControlStyles.OptimizedDoubleBuffer, true);
                this.MouseWheel += Form1_MouseWheel; ;
            }
    
            private void Form1_MouseWheel(object sender, MouseEventArgs e)
            {
                var delta = e.Delta;
                if (Math.Abs(delta) < 10)
                {
                    return;
                }
                var mousePosition = new PointF();
                mousePosition.X = e.X;
                mousePosition.Y = e.Y;
                _zoomOld = _zoom;
    
                if (delta < 0)
                {
                    _zoom -= FetchStep(delta);
                }
                else if (delta > 0)
                {
                    _zoom += FetchStep(delta);
                }
                if (_zoom < _zoomMin)
                {
                    _zoom = _zoomMin;
                }
                else if (_zoom > _zoomMax)
                {
                    _zoom = _zoomMax;
                }
    
                var zoomNew = _zoom;
                var zoomOld = _zoomOld;
                var deltaZoomNewToOld = zoomNew / zoomOld;
    
                //计算零点
                //任意比例下的鼠标点与零点的距离deltaPO1都等于鼠标点与零点在(0,0)且比例为1时的距离deltaPO乘以缩放比例zoom,
                //于是有deltaPO1=deltaPO*zoom,
                //记在第1种缩放比例zoom1下有deltaPO1=deltaPO*zoom1,
                //记在第2种缩放比例zoom2下有deltaPO2=deltaPO*zoom2,
                //将上面两式相比则有deltaPO2/deltaPO1=zoom2/zoom1,令deltaZoomNewToOld=zoom2/zoom1则有deltaPO2/deltaPO1=deltaZoomNewToOld
                //又鼠标点与零点的距离deltaPO=P(x,y)-O(x,y),代入得
                //O.x2=P.x-(P.x-O.x1)*deltaZoomNewToOld;
                //O.y2=P.y-(P.y-O.y1)*deltaZoomNewToOld;
                var zero = _gridLeftTop;
                zero.X = mousePosition.X - (mousePosition.X - zero.X) * deltaZoomNewToOld;
                zero.Y = mousePosition.Y - (mousePosition.Y - zero.Y) * deltaZoomNewToOld;
                _gridLeftTop = zero;
    
                this.Refresh();
            }
    
            #region FetchStep
            /// <summary>
            /// 获取缩放的步进
            /// </summary>
            /// <returns></returns>
            private float FetchStep(float delta)
            {
                if (_zoom == 1)
                {
                    return delta > 0 ? 1 : 0.05f;
                }
                else
                {
                    return _zoom >= 1 ? 1 : 0.05f;
                }
            }
            #endregion
    
            private void Form1_Paint(object sender, PaintEventArgs e)
            {
                if (_imageCache == null)
                {
                    _imageCache = new Bitmap(this.Width, this.Height);
                }
    
                if (_g == null)
                {
                    _g = Graphics.FromImage(_imageCache);
                }
                _g.Clear(this.BackColor);
    
                DrawGrid(_g);
    
    
                e.Graphics.DrawImage(_imageCache, new Point(0, 0));
            }
    
            #region DrawGrid
            /// <summary>
            /// 绘制表格
            /// </summary>
            /// <param name="g"></param>
            private void DrawGrid(Graphics g)
            {
                float cellWidth = _zoom * _cellWidth_px;
                float cellHeight = _zoom * _cellHeight_px;
    
                //单元格的宽和高最小为1像素
                cellWidth = cellWidth < 1 ? 1 : cellWidth;
                cellHeight = cellHeight < 1 ? 1 : cellHeight;
    
    
                int rowCount = 3;
                int columnCount = 3;
                var gridHeight = rowCount * cellHeight;
                var gridWidth = columnCount * cellWidth;
    
                Pen pen = Pens.White;
                var p1 = new PointF();
                var p2 = new PointF();
                //绘制横线
                for (int r = 0; r <= rowCount; r++)
                {
                    p1.X = _gridLeftTop.X;
                    p1.Y = _gridLeftTop.Y + r * cellHeight;
    
                    p2.X = p1.X + gridWidth;
                    p2.Y = p1.Y;
    
                    g.DrawLine(pen, p1, p2);
                }
    
                //绘制竖线
                for (int c = 0; c <= columnCount; c++)
                {
                    p1.X = _gridLeftTop.X + c * cellWidth;
                    p1.Y = _gridLeftTop.Y;
    
                    p2.X = p1.X;
                    p2.Y = p1.Y + gridHeight;
                    g.DrawLine(pen, p1, p2);
                }
    
                //绘制表格的十字中心
                pen = Pens.Red;
                //十字横线
                p1.X = _gridLeftTop.X + gridWidth / 2 - cellWidth / 2;
                p1.Y = _gridLeftTop.Y + gridHeight / 2;
                p2.X = p1.X + cellWidth;
                p2.Y = p1.Y;
                g.DrawLine(pen, p1, p2);
                //十字竖线
                p1.X = _gridLeftTop.X + gridWidth / 2;
                p1.Y = _gridLeftTop.Y + gridHeight / 2 - cellHeight / 2;
                p2.X = p1.X;
                p2.Y = p1.Y + cellHeight;
                g.DrawLine(pen, p1, p2);
    
                //绘制比例
                p1.X = _gridLeftTop.X;
                p1.Y = _gridLeftTop.Y + gridHeight;
                g.DrawString($"{_zoom * 100}%", SystemFonts.DefaultFont, Brushes.White, p1);
            }
            #endregion
    
            private void Form1_MouseMove(object sender, MouseEventArgs e)
            {
                var offsetX = e.X - _mousePosition.X;
                var offsetY = e.Y - _mousePosition.Y;
                if (_leftButtonPress)
                {
                    _gridLeftTop.X += offsetX;
                    _gridLeftTop.Y += offsetY;
    
                    _mousePosition.X = e.X;
                    _mousePosition.Y = e.Y;
    
                    this.Refresh();
                }
            }
    
            private void Form1_MouseDown(object sender, MouseEventArgs e)
            {
                if (e.Button == MouseButtons.Left)
                {
                    _mousePosition.X = e.X;
                    _mousePosition.Y = e.Y;
    
                    _leftButtonPress = true;
                    this.Cursor = Cursors.Hand;
                }
            }
    
            private void Form1_MouseUp(object sender, MouseEventArgs e)
            {
                _leftButtonPress = false;
                this.Cursor = Cursors.Default;
            }
        }
    注:

    1.在鼠标滚动的时候进行缩放,这时重新计算原点,关键在于计算缩放前后的比例变化。

    2.鼠标左键按下并移动鼠标时开始移动图形,移动图形只要改变图形的原点即可。

    3.绘制图形时,图形内部的所有点都以图形的原点_gridLeftTop来定位。这里以绘制表格来举例。为了更直观的看到以鼠标为中心缩放的效果,表格中专门绘制了一个十字中心。

    转载请注明出处。


  • 相关阅读:
    kafka consumer防止数据丢失(转)
    Cglib源码分析 invoke和invokeSuper的差别(转)
    如何查看k8s存在etcd中的数据(转)
    k8s Nodeport方式下service访问,iptables处理逻辑(转)
    项目演化系列--验证体系
    项目演化系列--路由解析
    项目演化系列--开篇
    基于SWFUpload的angular上传组件
    Query Object--查询对象模式(下)
    Query Object--查询对象模式(上)
  • 原文地址:https://www.cnblogs.com/sparkleDai/p/7604899.html
Copyright © 2011-2022 走看看