zoukankan      html  css  js  c++  java
  • [MFC]SDI在图片背景上实现文本跟随鼠标移动

    SDI是单文档接口应用程序的简称。本文要实现的是在视图区域显示一张图片,然后在图片表层显示文字,并且文字跟随鼠标移动。思考一下,可以判断这个问题一共分为以下几个部分:1、显示图片;2、找到鼠标的位置;3、插入文字;4、自动移动文字。以下分步骤说明。

    1、首先是使用“打开”方式打开一张图片并显示,出于方便这里仅仅针对bmp格式的图片,具体是在CYourView类内部的OnDraw函数内添加如下代码:

        if(GetDocument()->GetPathName() != "")//载入图片
        {
            HBITMAP bitmap;
            bitmap = (HBITMAP)LoadImage(AfxGetInstanceHandle(),
                GetDocument()->GetPathName(), IMAGE_BITMAP, 0, 0,
                LR_LOADFROMFILE|LR_CREATEDIBSECTION);
            HBITMAP OldBitmap;
            CBitmap cbitmap;
            cbitmap.Attach(bitmap);
            cbitmap.GetBitmap(&m_bitmap);
    
    
            CDC MemDC;
            MemDC.CreateCompatibleDC(pDC);
            CRect rect;
            GetClientRect(&rect);
            OldBitmap = (HBITMAP)MemDC.SelectObject(bitmap);
            pDC->BitBlt(0, 0, m_bitmap.bmWidth, m_bitmap.bmHeight, &MemDC, 0, 0, SRCCOPY);
            MemDC.SelectObject(OldBitmap);
            MemDC.DeleteDC();
        }

    其中m_bitmap是一个BITMAP对象。显示图片的方法是比较常见的,这里不再说明。

    2、接下来需要知道有个消息处理函数OnMouseMove用来处理鼠标移动消息。所以首先要添加这个函数,这个可以在Class Wizard中找到。先添加这个函数。

    OnMouseMove(UINT nFlags, CPoint point);

    这个函数的第二个参数CPoint对象就是鼠标所在位置,可以使用如下方法找到对应坐标:

    Xcoords = point.x;
    Ycoords = point.y;

    3、然后就要插入文字了,在SDI中特定位置插入文字其实很简单,使用CDC->DrawText方法就可以实现。这个函数可以实现在指定的矩形框内添加格式化文本,有点像word中的插入对话框。具体语法如下:

    int DrawText(const CString& str, LPRECT lpRect, UINT nFormat);

    第一个参数是要输出的CString对象,第二个参数是显示文本的矩形区域,最后一个参数是文本的格式,比如居左居中等,一个使用例子如下:

    CClientDC cdc(this);
    cdc.DrawText("text", CRect(left, top, reight, bottom), DT_LEFT);


     

    4、插入文字实现之后,就要进行文字的移动了。首先我想到的是SDI中图形的移动。比如画一个矩形CDC->Rectangle方法,然后移动这个矩形框。有一个常用的图形移动方式是进行异或画图,即使用CDC->SetROP2(R2_XORPEN)这个方法画图。使用与原来画笔异或屏幕颜色再画一遍,可以实现清除已画图形的效果,因此图形的移动也就是如下过程:

    (1)异或画图

    (2)在原位置再次异或画图

    (3)在新位置异或画图

    (4)...

    把画图与OnMouseMove结合就能容易实现图形跟随鼠标移动。但是同样的方法可以用于文本的移动吗?答案是否定的。因为DrawText与其他画图函数有本质的区别,因为其没有用到画笔,所以SetROP2这个方法整个对文本没有效果。不过,有了移动图形的方法,可以容易想到所谓移动(图形或者文本)的本质其实就是首先把原位置的内容清除,然后在新位置重新画(好像是废话)。

      因此,移动文字的关键就在于如何把原位置的文字清除。(用DrawText插入一个空文本的方法实行不同的,因为不会影响已经插入的文本。)一个容易想到的方法是将原位置(DrawText对应的矩形)整个复制下来,然后在插入文字后重新绘制上去,就用覆盖的方式清除了文字。于是有了如下的方法。

    首先添加一个截图函数,叫做ScreenShot。

    CBitmap* CYourView::ScreenShot(CDC* pDC, CBitmap *memBitmap, int Xcoords, int Ycoords, int Width, int Height)
    {
        CDC memDC;
        memDC.CreateCompatibleDC(pDC);
        
        memBitmap->CreateCompatibleBitmap(pDC, Width, Height);
        memDC.SelectObject(memBitmap);
        memDC.BitBlt(0, 0, Width, Height, pDC, Xcoords, Ycoords, SRCCOPY);  
        memDC.DeleteDC();
    
        return memBitmap;
    
    }

    这个函数的处理过程很简单,将指定位置和大小的矩形内部的图片内容保存起来。

    具体的实现文本跟随鼠标移动的代码在OnMouseMove函数中:

    void CYourView::OnMouseMove(UINT nFlags, CPoint point)
    {
        // TODO: 在此添加消息处理程序代码和/或调用默认值
        CClientDC cdc(this);
        Xcoords = point.x;
        Ycoords = point.y;
    if(m_loaded_flag) 
        {
            if(m_first_show_flag == false)
            {
            
                OldX = Xcoords+10;
                OldY = Ycoords-10;
                memBitmap.DeleteObject();
                memBmp = ScreenShot(&cdc, &memBitmap, OldX, OldY, 100, 50);
    
                cdc.SetBkMode(TRANSPARENT);
                cdc.DrawText("text",CRect(OldX,OldY, OldX+100, OldY+50),DT_LEFT);
                m_first_show_flag = true;
            }
            else
            {
                cdc.SetBkMode(TRANSPARENT);//去除原来位置的文字
                BITMAP bm;
                memBmp->GetObject (sizeof(BITMAP),&bm);
                CDC MemDC;
                MemDC.CreateCompatibleDC(&cdc);
                CBitmap *pOldBitmap=MemDC.SelectObject(memBmp); 
                cdc.BitBlt(OldX,OldY,bm.bmWidth ,bm.bmHeight,&MemDC,0,0,SRCCOPY);    
                MemDC.SelectObject (pOldBitmap);
                MemDC.DeleteDC();
                
                //新坐标作为下次使用的前一次坐标
                OldX = Xcoords+10;
                OldY = Ycoords-10;
                memBitmap.DeleteObject();
                memBmp = ScreenShot(&cdc, &memBitmap, OldX, OldY, 100, 50);
                cdc.DrawText("text",CRect(OldX, OldY, OldX+100, OldY+50),DT_LEFT);//在新位置添加文字
                
            }
    
        }
    
        CScrollView::OnMouseMove(nFlags, point);
    }

    由于第一次鼠标放上去的状态跟后面的移动状态有点区别,说以代码中分开处理,因此还需要在OnDraw函数中if(GetDocument()->GetPathName() != "")语句内添加如下内容:

            m_loaded_flag = true;
         if(m_first_show_flag == true)
            {
                Invalidate();
                m_first_show_flag = false;
            }

    当载入图片后才显示跟随鼠标的文字。并且当移动图片时刷新以清除文字。

    总的来说整个过程是比较简单的,曾经看到过一个采用OnTimer实现的文字跟随鼠标移动的例子,其实就是不断地刷新屏幕来显示不同的文字,当有背景图时会出现严重的闪烁,需要采用其他方法来避免闪烁,但是使用我的方法就没有这个问题。一小部分的重绘在视觉上是没有影响的。

    另外要注意使用到CDC对象的回收,如果使用CDC *pDC = this->DetDC()这样的方法创建一个设备上下文则需要在不用的时候使用pDC->DeleteDC()删除DC,不然就会导致严重的内存泄露。但是使用CClientDC cdc(this)创建的CDC对象系统能够自动回收,所以以上代码中采用的是这种方法。

  • 相关阅读:
    redis 用scan 代替keys 解决百万数据模糊查询超时问题
    集成spring-ldap
    IDEA报Unable to save settings: Failed to save settings. Please restart IntelliJ IDEA随后闪退
    struts2响应文件 struts2下载txt
    QRCodeUtil 二维码生成 解析 带图片与不带图片的二维码
    java 手机号正则表达式 截止2019年6月最新
    Java遍历Map对象的四种方式效率对比
    SpringCloud Alibaba系列(二) Nacos高可用和持久化
    SpringCloud Alibaba系列(二) Nacos配置中心-分类配置
    SpringCloud Alibaba系列(二) Nacos使用
  • 原文地址:https://www.cnblogs.com/lcyty/p/3327237.html
Copyright © 2011-2022 走看看