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來定位。這里以繪制表格來舉例。為了更直觀的看到以鼠標為中心縮放的效果,表格中專門繪制了一個十字中心。

轉載請注明出處。



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM