精华内容
下载资源
问答
  • 闪烁

    2017-08-03 13:51:44
    用MFC如何高效绘图   显示图形如何避免闪烁,如何提高显示效率是问得比较多的问题。   而且多数人认为MFC的绘图函数效率很低,总是想寻求其它的解决方案。   MFC的绘图效率的确不高但也不差,而且它的...

    用MFC如何高效地绘图  
              显示图形如何避免闪烁,如何提高显示效率是问得比较多的问题。  
      而且多数人认为MFC的绘图函数效率很低,总是想寻求其它的解决方案。  
      MFC的绘图效率的确不高但也不差,而且它的绘图函数使用非常简单,  
      只要使用方法得当,再加上一些技巧,用MFC可以得到效率很高的绘图程序。  
      我想就我长期(呵呵当然也只有2年多)使用MFC绘图的经验谈谈  
      我的一些观点。  
      1、显示的图形为什么会闪烁?  
              我们的绘图过程大多放在OnDraw或者OnPaint函数中,OnDraw在进行屏  
      幕显示时是由OnPaint进行调用的。当窗口由于任何原因需要重绘时,  
      总是先用背景色将显示区清除,然后才调用OnPaint,而背景色往往与绘图内容  
      反差很大,这样在短时间内背景色与显示图形的交替出现,使得显示窗口看起来  
      在闪。如果将背景刷设置成NULL,这样无论怎样重绘图形都不会闪了。  
      当然,这样做会使得窗口的显示乱成一团,因为重绘时没有背景色对原来  
      绘制的图形进行清除,而又叠加上了新的图形。  
              有的人会说,闪烁是因为绘图的速度太慢或者显示的图形太复杂造成的,  
      其实这样说并不对,绘图的显示速度对闪烁的影响不是根本性的。  
      例如在OnDraw(CDC   *pDC)中这样写:  
        pDC->MoveTo(0,0);  
        pDC->LineTo(100,100);  
      这个绘图过程应该是非常简单、非常快了吧,但是拉动窗口变化时还是会看见  
      闪烁。其实从道理上讲,画图的过程越复杂越慢闪烁应该越少,因为绘图用的  
      时间与用背景清除屏幕所花的时间的比例越大人对闪烁的感觉会越不明显。  
      比如:清楚屏幕时间为1s绘图时间也是为1s,这样在10s内的连续重画中就要闪  
      烁5次;如果清楚屏幕时间为1s不变,而绘图时间为9s,这样10s内的连续重画  
      只会闪烁一次。这个也可以试验,在OnDraw(CDC   *pDC)中这样写:  
        for(int   i=0;i<100000;i++)  
        {  
          pDC->MoveTo(0,i);  
          pDC->LineTo(1000,i);  
        }  
      呵呵,程序有点变态,但是能说明问题。  
              说到这里可能又有人要说了,为什么一个简单图形看起来没有复杂图形那么  
      闪呢?这是因为复杂图形占的面积大,重画时造成的反差比较大,所以感觉上要  
      闪得厉害一些,但是闪烁频率要低。  
              那为什么动画的重画频率高,而看起来却不闪?这里,我就要再次强调了,  
      闪烁是什么?闪烁就是反差,反差越大,闪烁越厉害。因为动画的连续两个帧之间  
      的差异很小所以看起来不闪。如果不信,可以在动画的每一帧中间加一张纯白的帧,  
      不闪才怪呢。  
       
      2、如何避免闪烁  
              在知道图形显示闪烁的原因之后,对症下药就好办了。首先当然是去掉MFC  
      提供的背景绘制过程了。实现的方法很多,  
          *   可以在窗口形成时给窗口的注册类的背景刷付NULL  
          *   也可以在形成以后修改背景  
        static   CBrush   brush(RGB(255,0,0));  
        SetClassLong(this->m_hWnd,GCL_HBRBACKGROUND,(LONG)(HBRUSH)brush);  
          *   要简单也可以重载OnEraseBkgnd(CDC*   pDC)直接返回TRUE  
              这样背景没有了,结果图形显示的确不闪了,但是显示也象前面所说的一样,  
      变得一团乱。怎么办?这就要用到双缓存的方法了。双缓冲就是除了在屏幕上有  
      图形进行显示以外,在内存中也有图形在绘制。我们可以把要显示的图形先在内存中绘制好,然后再一次性的将内存中的图形按照一个点一个点地覆盖到屏幕上去(这个过程非常快,因为是非常规整的内存拷贝)。这样在内存中绘图时,随便用什么反差大的背景色进行清除都不会闪,因为看不见。当贴到屏幕上时,因为内存中最终的图形与屏幕显示图形差别很小(如果没有运动,当然就没有差别),这样看起来就不会闪。  
       
      3、如何实现双缓冲  
              首先给出实现的程序,然后再解释,同样是在OnDraw(CDC   *pDC)中:  
        CDC   MemDC;   //首先定义一个显示设备对象  
        CBitmap   MemBitmap;//定义一个位图对象  
        //随后建立与屏幕显示兼容的内存显示设备  
        MemDC.CreateCompatibleDC(NULL);  
        //这时还不能绘图,因为没有地方画   ^_^  
        //下面建立一个与屏幕显示兼容的位图,至于位图的大小嘛,可以用窗口的大小  
        MemBitmap.CreateCompatibleBitmap(pDC,nWidth,nHeight);  
           
        //将位图选入到内存显示设备中  
        //只有选入了位图的内存显示设备才有地方绘图,画到指定的位图上  
        CBitmap   *pOldBit=MemDC.SelectObject(&MemBitmap);  
        //先用背景色将位图清除干净,这里我用的是白色作为背景  
        //你也可以用自己应该用的颜色  
        MemDC.FillSolidRect(0,0,nWidth,nHeight,RGB(255,255,255));  
        //绘图  
        MemDC.MoveTo(……);  
        MemDC.LineTo(……);  
         
        //将内存中的图拷贝到屏幕上进行显示  
        pDC->BitBlt(0,0,nWidth,nHeight,&MemDC,0,0,SRCCOPY);  
        //绘图完成后的清理  
        MemBitmap.DeleteObject();  
        MemDC.DeleteDC();  
      上面的注释应该很详尽了,废话就不多说了。  
       
      4、如何提高绘图的效率  
              我主要做的是电力系统的网络图形的CAD软件,在一个窗口中往往要显示成千上万个电力元件,而每个元件又是由点、线、圆等基本图形构成。如果真要在一次重绘过程重画这么多元件,可想而知这个过程是非常漫长的。如果加上了图形的浏览功能,鼠标拖动图形滚动时需要进行大量的重绘,速度会慢得让用户将无法忍受。怎么办?只有再研究研究MFC的绘图过程了。  
              实际上,在OnDraw(CDC   *pDC)中绘制的图并不是所有都显示了的,例如:你  
      在OnDraw中画了两个矩形,在一次重绘中虽然两个矩形的绘制函数都有执行,但是很有可能只有一个显示了,这是因为MFC本身为了提高重绘的效率设置了裁剪区。裁剪区的作用就是:只有在这个区内的绘图过程才会真正有效,在区外的是无效的,即使在区外执行了绘图函数也是不会显示的。因为多数情况下窗口重绘的产生大多是因为窗口部分被遮挡或者窗口有滚动发生,改变的区域并不是整个图形而只有一小部分,这一部分需要改变的就是pDC中的裁剪区了。因为显示(往内存或者显存都叫显示)比绘图过程的计算要费时得多,有了裁剪区后显示的就只是应该显示的部分,大大提高了显示效率。但是这个裁剪区是MFC设置的,它已经为我们提高了显示效率,在进行复杂图形的绘制时如何进一步提高效率呢?那就只有去掉在裁剪区外的绘图过程了。可以先用pDC->GetClipBox()得到裁剪区,然后在绘图时判断你的图形是否在这个区内,如果在就画,不在就不画。  
      如果你的绘图过程不复杂,这样做可能对你的绘图效率不会有提高

    ==============================================================================

    在OnEraseBkGnd中,如果你不调用原来缺省
    的OnEraseBkGnd只是重画背景则不会有闪烁.而在OnPaint里面,
    由于它隐含的调用了OnEraseBkGnd,而你又没有处理OnEraseBkGnd
    函数,这时就和窗口缺省的背景刷相关了.缺省的
    OnEraseBkGnd操作使用窗口的缺省背景刷刷新背景(一般情况
    下是白刷),而随后你又自己重画背景造成屏幕闪动.
    另外一个问题是OnEraseBkGnd不是每次都会被调用的.如果你
    调用Invalidate的时候参数为TRUE,那么在OnPaint里面隐含
    调用BeginPaint的时候就产生WM_ERASEBKGND消息,如果参数
    是FALSE,则不会重刷背景.

    所以解决方法有三个半:
    1.用OnEraseBkGnd实现,不要调用原来的OnEraseBkGnd函数.
    2.用OnPaint实现,同时重载OnEraseBkGnd,其中直接返回.
    3.用OnPaint实现,创建窗口时设置背景刷为空
    4.用OnPaint实现,但是要求刷新时用Invalidate(FALSE)这样
    的函数.(不过这种情况下,窗口覆盖等造成的刷新还是要闪一
    下,所以不是彻底的解决方法)
    都挺简单的.
    ------------------------------------------------------
    在MFC中 任何一个window组件的绘图 都是放在这两个member function中
    在设定上 OnEraseBkgnd()是用来画底图的 而OnPaint()是用来画主要对象的
    举例说明 一个按钮是灰色的 上面还有文字
    则OnEraseBkgnd()所做的事就是把按钮画成灰色
    而OnPaint()所做的事 就是画上文字

    既然这两个member function都是用来画出组件的
    那为何还要分OnPaint() 与 OnEraseBkgnd() 呢
    其实OnPaint() 与 OnEraseBkgnd() 特性是有差的
    1. OnEraseBkgnd()的要求是快速 在里面的绘图程序最好是不要太耗时间
    因为 每当window组件有任何小变动 都会马上呼叫OnEraseBkgnd()
    2. OnPaint() 是只有在程序有空闲的时候才会被呼叫
    3. OnEraseBkgnd() 是在 OnPaint() 之前呼叫的
    所以 OnPaint()被呼叫一次之前 可能会呼叫OnEraseBkgnd()好几次


    如果我们是一个在做图形化使用者接口的人
    常会需要把一张美美的图片设为我们dialog的底图
    把绘图的程序代码放在OnPaint() 之中 可能会常碰到一些问题
    比方说拖曳一个窗口在我们做的dialog上面一直移动
    则dialog会变成灰色 直到动作停止才恢复
    这是因为每次需要重绘的时候 程序都会马上呼叫OnEraseBkgnd()
    OnEraseBkgnd()就把dialog画成灰色
    而只有动作停止之后 程序才会呼叫OnPaint() 这时才会把我们要画的底图贴上去


    这个问题的解法 比较差点的方法是把OnEraseBkgnd() 改写成不做事的function
    如下所示
    BOOL CMyDlg::OnEraseBkgnd(CDC* pDC)
    {
    return TRUE;
    }
    以上本来是会呼叫CDialog::OnEraseBkgnd() 但是如果我们不呼叫的话
    程序便不会画上灰色的底色了


    比较好的做法是直接将绘图的程序从OnPaint()移到OnEraseBkgnd()来做
    如下所示

    // m_bmpBKGND 为一CBitmap对象 且事先早已加载我们的底图
    // 底图的大小与我们的窗口client大小一致


    BOOL CMyDlg::OnEraseBkgnd(CDC* pDC)
    {
    CRect rc;
    GetUpdateRect(&rc);
    CDC srcDC;
    srcDC.CreateCompatibleDC(pDC);
    srcDC.SelectObject(m_bmpBKGND);

    pDC->BitBlt(rc.left,rc.top,rc.GetWidth(),
    rc.GetHeight(),&srcDC,rc.left,rc.top,SRCCOPY);
    return TRUE;
    }

    特别要注意的是 取得重画大小是使用GetUpdateRect() 而不是GetClientRect()
    如果使用GetClientRect() 会把不该重画的地方重画

    from:

    http://blog.163.com/wei_zhiwei/blog/static/130369656201031211425123/

    http://blog.163.com/wei_zhiwei/blog/static/1303696562010399538636/

    展开全文
  • 图形闪烁

    2011-08-07 22:30:51
    显示图形如何避免闪烁,如何提高显示效率是问得比较多的问题。而且多数人认为MFC的绘图函数效率很低,总是想寻求其它的解决方案。 MFC的绘图效率的确不高但也不差,而且它的绘图函数使用非常简单,只要使用方法得当...
    显示图形如何避免闪烁,如何提高显示效率是问得比较多的问题。而且多数人认为MFC的绘图函数效率很低,总是想寻求其它的解决方案。
    
    MFC的绘图效率的确不高但也不差,而且它的绘图函数使用非常简单,只要使用方法得当,再加上一些技巧,用MFC可以得到效率很高的绘图程序。
    我想就我长期(呵呵当然也只有2年多)使用MFC绘图的经验谈谈我的一些观点。

    1、显示的图形为什么会闪烁?
       我们的绘图过程大多放在OnDraw或者OnPaint函数中,OnDraw在进行屏幕显示时是由OnPaint进行调用的。当窗口由于任何原因需要重绘时,总是先用背景色将显示区清除,然后才调用OnPaint,而背景色往往与绘图内容反差很大,这样在短时间内背景色与显示图形的交替出现,使得显示窗口看起来在闪。如果将背景刷设置成NULL,这样无论怎样重绘图形都不会闪了。当然,这样做会使得窗口的显示乱成一团,因为重绘时没有背景色对原来绘制的图形进行清除,而又叠加上了新的图形。有的人会说,闪烁是因为绘图的速度太慢或者显示的图形太复杂造成的,其实这样说并不对,绘图的显示速度对闪烁的影响不是根本性的。例如在OnDraw(CDC *pDC)中这样写:
    pDC->MoveTo(0,0);
    pDC->LineTo(100,100);
    这个绘图过程应该是非常简单、非常快了吧,但是拉动窗口变化时还是会看见闪烁。其实从道理上讲,画图的过程越复杂越慢闪烁应该越少,因为绘图用的时间与用背景清除屏幕所花的时间的比例越大人对闪烁的感觉会越不明显。比如:清楚屏幕时间为1s绘图时间也是为1s,这样在10s内的连续重画中就要闪烁5次;如果清楚屏幕时间为1s不变,而绘图时间为9s,这样10s内的连续重画只会闪烁一次。这个也可以试验,在OnDraw(CDC *pDC)中这样写:
    for(int i=0;i<100000;i++)
    {
    pDC->MoveTo(0,i);
    pDC->LineTo(1000,i);
    }
    呵呵,程序有点变态,但是能说明问题。
       说到这里可能又有人要说了,为什么一个简单图形看起来没有复杂图形那么闪呢?这是因为复杂图形占的面积大,重画时造成的反差比较大,所以感觉上要闪得厉害一些,但是闪烁频率要低。那为什么 动画的重画频率高,而看起来却不闪?这里,我就要再次强调了,闪烁是什么?闪烁就是反差,反差越大,闪烁越厉害。因为 动画的连续两个帧之间的差异很小所以看起来不闪。如果不信,可以在 动画的每一帧中间加一张纯白的帧,不闪才怪呢。


    2、如何避免闪烁
       在知道图形显示闪烁的原因之后,对症下药就好办了。首先当然是去掉MFC提供的背景绘制过程了。实现的方法很多,
    * 可以在窗口形成时给窗口的注册类的背景刷付NULL
    * 也可以在形成以后修改背景
       static CBrush brush(RGB(255,0,0));
       SetClassLong(this->m_hWnd,GCL_HBRBACKGROUND,(LONG)(HBRUSH)brush);
    * 要简单也可以重载OnEraseBkgnd(CDC* pDC)直接返回TRUE
       这样背景没有了,结果图形显示的确不闪了,但是显示也象前面所说的一样,变得一团乱。怎么办?这就要用到双缓存的方法了。双缓冲就是除了在屏幕上有图形进行显示以外,在内存中也有图形在绘制。我们可以把要显示的图形先在内存中绘制好,然后再一次性的将内存中的图形按照一个点一个点地覆盖到屏幕上去(这个过程非常快,因为是非常规整的内存拷贝)。这样在内存中绘图时,随便用什么反差大的背景色进行清除都不会闪,因为看不见。当贴到屏幕上时,因为内存中最终的图形与屏幕显示图形差别很小(如果没有运动,当然就没有差别),这样看起来就不会闪。


    3、如何实现双缓冲
       首先给出实现的程序,然后再解释,同样是在OnDraw(CDC *pDC)中:

    CDC MemDC; //首先定义一个显示设备对象
    CBitmap MemBitmap;//定义一个位图对象

    //随后建立与屏幕显示兼容的内存显示设备
    MemDC.CreateCompatibleDC(NULL);
    //这时还不能绘图,因为没有地方画 ^_^
    //下面建立一个与屏幕显示兼容的位图,至于位图的大小嘛,可以用窗口的大小
    MemBitmap.CreateCompatibleBitmap(pDC,nWidth,nHeight);

    //将位图选入到内存显示设备中
    //只有选入了位图的内存显示设备才有地方绘图,画到指定的位图上
    CBitmap *pOldBit=MemDC.SelectObject(&MemBitmap);

    //先用背景色将位图清除干净,这里我用的是白色作为背景
    //你也可以用自己应该用的颜色
    MemDC.FillSolidRect(0,0,nWidth,nHeight,RGB(255,255,255));

    //绘图
    MemDC.MoveTo(……);
    MemDC.LineTo(……);

    //将内存中的图拷贝到屏幕上进行显示
    pDC->BitBlt(0,0,nWidth,nHeight,&MemDC,0,0,SRCCOPY);

    //绘图完成后的清理
    MemBitmap.DeleteObject();
    MemDC.DeleteDC();

    上面的注释应该很详尽了,废话就不多说了。


    4、如何提高绘图的效率
       我主要做的是电力系统的网络图形的CAD软件,在一个窗口中往往要显示成千上万个电力元件,而每个元件又是由点、线、圆等基本图形构成。如果真要在一次重绘过程重画这么多元件,可想而知这个过程是非常漫长的。如果加上了图形的浏览功能,鼠标拖动图形滚动时需要进行大量的重绘,速度会慢得让用户将无法忍受。怎么办?只有再研究研究MFC的绘图过程了。
       实际上,在OnDraw(CDC *pDC)中绘制的图并不是所有都显示了的,例如:你在OnDraw中画了两个矩形,在一次重绘中虽然两个矩形的绘制函数都有执行,但是很有可能只有一个显示了,这是因为MFC本身为了提高重绘的效率设置了裁剪区。裁剪区的作用就是:只有在这个区内的绘图过程才会真正有效,在区外的是无效的,即使在区外执行了绘图函数也是不会显示的。因为多数情况下窗口重绘的产生大多是因为窗口部分被遮挡或者窗口有滚动发生,改变的区域并不是整个图形而只有一小部分,这一部分需要改变的就是pDC中的裁剪区了。因为显示(往内存或者显存都叫显示)比绘图过程的计算要费时得多,有了裁剪区后显示的就只是应该显示的部分,大大提高了显示效率。但是这个裁剪区是MFC设置的,它已经为我们提高了显示效率,在进行复杂图形的绘制时如何进一步提高效率呢?那就只有去掉在裁剪区外的绘图过程了。可以先用pDC->GetClipBox()得到裁剪区,然后在绘图时判断你的图形是否在这个区内,如果在就画,不在就不画。
    如果你的绘图过程不复杂,这样做可能对你的绘图效率不会有提高。
    展开全文
  • rem布局加载闪烁问题

    千次阅读 2017-09-11 22:25:31
    先上体验地址:http://jiguang.qq.com/jiguang_app/kapian/html/card-query.html?from=singlemessage扫描二维码:问题rem布局在加载的时候会出现元素一开始很小,闪烁一下恢复正常大小,这是怎么回事呢?为此,我们...

    先上体验地址:http://jiguang.qq.com/jiguang_app/kapian/html/card-query.html?from=singlemessage

    扫描二维码:这里写图片描述

    问题

    翻到文章底部(反思1024),查看最新解决方案。

    rem布局在加载的时候会出现元素一开始很小,闪烁一下恢复正常大小,这是怎么回事呢?为此,我们来探讨一下!

    rem的布局具体不介绍了吧,你应该已经掌握或者会用了。

    这里我的设计稿是750px的,以下是我用来动态设置html根元素font-size的代码,这里我设置了最大值100px哈!

    !(function(doc, win) {
        var docEl = doc.documentElement,
            resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize',
            recalc = function() {
                var clientWidth = docEl.clientWidth;
                if (!clientWidth) return;
                if (clientWidth < 750) {
                    docEl.style.fontSize = 100 * (clientWidth / 750) + 'px';
                } else {
                    docEl.style.fontSize = '100px';
                }
            };
        if (!doc.addEventListener) return;
        win.addEventListener(resizeEvt, recalc, false);
        doc.addEventListener('DOMContentLoaded', recalc, false);
    })(document, window);

    我新建了一个common.js文件,把该js代码放里面,然后在</body>前引入

    <script src="js/common.js"></script>

    写好页面后发布看下,我们会看到html元素先很小,闪烁一下又正常了,虽然不影响功能,但是这给用户的体验是不行的啊,太明显了!

    正常应该是:

    这里写图片描述

    实际情况是:

    这里写图片描述

    解决

    模拟弱网环境,通过查看代码发现,DOM在渲染出来的时候,html根元素font-size的大小,js还没有设置进去。如下图

    这里写图片描述

    设想一:既然js看上去是执行的晚了,那我们就把js放在head中怎么样?
    测试发现结果还是一样!

    设想二:是不是通过外链引入js会耗费加载时间,我们直接把这段js放在</body>前怎么样?
    测试发现结果还是一样!

    设想三:是不是js代码放head中会快点?
    测试发现结果还是一样!

    算了,不设想了,这里我们就需要清楚,css/js对DOM树渲染的影响了

    • CSS(外链或内联)会阻塞整个DOM的渲染(Rendering),然而DOM解析(Parsing)会正常进行
    • 很多浏览器中,CSS会延迟脚本执行和DOMContentLoaded事件
    • JS(外链或内联)会阻塞后续DOM的解析(Parsing),后续DOM的渲染(Rendering)也将被阻塞
    • JS前的DOM可以正常解析(Parsing)和渲染(Rendering)

    说明:
    无论是外链CSS还是内联CSS都会阻塞DOM渲染(Rendering),然而DOM解析(Parsing)会正常进行。 这意味着在CSS下载并解析结束之前,它后面的HTML都不会显示。 这也是为什么我们把样式放在HTML内容之前,以防止被呈现内容发生样式跳动。 当然代价就是显示延迟,所以性能攸关的站点都会内联所有CSS。

    很多浏览器中CSS还会延迟脚本执行和DOMContentLoaded事件触发(该事件就是jQuery的dom ready)。

    不论是内联还是外链JavaScript都会阻塞后续DOM解析(Parsing),当然后续DOM的渲染(Rendering)也被阻塞了。 之所以DOM解析(Parsing)需要暂停, 是因为脚本中可能会包含类似document.write的语句,即脚本有可能改变当前DOM树。

    值得注意的是JavaScript只会阻塞后续的DOM而非整个DOM,这意味着前面的DOM可以被正确地解析以及渲染。 这也是为什么我们把脚本放在页面底部:脚本仍在下载时页面已经可以正常地显示了。

    下图可以表明资源加载时会先加载css再加载js。

    这里写图片描述

    结论:
    从html入手无法解决问题,那我们就从css入手吧!
    上面说了,既然我们的css都会选择优先加载,防止跳动,那我们是不是可以一开始就让网页是正常显示的呢,也就是说,我们来设置html的font-size属性,这里我们通过媒体查询,把相对应的设备区间的根元素font-size列举出来,因为我们是750px的设计稿,而且我们的开发方式是1rem=100px(如果你了解rem的话你应该理解),也就是说,在iPhone6的375像素宽的设备上,我们的html根元素font-size大小应该是50px,具体代码如下:

    @media (min-width: 320px){html{font-size: 42.6667px;} }
    @media (min-width: 360px){html{font-size: 48px;} }
    @media (min-width: 375px){html{font-size: 50px;} }
    @media (min-width: 384px){html{font-size: 51.2px;} }
    @media (min-width: 414px){html{font-size: 55.2px;} }
    @media (min-width: 448px){html{font-size: 59.7333px;} }
    @media (min-width: 480px){html{font-size: 48px;} }
    @media (min-width: 512px){html{font-size: 68.2667px;} }
    @media (min-width: 544px){html{font-size: 72.5333px;} }
    @media (min-width: 576px){html{font-size: 76.8px;} }
    @media (min-width: 608px){html{font-size: 81.0667px;} }
    @media (min-width: 640px){html{font-size: 85.3333px;} }
    @media (min-width: 750px){html{font-size: 100px;} }

    如此解决的话我们发现,一开始DOM还没有渲染完但可以正常展现的时候,html的根元素字体大小的设置来源于媒体查询,如下图;当DOM解析完成,js计算了html的根元素字体大小后,字体大小值来源于js设置的行内样式,计算后的值发挥作用!至此这个问题完美解决。

    (请忽略背景图片没加载出来的问题哈)

    这里写图片描述

    扩展

    到这个时候,你已经解决加载闪烁的问题了,但是运气不好的你还可能遇到一个问题,在某些设备上,会出现页面元素比期望值要小或者大,如下图,偷偷告诉你,这是设备系统字体大小的问题,详见下篇!rem布局在webview中页面错乱

    补充 0925

    如果你也好奇媒体查询的这些设备宽度节点哪里来的,有没有什么标准,那我会告诉你,我是在chrome的代码调试工具里找的,如下图,在箭头处找到 show media queries 就会显示啦。

    这里写图片描述

    当然除了显示媒体查询的节点,你还可以点击其他的选项看看,还有会有新发现的。比如可以显示手机外形,dpr等。是不是挺有意思的,快去试试吧!

    这里写图片描述


    反思 1024

    碰巧今天是程序员节哈哈哈!

    其实之前对这个解决方案一直不是很满意,我写了JS还要写一堆媒体查询,总感觉不对。

    这两天我想了下,问题的原因无非就是html一开始没有设置字体大小嘛,那我们就一开始按最常用的iPhone 6 尺寸,设置html的font-size: 50px;好了,这样的话JS动态计算和媒体查询,我们只要选择一套方案就好了,他们两个是不同的适配方案,而且媒体查询的目的就是为了加载css的时候就设置html字体大小,我居然把他们混合起来使用,感觉有点冗余了。

    有同学也会问了,设置html的font-size: 50px;就合理了吗?我的回答是,至少变化的范围非常小,以360px宽的设备为例,根字体大小应该是48px;以前相当于是从0px-48px,现在是50px-48px,不会造成很明显的闪烁问题。

    至于为什么设置为50px;首先,设计稿是基于750px来设计的,我们重构稿实现的时候根元素大小应该是设置为50px(在规定1rem=100px的前提下);其次,720px和750px这两个设备尺寸,是安卓和IOS设备中占比比较大的设备尺寸。

    最终方案:
    1.html{font-size: 50px;}//这个一定写
    2.JS动态计算和密集的媒体查询二选一

    展开全文
  • 用MFC如何高效绘图TouchMe 显示图形如何避免闪烁,如何提高显示效率是问得比较多的问题。而且多数人认为MFC的绘图函数效率很低,总是想寻求其它的解决方案。MFC的绘图效率的确不高但也不差,而且它的绘图函数使用...
      
    

    用MFC如何高效地绘图

    TouchMe

        显示图形如何避免闪烁,如何提高显示效率是问得比较多的问题。
    而且多数人认为MFC的绘图函数效率很低,总是想寻求其它的解决方案。
    MFC的绘图效率的确不高但也不差,而且它的绘图函数使用非常简单,
    只要使用方法得当,再加上一些技巧,用MFC可以得到效率很高的绘图程序。
    我想就我长期(呵呵当然也只有2年多)使用MFC绘图的经验谈谈
    我的一些观点。

    1、显示的图形为什么会闪烁?
        我们的绘图过程大多放在OnDraw或者OnPaint函数中,OnDraw在进行屏
    幕显示时是由OnPaint进行调用的。当窗口由于任何原因需要重绘时,
    总是先用背景色将显示区清除,然后才调用OnPaint,而背景色往往与绘图内容
    反差很大,这样在短时间内背景色与显示图形的交替出现,使得显示窗口看起来
    在闪。如果将背景刷设置成NULL,这样无论怎样重绘图形都不会闪了。
    当然,这样做会使得窗口的显示乱成一团,因为重绘时没有背景色对原来
    绘制的图形进行清除,而又叠加上了新的图形。
        有的人会说,闪烁是因为绘图的速度太慢或者显示的图形太复杂造成的,
    其实这样说并不对,绘图的显示速度对闪烁的影响不是根本性的。
    例如在OnDraw(CDC *pDC)中这样写:
     pDC->MoveTo(0,0);
     pDC->LineTo(100,100);
    这个绘图过程应该是非常简单、非常快了吧,但是拉动窗口变化时还是会看见
    闪烁。其实从道理上讲,画图的过程越复杂越慢闪烁应该越少,因为绘图用的
    时间与用背景清除屏幕所花的时间的比例越大人对闪烁的感觉会越不明显。
    比如:清楚屏幕时间为1s绘图时间也是为1s,这样在10s内的连续重画中就要闪
    烁5次;如果清楚屏幕时间为1s不变,而绘图时间为9s,这样10s内的连续重画
    只会闪烁一次。这个也可以试验,在OnDraw(CDC *pDC)中这样写:
     for(int i=0;i<100000;i++)
     {
      pDC->MoveTo(0,i);
      pDC->LineTo(1000,i);
     }
    呵呵,程序有点变态,但是能说明问题。
        说到这里可能又有人要说了,为什么一个简单图形看起来没有复杂图形那么
    闪呢?这是因为复杂图形占的面积大,重画时造成的反差比较大,所以感觉上要
    闪得厉害一些,但是闪烁频率要低。
        那为什么动画的重画频率高,而看起来却不闪?这里,我就要再次强调了,
    闪烁是什么?闪烁就是反差,反差越大,闪烁越厉害。因为动画的连续两个帧之间
    的差异很小所以看起来不闪。如果不信,可以在动画的每一帧中间加一张纯白的帧,
    不闪才怪呢。


    2、如何避免闪烁
        在知道图形显示闪烁的原因之后,对症下药就好办了。首先当然是去掉MFC
    提供的背景绘制过程了。实现的方法很多,
      * 可以在窗口形成时给窗口的注册类的背景刷付NULL
      * 也可以在形成以后修改背景
     static CBrush brush(RGB(255,0,0));
     SetClassLong(this->m_hWnd,GCL_HBRBACKGROUND,(LONG)(HBRUSH)brush);
      * 要简单也可以重载OnEraseBkgnd(CDC* pDC)直接返回TRUE
        这样背景没有了,结果图形显示的确不闪了,但是显示也象前面所说的一样,
    变得一团乱。怎么办?这就要用到双缓存的方法了。双缓冲就是除了在屏幕上有
    图形进行显示以外,在内存中也有图形在绘制。我们可以把要显示的图形先在内存中
    绘制好,然后再一次性的将内存中的图形按照一个点一个点地覆盖到屏幕上去(这个
    过程非常快,因为是非常规整的内存拷贝)。这样在内存中绘图时,随便用什么反差
    大的背景色进行清除都不会闪,因为看不见。当贴到屏幕上时,因为内存中最终的图形
    与屏幕显示图形差别很小(如果没有运动,当然就没有差别),这样看起来就不会闪。


    3、如何实现双缓冲
        首先给出实现的程序,然后再解释,同样是在OnDraw(CDC *pDC)中:

     CDC MemDC; //首先定义一个显示设备对象
     CBitmap MemBitmap;//定义一个位图对象

     //随后建立与屏幕显示兼容的内存显示设备
     MemDC.CreateCompatibleDC(NULL);
     //这时还不能绘图,因为没有地方画 ^_^
     //下面建立一个与屏幕显示兼容的位图,至于位图的大小嘛,可以用窗口的大小
     MemBitmap.CreateCompatibleBitmap(pDC,nWidth,nHeight);
      
     //将位图选入到内存显示设备中
     //只有选入了位图的内存显示设备才有地方绘图,画到指定的位图上
     CBitmap *pOldBit=MemDC.SelectObject(&MemBitmap);

     //先用背景色将位图清除干净,这里我用的是白色作为背景
     //你也可以用自己应该用的颜色
     MemDC.FillSolidRect(0,0,nWidth,nHeight,RGB(255,255,255));

     //绘图
     MemDC.MoveTo(……);
     MemDC.LineTo(……);
     
     //将内存中的图拷贝到屏幕上进行显示
     pDC->BitBlt(0,0,nWidth,nHeight,&MemDC,0,0,SRCCOPY);

     //绘图完成后的清理
     MemBitmap.DeleteObject();
     MemDC.DeleteDC();

    上面的注释应该很详尽了,废话就不多说了。


    4、如何提高绘图的效率
        我主要做的是电力系统的网络图形的CAD软件,在一个窗口中往往要显示成千上万个电力元件,而每个元件又是由点、线、圆等基本图形构成。如果真要在一次重绘过程重画这么多元件,可想而知这个过程是非常漫长的。如果加上了图形的浏览功能,鼠标拖动图形滚动时需要进行大量的重绘,速度会慢得让用户将无法忍受。怎么办?只有再研究研究MFC的绘图过程了。
        实际上,在OnDraw(CDC *pDC)中绘制的图并不是所有都显示了的,例如:你
    在OnDraw中画了两个矩形,在一次重绘中虽然两个矩形的绘制函数都有执行,但是很有可能只有一个显示了,这是因为MFC本身为了提高重绘的效率设置了裁剪区。裁剪区的作用就是:只有在这个区内的绘图过程才会真正有效,在区外的是无效的,即使在区外执行了绘图函数也是不会显示的。因为多数情况下窗口重绘的产生大多是因为窗口部分被遮挡或者窗口有滚动发生,改变的区域并不是整个图形而只有一小部分,这一部分需要改变的就是pDC中的裁剪区了。因为显示(往内存或者显存都叫显示)比绘图过程的计算要费时得多,有了裁剪区后显示的就只是应该显示的部分,大大提高了显示效率。但是这个裁剪区是MFC设置的,它已经为我们提高了显示效率,在进行复杂图形的绘制时如何进一步提高效率呢?那就只有去掉在裁剪区外的绘图过程了。可以先用pDC->GetClipBox()得到裁剪区,然后在绘图时判断你的图形是否在这个区内,如果在就画,不在就不画。
    如果你的绘图过程不复杂,这样做可能对你的绘图效率不会有提高。

    from

    http://www.host01.com/article/software/VisualC/20060917184432231.htm

     

    以一个例子来说明一些具体如何解决问题.

    from:http://www.programbbs.com/doc/4882.htm
    我想让一个区域动起来,
    如何解决窗口刷新时区域的闪烁。

    1. void CJhkljklView::OnDraw(CDC* pDC) 
    2.     CJhkljklDoc* pDoc = GetDocument(); 
    3.     ASSERT_VALID(pDoc); 
    4.     // TODO: add draw code for native data here 
    5.             int i; 
    6.     int x[20],y[20]; 
    7.     CPen hPen; 
    8.      POINT w[5]; 
    9.      
    10.              x[0]=a/100+10; 
    11.              x[1]=a/100+30; 
    12.                  x[2]=a/100+80; 
    13.                  x[3]=a/100+30; 
    14.                  x[4]=a/100+10;
    15.                      y[0]=10; 
    16.                   y[1]=10; 
    17.                    y[2]=25; 
    18.                     y[3]=40; 
    19.                      y[4]=40;    
    20.       for (i=0;i<5;i++) 
    21.       {         w[i].x=x[i]; 
    22.       w[i].y=y[i]; 
    23.       } 
    24.       //CClientDC dc(this); 
    25.              //hPen=CreatePen(PS_SOLID,1,RGB(255,0,0)); 
    26.       CRgn argn,Brgn; 
    27.         CBrush abrush(RGB(40,30,20)); 
    28.         argn.CreatePolygonRgn(w, 5, 1);// point为CPoint数组, 
    29.         pDC->FillRgn(&argn, &abrush); 
    30.          abrush.DeleteObject(); 
    31. }
    32. void CJhkljklView::OnTimer(UINT nIDEvent) 
    33.     // TODO: Add your message handler code here and/or call default 
    34.         InvalidateRect(NULL,true); 
    35.         UpdateWindow(); 
    36.         a+=100; 
    37.     CView::OnTimer(nIDEvent); 
    38. }
    39. int CJhkljklView::OnCreate(LPCREATESTRUCT lpCreateStruct) 
    40.     if (CView::OnCreate(lpCreateStruct) == -1) 
    41.         return -1; 
    42.      
    43.     // TODO: Add your specialized creation code here 
    44.     SetTimer(1,10,NULL); 
    45.     return 0; 
    46. }
    47. 利用定时器直接进行10毫秒的屏幕刷新,这样效果会出现不停的闪烁的情况.
    48. 解决方法利用双缓冲,首先触发WM_ERASEBKGND,然后修改返回TRUE; 
    49. 定义变量: 
    50. CBitmap *m_pBitmapOldBackground ; 
    51. CBitmap m_bitmapBackground ; 
    52. CDC m_dcBackground;
    53. //绘制背景 
    54. if(m_dcBackground.GetSafeHdc()== NULL|| (m_bitmapBackground.m_hObject == NULL)) 
    55.     { 
    56.         m_dcBackground.CreateCompatibleDC(&dc); 
    57.         m_bitmapBackground.CreateCompatibleBitmap(&dc,rect.Width(),rect.Height()) ; 
    58.         m_pBitmapOldBackground = m_dcBackground.SelectObject(&m_bitmapBackground) ; 
    59.         //DrawMeterBackground(&m_dcBackground, rect); 
    60.         CBrush brushFill, *pBrushOld;         
    61.         // 背景色黑色 
    62.         brushFill.DeleteObject(); 
    63.         brushFill.CreateSolidBrush(RGB(255, 255, 255)); 
    64.         pBrushOld = m_dcBackground.SelectObject(&brushFill); 
    65.         m_dcBackground.Rectangle(rect); 
    66.         m_dcBackground.SelectObject(pBrushOld); 
    67.     } 
    68.     memDC.BitBlt(0, 0, rect.Width(), rect.Height(), 
    69.                        &m_dcBackground, 0, 0, SRCCOPY) ;
    70.     //绘制图形 
    71.     int i; 
    72.     int x[20],y[20]; 
    73.     CPen hPen; 
    74.     POINT w[5]; 
    75.      
    76.     x[0]=a/100+10; 
    77.     x[1]=a/100+30; 
    78.     x[2]=a/100+80; 
    79.     x[3]=a/100+30; 
    80.     x[4]=a/100+10; 
    81.      
    82.     y[0]=10; 
    83.     y[1]=10; 
    84.     y[2]=25; 
    85.     y[3]=40; 
    86.     y[4]=40; 
    87.      
    88.     for (i=0;i<5;i++) 
    89.     {         w[i].x=x[i]; 
    90.     w[i].y=y[i]; 
    91.     } 
    92.     //CClientDC dc(this); 
    93.     //hPen=CreatePen(PS_SOLID,1,RGB(255,0,0)); 
    94.     CRgn argn,Brgn; 
    95.     CBrush abrush(RGB(40,30,20)); 
    96.     argn.CreatePolygonRgn(w, 5, 1);// point为CPoint数组, 
    97.     memDC.FillRgn(&argn, &abrush); 
    98.     abrush.DeleteObject(); 
    99. }

    这样编译运行程序就会出现屏幕不闪烁的情况了.

     

     

     

     

    展开全文
  • Verilog 实现数码管闪烁显示

    千次阅读 2019-12-17 20:53:54
    实现数码管闪烁显示不是简简单单和一个2hz的闪烁信号相与,我在这里用了使能端的思想,通过把一个闪烁信号传递到使能端,来使数码管闪烁。 使能端可以做很多事的 module segflitter (clk,segled,cat,enable); ...
  • 防止屏幕闪烁

    2012-08-21 21:28:00
    1.显示的图形为什么会闪烁  我们的绘图过程大多放在OnDraw或者OnPaint函数中,OnDraw在进行屏幕显示时是由OnPaint进行调用的。当窗口由于任何原因需要重绘时,总是先用背景色将显示区清除,然后才调用OnPaint,而...
  • 图像闪烁问题

    千次阅读 2013-04-10 18:23:54
    双缓存机制解决VC++绘图时的闪烁问题  用CDC画图时,相当于使用了缓冲,如果在画图时,不直接用CDC来画,如在OnDraw 当中,不直接用pDC指针来画,而是采用一个过渡的CDC:  CDC memDC;  memDC....
  • led灯闪烁代码Coding an LED Light is introductory project that shows you how software and hardware interact with each other. It's a simple project you can complete in a weekend that'll help you learn ...
  • 闪烁刷屏

    2008-08-05 13:27:00
    在实现绘图的过程中,显示的图形总是会闪烁,笔者曾经被这个问题折磨了好久,通过向高手请教,搜索资料,问题一基本解决,现将文档整理出来以供大家参考.1.显示的图形为什么会闪烁? 我们的绘图过程大多放在OnDraw...
  • MFC如何避免闪烁

    2014-06-18 10:01:00
    显示图形如何避免闪烁,如何提高显示效率是问得比较多的问题。而且多数人认为MFC的绘图函数效率很低,总是想寻求其它的解决方案。 MFC的绘图效率的确不高但也不差,而且它的绘图函数使用非常简单,只要使用方法得当...
  • MFC中闪烁的处理

    千次阅读 2007-10-14 10:10:00
    MFC 闪烁 绘制
  • VC++无闪烁刷屏技术

    2013-02-27 23:50:55
    VC++无闪烁刷屏技术  在实现绘图的过程中,显示的图形总是会闪烁,笔者曾经被这个问题折磨了好久,通过向高手请教,搜索资料,问题基本解决,现将文档整理出来以供大家参考. 1.显示的图形为什么会闪烁  我们...
  • MFC绘制图片闪烁详解

    2016-04-23 01:46:00
    用MFC如何高效绘图 显示图形如何避免闪烁,如何提高显示效率是问得比较多的问题。 而且多数人认为MFC的绘图函数效率很低,总是想寻求其它的解决方案。 MFC的绘图效率的确不高但也不差,而且它的绘图函数使用非常...
  • java 物体运动过程中闪烁问题浅谈

    千次阅读 2014-11-01 11:58:35
    本文从J2SE的一个再现了屏幕闪烁的Java Appilication简单动画实例展开,对屏幕闪烁的原因进行了分析,找出了闪烁成因的关键:update(Graphics g)函数对于前端屏幕的清屏。由此引出消除闪烁的方法——双缓冲。双缓冲...
  • 显示图形如何避免闪烁     显示图形如何避免闪烁,如何提高显示效率是问得比较多的问题。   而且多数人认为MFC的绘图函数效率很低,总是想寻求其它的解决方案。   MFC的绘图效率的确不高但也不差,而且它...
  • Alpha 闪烁效果

    2001-06-14 01:19:00
    Alpha 闪烁效果Philip Taylor 2000年9月 下载本文的源代码 (521 KB) 欢迎阅读 Driving DirectX。这个月,我将通过开发一个使用 alpha 的 Direct3D 屏幕保护程序继续对 alpha 混色进行探索。 图 1. MSDNSparkles 屏幕...
  • 显示图形如何避免闪烁     显示图形如何避免闪烁,如何提高显示效率是问得比较多的问题。   而且多数人认为MFC的绘图函数效率很低,总是想寻求其它的解决方案。   MFC的绘图效率的确不高但也不
  • 闪烁刷屏技术的实现 作者 :树爱兵 邮箱 :spily365@163.com  在实现绘图的过程中,显示的图形总是会闪烁,笔者曾经被这个问题折磨了好久,通过向高手请教,搜索资料,问题已基本解决,现将文档整理...
  • 1,刷新整个程序区域,有明显的闪烁情况   Invalidate( TRUE );   UpdateWindow();  2,刷新指定区域,该区域有闪烁情况,不过比上一种方法要好多了。   InvalidateRect( &m_rect, TRUE 0);  ...
  • 轻松消除贴图闪烁

    2007-09-07 13:50:00
    //========================================================================//TITLE:// 轻松消除贴图闪烁//AUTHOR:// norains//DATE:// Thursday 25-December -2006//Environment:// EVC4.0 + Standard SDK/...
  • 今天做实验,发现了屏幕闪烁问题,网上找到解决方法,在此谢谢原作者 转自http://blog.sina.com.cn/s/blog_611ca6300100ezfx.html ...本文从J2SE的一个再现了屏幕闪烁的Java Appilication简单动画实例展开,对屏幕闪...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 10,822
精华内容 4,328
关键字:

怎样地闪烁