欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

C#图片缩放平移 —— 从功能分析到编码实现

程序员文章站 2022-04-28 13:49:18
序 一直都是在看别人的博客,查到想要的,看完后把页面一关就万事大吉了,没啥感觉;直到后来遇到了同样的问题,总想不起来咋弄,关键是还查不到以前看过的,郁闷!现在想想,还是“好记性不如烂笔头”啊,自己弄过的东西总要留下的什么呀,不然你都不知道自己曾经多么优秀。注册博客园也好久了,因为不知道该写点啥,再加 ......

       一直都是在看别人的博客,查到想要的,看完后把页面一关就万事大吉了,没啥感觉;直到后来遇到了同样的问题,总想不起来咋弄,关键是还查不到以前看过的,郁闷!现在想想,还是“好记性不如烂笔头”啊,自己弄过的东西总要留下的什么呀,不然你都不知道自己曾经多么优秀。注册博客园也好久了,因为不知道该写点啥,再加上懒,一直没有去管它,今日有空,正好开张!


 1. 需求说明

       这个没啥好说的,主要干三个事,用电脑的照片查看器打开一张你宝贝的自拍照。

       (1)拉动显示窗口,图片按照原有比例被放大和缩小,照片查看器中当图片没能完全显示时,拉框时只是拉框,我们不管这个,只要图片显示窗口变了,那就按照原有比例被放大和缩小。

       (2)鼠标放在图片的有效区域,鼠标滚轮放大和缩小图片,缩小时最小只能到图片原大小;放大无限制,照片查看器放大也有限制,咱也不管它。

       (3)鼠标放在图片的有效区域,按住鼠标左键平移图片,平移时只能平移图片有效范围。


 2. 功能分析

       想想上面要实现的功能,结合c#,我们用winform的窗体程序来实现,图片显示用picturebox控件,它有一个pictureboxsizemode属性,值改成zoom,这样就能保证picturebox控件里面的图片随picturebox控件大小改变而按照原有比例缩放,然后把picturebox控件放大form窗体中,dock属性改成fill填满就可以了,但dock属性改成fill填满之后,picturebox控件的大小变得无法改变(我也是试了之后才知道的),一种有效的解决方案是在窗体里面放一个panel控件,dock属性fill,然后把picturebox控件放在panel中,大小改成和panel控件一样大,再加一个panel控件的sizechanged事件,随时设置picturebox控件和panel控件一样大。这里不细说,具体看下面的c#编码实现,咱重点说说picturebox控件里的图斑如何缩放和平移。

       要想实现缩放和平移,首先我们得了解它实现的原理,这是下面编码实现的基础。因为图片随picturebox控件大小改变而按照原有比例缩放,因此我们改变picturebox控件的大小,也就是它的width和height属性,在视觉上就能看到图片被放大和缩小,也就是缩放;当图片被放大后,窗体中不能显示完整的图片内容,这时就需要我们通过平移来查看未能显示在窗体上的图片部分了,同样的,我们只要改变picturebox控件的位置,也就是它的left和top属性,就能把需要展示的图片局部正好显示在窗体上,从而在视觉上看到图片平移。

       原理简单说明了一下后,所以,我们想要实现缩放与偏移,本质上就是计算picturebox控件的大小和位置,只要搞定了这个,缩放平移也就搞定了。那么这个大小和位置咋算呢,请接着往下看。我们知道照片查看器缩放用的鼠标滚轮,前滚放大,后滚缩小。picturebox控件中找一下,mousewheel事件正好干这个事。再一查,哎呀,systeminformation.mousewheelscrolllines代码滚一格(微软叫它制动器)代表多少行。那就好办了,我们把这个多少行按一定的比例转换成picturebox控件left、top、width、height四个属性的增量,加上原值后,调整与显示窗体大小以及图片有效区域的位置关系,重新赋值回去就ok了。平移稍稍麻烦一点,其实也不是太麻烦。涉及到mousedown、mousemove、mouseup三个事件,在鼠标按下时记录下按下点坐标,同时标识正在平移操作;在鼠标移动时计算移动的距离,换算left、top的增量,并与显示窗体大小和图片有效区域做调整,最后赋值会这俩属性;鼠标弹起时结束平移操作标识。


 3. 编码实现

       新建一个窗体应用程序,改窗体名称为frmmian,在其内添加一个panel控件,命名pel;再在panel控件中添加一个picturebox控件,命名pboimage,以下为窗体类需要编写的代码:

C#图片缩放平移 —— 从功能分析到编码实现
public partial class frmmian : form
{
    public frmmian()
    {
        initializecomponent();

        this.pel.dock = system.windows.forms.dockstyle.fill;
        this.pel.sizechanged += new system.eventhandler(this.pel_sizechanged);

        this.pboimage.margin = new system.windows.forms.padding(0);
        this.pboimage.location = new system.drawing.point(0, 0);
        this.pboimage.size = new system.drawing.size(this.pel.width, this.pel.height);
        this.pboimage.sizemode = system.windows.forms.pictureboxsizemode.zoom;
        this.pboimage.cursor = cursors.sizeall;
        this.pboimage.mousedown += new system.windows.forms.mouseeventhandler(this.pboimage_mousedown);
        this.pboimage.mouseenter += new system.eventhandler(this.pboimage_mouseenter);
        this.pboimage.mousemove += new system.windows.forms.mouseeventhandler(this.pboimage_mousemove);
        this.pboimage.mouseup += new system.windows.forms.mouseeventhandler(this.pboimage_mouseup);
        this.pboimage.mousewheel += new system.windows.forms.mouseeventhandler(this.pboimage_mousewheel);

        pboimage.image = image.fromfile(@"c:\宝贝自拍照.jpg");
    }

    private system.drawing.point mousedownpoint = new system.drawing.point();//平移时鼠标按下的位置
    private bool isselected = false;    //鼠标是否是按下状态

    //pboimage获取焦点事件
    private void pboimage_mouseenter(object sender, eventargs e)
    {
        pboimage.focus();
    }

    //pboimage鼠标滚轮事件
    private void pboimage_mousewheel(object sender, mouseeventargs e)
    {
        if (pboimage.image == null) return;

        //计算缩放后的锚点和宽高
        int i = e.delta * systeminformation.mousewheelscrolllines / 4;
        int left = pboimage.left - i / 2, top = pboimage.top - i / 2;
        int width = pboimage.width + i, heigth = pboimage.height + i;

        if (i < 0)      //缩小时需要考虑与显示范围间关系,放大时无需考虑
        {
            //计算缩放后图片有效范围
            double widthscale = convert.todouble(pboimage.image.width) / width;
            double heigthscale = convert.todouble(pboimage.image.height) / heigth;
            if (widthscale > heigthscale)
            {
                top = top + convert.toint32(math.ceiling(heigth - (pboimage.image.height / widthscale))) / 2;
                heigth = convert.toint32(math.ceiling(pboimage.image.height / widthscale));
            }
            else
            {
                left = left + convert.toint32(math.ceiling(width - (pboimage.image.width / heigthscale))) / 2;
                width = convert.toint32(math.ceiling(pboimage.image.width / heigthscale));
            }

            if (left > 0)   //左侧在显示范围内部,调整到左边界
            {
                if (width - left < pel.width) width = pel.width;
                else width = width - left;
                left = 0;
            }
            if (left + width < pel.width)//右侧在显示范围内部,调整到右边界
            {
                if (pel.width - width > 0) left = 0;
                else left = pel.width - width;
                width = pel.width - left;
            }

            if (top > 0)//上侧在显示范围内部,调整到上边界
            {
                if (heigth - top < pel.height) heigth = pel.height;
                else heigth = heigth - top;
                top = 0;
            }
            if (top + heigth < pel.height)//下侧在显示范围内部,调整到下边界
            {
                if (pel.height - heigth > 0) top = 0;
                else top = pel.height - heigth;
                heigth = pel.height - top;
            }
        }

        pboimage.width = width;
        pboimage.height = heigth;
        pboimage.left = left;
        pboimage.top = top;
    }

    //pboimage鼠标按下事件
    private void pboimage_mousedown(object sender, mouseeventargs e)
    {
        if (pboimage.image == null) return;

        if (e.button == mousebuttons.left)
        {
            //记录摁下点坐标,作为平移原点
            mousedownpoint.x = pointtoclient(system.windows.forms.cursor.position).x;
            mousedownpoint.y = pointtoclient(system.windows.forms.cursor.position).y;
            isselected = true;
            pboimage.cursor = cursors.hand;
        }
    }

    //pboimage鼠标移动事件
    private void pboimage_mousemove(object sender, mouseeventargs e)
    {
        if (pboimage.image == null) return;

        //计算图片有效范围
        double widthscale = convert.todouble(pboimage.image.width) / pboimage.width;
        double heigthscale = convert.todouble(pboimage.image.height) / pboimage.height;
        int invalidtop = pboimage.top, invalidheigth = pboimage.height, invalidleft = pboimage.left, invalidwidth = pboimage.width;
        if (widthscale > heigthscale)
        {
            invalidtop = invalidtop + ((int)math.ceiling(invalidheigth - (pboimage.image.height / widthscale))) / 2;
            invalidheigth = (int)math.ceiling(pboimage.image.height / widthscale);
        }
        else
        {
            invalidleft = invalidleft + ((int)math.ceiling(invalidwidth - (pboimage.image.width / heigthscale))) / 2;
            invalidwidth = (int)math.ceiling(pboimage.image.width / heigthscale);
        }

        //鼠标是否摁在图片上
        bool ismouseinpanel = invalidleft < pointtoclient(system.windows.forms.cursor.position).x &&
                                pointtoclient(system.windows.forms.cursor.position).x < invalidleft + invalidwidth &&
                                invalidtop < pointtoclient(system.windows.forms.cursor.position).y &&
                                pointtoclient(system.windows.forms.cursor.position).y < invalidtop + invalidheigth;
        if (isselected && ismouseinpanel)
        {
            //计算平移后图片有效范围的锚点和宽高
            int left = invalidleft + (pointtoclient(system.windows.forms.cursor.position).x - mousedownpoint.x);
            int top = invalidtop + (pointtoclient(system.windows.forms.cursor.position).y - mousedownpoint.y);
            int right = left + invalidwidth;
            int down = top + invalidheigth;

            if (left >= invalidleft && left >= 0) left = 0; //向右平移且平移后在显示范围内部,调整到左边界
            if (left < invalidleft && right <= pel.width) left = left + pel.width - right;//向左平移且平移后在显示范围内部,调整到右边界
            if (top >= invalidtop && top >= 0) top = 0;//向下平移且平移后在显示范围内部,调整到上边界
            if (top < invalidtop && down <= pel.height) top = top + pel.height - down;//向上平移且平移后在显示范围内部,调整到下  边界

            //有效范围锚点换算到整体的锚点
            left = left + pboimage.left - invalidleft;
            top = top + pboimage.top - invalidtop;

            if (invalidleft <= 0) pboimage.left = left;
            if (invalidtop <= 0) pboimage.top = top;

            //记录摁下点坐标,作为平移原点
            mousedownpoint.x = pointtoclient(system.windows.forms.cursor.position).x;
            mousedownpoint.y = pointtoclient(system.windows.forms.cursor.position).y;
        }
    }

    //pboimage鼠标弹起事件
    private void pboimage_mouseup(object sender, mouseeventargs e)
    {
        if (pboimage.image == null) return;
        isselected = false;
        pboimage.cursor = cursors.sizeall;
    }

    //pel大小改变事件
    private void pel_sizechanged(object sender, eventargs e)
    {
        pboimage.left = 0;
        pboimage.top = 0;
        pboimage.width = pel.width;
        pboimage.height = pel.height;
    }
}
参考代码 


作者:
出处: 
本文为作者原创,版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利。