精华内容
下载资源
问答
  • 代码重绘
    2021-11-20 15:34:29

    1.浏览器的渲染过程

    • 解析生成DOM Tree(此时包含所有节点,包括display:none);
    • 根据CSS Object Module(CCSSOM)计算节点的几何属性(坐标和大小)(margin,pading,height,width等),生成Render Tree(不包含display: none的节点);这一过程叫回流或者布局;
    • 在Render Tree进一步渲染其它属性。如:color等。

    2.重绘

    重绘(repaint):当元素样式的改变不影响布局时,浏览器将使用重绘对元素进行更新,此时由于只需要 UI 层面的重新像素绘制,因此损耗较少。
    常见的重绘操作有:

    • 改变元素颜色
    • 改变元素背景色
      在这里插入图片描述

    3.回流

    回流(reflow):又叫重排(layout)。当元素的尺寸、结构或者触发某些属性时,浏览器会重新渲染页面,称为回流。此时,浏览器需要重新经过计算,计算后还需要重新页面布局,因此是较重的操作。
    常见的回流操作有:

    • DOM的添加和删除;
    • 页面的加载;
    • 元素尺寸改变——边距、填充、边框、宽度和高度;
    • 元素位置的改变;
    • 内容变化,比如用户在input框中输入文字;
    • 浏览器窗口尺寸改变——resize事件发生时;
    • 获取某些属性:offsetTop、offsetLeft、 offsetWidth、offsetHeight、scrollTop、scrollLeft、scrollWidth、scrollHeight、 clientTop、clientLeft、clientWidth、clientHeight。(浏览器为了返回最精确的值,需要flush队列,因为队列中可能会有影响到这些值的操作)
      重点:回流必定会触发重绘,重绘不一定会触发回流。重绘的开销较小,回流的代价较高。

    4.如何减少重绘和回流

    • 使用 transform 替代 top
    • 使用 visibility 替换 display: none ,因为前者只会引起重绘,后者会引发回流(改变了布局)
    • 不要把节点的属性值放在一个循环里当成循环里的变量。
    • 不要使用 table 布局,可能很小的一个小改动会造成整个 table 的重新布局
    • 动画实现的速度的选择,动画速度越快,回流次数越多,也可以选择使用 requestAnimationFrame
    • CSS 选择符从右往左匹配查找,避免节点层级过多
    • 将频繁重绘或者回流的节点设置为图层,图层能够阻止该节点的渲染行为影响别的节点。比如对于 video 标签来说,浏览器会自动将该节点变为图层。
      浏览器的回流优化机制:浏览器会维护1个队列,把所有会引起重排、重绘的操作放入这个队列,等队列中的操作到了一定的数量或者到了一定的时间间隔,浏览器就会flush队列,进行一个批处理。这样就会让多次的重排、重绘变成一次重排重绘。

    4.一次渲染多个DOM会出现的问题

    • 1.如果一次性创建加载的DOM数量太多,js执行时间很长,会导致白屏
    • 2.浏览器是单线程的,用户点击、滚动的回调函数会添加到js任务队列尾部,然后等待执行。这些回调都因为等待js引擎创建渲染DOM而被阻塞了。所以二次点击的操作不会马上执行,而是在DOM加载完成之后又得到了执行,给出弹窗。造成卡的情况发生。

    5.原生js渲染大量DOM优化

    <body>
        <button onclick="alert('hello')">按钮</button>
        <ul>列表</ul>
    
        <script>
            // 插入100000条数据
            let total = 100000;
            // 设置每次插入的DOM数量
            let flag = 100;
            // 当前已经渲染的DOM数量
            let alreadyRender = 0;
            let ul = document.querySelector('ul');
    
            function loop() {
                requestAnimationFrame(() => {
                    const fragment = document.createDocumentFragment();
                    for (let i = 0; i < flag; i++) {
                        // DOM渲染完就退出
                        if (alreadyRender >= total) return;
                        const li = document.createElement('li');
                        li.innerText = Math.floor(Math.random() * total);
                        fragment.appendChild(li);
                        alreadyRender++;
                    }
                    ul.appendChild(fragment);
                    loop();
                })
            }
            loop()
        </script>
    </body>
    

    6.vue3封装高速渲染DOM组件

    更多相关内容
  • 在用XAML布局的时候,有时候为了使界面Metro化,有些Button要使用圆形代替默认...复制代码代码如下:<Style x:Key=”btnNext” TargetType=”Button”> ”Template”> <Setter> <ControlTemplate TargetType=”
  • 主要介绍了用PHP代码在网页上生成图片的方法和实例,十分的简单实用,有需要的小伙伴可以参考下。
  • c#重绘圆角button代码 可更改圆角半径,最大可形成半圆型,适当改变宽和高可形成圆形
  • 博文《关于highcharts的封装和刷新》中代码.
  • C#277重绘ListBox控件 源代码
  • C++ MFC 重绘CListCtrl、CTreeCtrl滚动条源码。#define SCROLLBAR_NORMAL_MINIMUM 45 //经验值,当Scrollbar宽度/高度小于这一数值时需要特殊处理,保证与系统的处理方式一致。编译环境:VS2012,VC6.0不能编译。 ...
  • 重绘ListBox.rar

    2021-06-17 09:16:18
    重绘ListBox.rar
  • MFC ComboxBox重绘

    2019-04-22 16:25:59
    MFC,ComboxBox,重绘代码类 MFC,ComboxBox,重重绘代码
  • c#窗体美化,窗体重绘

    2017-10-31 10:08:44
    使用c#语言开发,重新绘制窗体,窗体圆角,此代码仅供参考
  • C#重绘ComboBox.rar

    2020-07-15 13:28:44
    美化ComboBox ,用户自定义控件的完整代码,帮助学习,值得拥有,美化ComboBox ,用户自定义控件的完整代码,帮助学习,值得拥有,美化ComboBox ,用户自定义控件的完整代码,帮助学习,值得拥有
  • 重绘 C# button按钮

    2014-11-27 12:12:53
    看这了代码后,在C# winform下,你想要什么样的按纽都可以用代码画出来,这样的重绘方法资率比较高,可以放心使用
  • 一个供演示用的C#画图程序源码,支持撤销、重绘功能,可了解一些C#的基本绘图知识,代码完整,其中的撤销重做思路非常有帮助。除此之外,程序还可以设置画笔颜色、填充颜色、背景颜色、选择图案开关以及线条粗细等,...
  • 摘要:VB源码,界面编程,ListView,SkinH_VB6.dll ListView重绘,请把 View 设置为 Report 样式,FullSelect设置为 True ,以获得最佳效果。仅仅为了公司要求所编写,并不适合所有情况。  其中一个使用了SkinH_VB6....
  • C#ListView自定义控件(重绘代码+说明文档 C#中的listView很不强大
  • MFC各种重绘漂亮按钮

    2014-03-05 10:19:28
    这里面包含几种有特色的重绘按钮,代码注释清晰容易上手。
  • css重绘与重排的方法

    2020-09-27 21:20:59
    主要介绍了css重绘与重排的方法的相关资料,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • Quartz 2D重绘帧示例代码
  • c#重绘圆角button代码 可更改圆角半径,最大可形成半圆型,适当改变宽和高可形成圆形
  • QT 控件重绘

    千次阅读 2020-10-28 22:11:52
    重绘控件感觉一般在VS里面开发的时候,使用重绘比较方便,相对于关联控件设置来说,直接代码重绘要比关联控件要便捷,如果使用QT Creator来进行开发的话个人感觉还是拖控件再用样式表较为方便。 //.h 源文件 //...

    前言:

    转载请附上连接,本帖原创请勿照抄。

          QT重绘控件是指通过实现控件头文件,使用QSS或者样式表来对某个控件进行重新绘制。

          1、重绘QButton按钮;2、重绘QComboBox下拉框;3、其它控件重绘的办法。

    1. 重绘QButton

          重绘控件感觉一般在VS里面开发的时候,使用重绘比较方便,相对于关联控件设置来说,直接代码重绘要比关联控件要便捷,如果使用QT Creator来进行开发的话个人感觉还是拖控件再用样式表较为方便。

    //.h 源文件
    //引入按钮抬头
    #include<QPushButton>
        //.cpp实现文件
    
        QPushButton *button1= new QPushButton(this);
        QPushButton *button2= new QPushButton(this);
        QPushButton *button3= new QPushButton(this);
        //new完记得删除指针
    
        //背景
        QPalette pal;
        pal.setColor(QPalette::ButtonText, QColor(255, 255, 255));
        
        //定义按钮大小背景
        //setStyleSheet注释因为在样式表写在QSS里面
        button1->setText("按钮1");
        button1->setGeometry(40,120,93,28);//93 28
        button1->setObjectName("btn1");
        //button1->setStyleSheet("background-color:rgb(17,17,17);");
        button1->setPalette(pal);
    
        button2->setText("按钮2");
        button2->setGeometry(140,120,120,28);
        button2->setObjectName("btn2");
        //button2->setStyleSheet("font-size:200px;");
        button2->setPalette(pal);
    
        button3->setText("按钮3");
        button3->setGeometry(340,120,120,28);
        button3->setObjectName("btn3");
        //button3->setStyleSheet("font-size:200px;");
        button3->setPalette(pal);
    
        //显示按钮
        button1->show();
        button2->show();
        button3->show();
        
        //关联槽函数
        connect(button1, SIGNAL(clicked()), this, SLOT(chooseBtn()));
        connect(button2, SIGNAL(clicked()), this, SLOT(chooseBtn()));
        connect(button3, SIGNAL(clicked()), this, SLOT(chooseBtn()));
    //QSS样式表  myqss.qss
    QPushButton{
            background:qlineargradient(spread:pad, x1:0.460227, y1:0, x2:0.465818, y2:0.875, stop:0 rgba(170, 255, 255, 255), stop:1 rgba(255, 255, 255, 255));
            border-radius:8px;
            border:2px solid rgb(0, 170, 255);
    		color:rgb(17,17,17);
    }
    QPushButton:hover{
            background:qlineargradient(spread:pad, x1:0.460227, y1:0, x2:0.443091, y2:0.898, stop:0 rgba(0, 255, 255, 255), stop:1 rgba(255, 255, 255, 255))
    }
     
    QPushButton:hover:pressed{
            background:qlineargradient(spread:pad, x1:0.460227, y1:0, x2:0.465818, y2:0.875, stop:0 rgba(170, 255, 255, 255), stop:1 rgba(255, 255, 255, 255));
    }
    #btn2{
            background:qlineargradient(
    		spread:pad, 
    		x1:0, 
    		y1:0, 
    		x2:1, 
    		y2:1, 
    		stop:0 rgba(211 ,239 ,250 ,255 ), 
    		stop:0.12 rgba(208 ,241 ,250 ,255 ),
    		stop:0.21 rgba(215 ,227 ,243 ,255 ), 
    		stop:0.3 rgba(214 ,233 ,247 ,255 ),
    		stop:0.364 rgba(218 ,215 ,236 ,255 ),
    		stop:0.459 rgba(220 ,200 ,229 ,255 ),
    		stop:0.7 rgba(227 ,194 ,225 ,255 ),
    		stop:0.9 rgba(233 ,166 ,209 ,255 ),
    		stop:0.92 rgba(236 ,159 ,189 ,255 ),
    		stop:1 rgba(237 ,153 ,202 , 255));
            border-radius:8px;
            border:2px solid rgb(0, 170, 255);
    		color:red;
    }
    #btn3{
            background:qlineargradient(spread:pad, x1:0.460227, y1:0, x2:0.465818, y2:0.875, stop:0 rgba(255, 69, 0, 255), stop:1 rgba(255, 255, 255, 255));
            border-radius:8px;
            border:2px solid rgb(255, 127, 80)
    }

    QSS样式表引入可以借鉴

    https://blog.csdn.net/qq_37529913/article/details/109288422

    2、重绘QComboBox下拉框

          部分定制样式可以借鉴该博客:https://www.cnblogs.com/bclshuai/p/9809679.html

          其实大部分样式也可以借鉴CSS样式来做大部分样式是和CSS通用的,可以进行尝试。

    //上面的下拉框控件重绘过后的
    //下面的下拉框控件是默认的
    //.h源文件
    #include<QComboBox>
        //.cpp实现文件
        //可编辑模式
        ui->comboBox->setEditable(true);
        ui->comboBox_2->setEditable(true);
    
        /*
         * #comboBox
         * combobox-popup                   Value(0、1)两个下拉框样式,默认0
         * border:2px solid #2080F7;        Value 边框圆滑程度(不设置颜色边框默认白色)
         *
         * #comboBox:hover
         * background-color                 触发背景
         * color                            字体
         * border comboBox边框
         *
         * #comboBox::down-arrow            下拉框按钮图标
         * #comboBox::down-arrow:on         下拉框按钮图标
         * #comboBox::drop-down             下拉框按钮图标设置
         *
         * #comboBox
         * QAbstractItemView::item          下拉列表样式
         * QAbstractItemView::item:hover    下拉列表样式
         *
         * #comboBox
         * QAbstractScrollArea QScrollBar::vertical     垂直滚动条
         *
         * #comboBox
         * QScrollBar::handle::vertical     垂直滚动条滑块属性设置
        */
        QString style =
                 "#comboBox {combobox-popup: 0;  border:2px solid #2080F7;}"
                 "#comboBox:hover{background-color:white;color: black;border:1px;border-color:rgb(2,141,193);border-style:solid;}"
                 "#comboBox::down-arrow{width: 16px;height: 16px;image: url(:./01.png);}"
                 "#comboBox::down-arrow:on{image: url(:./02.png);}"
                 "#comboBox::drop-down{subcontrol-origin: padding;subcontrol-position: right;width: 20px;border: 0px;}"
                 "#comboBox QAbstractItemView::item{height: 30px;min-height:30px;color:rgb(0,0,0);background-color:white;}"
                 "#comboBox QAbstractItemView::item:hover{color:rgb(0,0,0);background-color:rgb(2,141,193);}"
                 "#comboBox QAbstractScrollArea QScrollBar::vertical{background-color:white;width: 12px;}"
                 "#comboBox QScrollBar::handle::vertical {width: 12px;background:rgb(112,112,112);}"
                    ;
    
         ui->comboBox->setStyleSheet(style);
         QStringList list;
         list << "test1" << "test2" << "test3" << "test4" << "test5"<< "test6"<< "test7" << "test8"<< "test9"<< "test10"<< "test11"<< "test12";
         ui->comboBox->addItems(list);
         ui->comboBox_2->addItems(list);
    
        ui->comboBox->setView(new QListView());
        ui->comboBox->setMaxVisibleItems(10);

    下拉框样式和控制:https://blog.csdn.net/imred/article/details/78158238

    CSS样式W3C:https://www.w3school.com.cn/cssref/pr_border-style.asp

    滚动条垂直水平和样式控制:http://www.voidcn.com/article/p-wgnigvhf-brr.html

    全面的QSS样式:https://www.cnblogs.com/bclshuai/p/9809679.html

    3、其它控件重绘的办法。

          其实控件重绘的办法都是类似的,重绘定制每个空间的样式都需要按照每个空间的方法来通过样式表来进行重绘定制某个部分。

          接下来会发布正则验证、自绘图形、C\S架构的服务器和客户端

     

    展开全文
  • 一般在处理图片上传时,通常的逻辑都... //以下为源代码 代码如下: function drawCanvasImage(obj,width, callback){ var $canvas = $(‘<canvas></canvas>’), canvas = $canvas[0], context = canvas.getContext(‘2
  • MFC 控件重绘

    千次阅读 2021-03-24 14:10:50
    1. MFC 控件的重绘原理 MFC 的基础控件有很多,常见的就是按钮、列表、标签、输入框等,通常一个应用程序的界面都是由这些小小的控件组合在一起形成了最终的用户界面。系统为每一个基础控件都绘制了一个默认的外观...

    1. MFC 控件的重绘原理

    MFC 的基础控件有很多,常见的就是按钮、列表、标签、输入框等,通常一个应用程序的界面都是由这些小小的控件组合在一起形成了最终的用户界面。系统为每一个基础控件都绘制了一个默认的外观,让我们看个例子:

    基础登录框

    大家可以看到,这是一个基础的 MFC 的对话框,上面有 2 个按钮,2 个标签,2 个输入框,也就组合成了大家熟悉的登录界面。这个界面确实非常"朴素",可能不太符合当下的审美需求,那如果要美化这个界面,要怎么做呢?通常有两种做法:

    第一种,找美工设计界面,然后给你切图,直接贴图到程序界面上,就是做网站一样,这种做法的好处是程序实现上相对容易,但是美工的工作量大,比如一个按钮就可能有 4 种状态,如果是 10 个不同的按钮,那就是 40 张图,如果按钮大小还不一样,那又会针对各个尺寸出图,灵活性不够,而且最后的程序资源会非常大。

    第二种,也就是我们今天讲的重绘控件,相当于你不用系统默认的控件外观了,而是自己实现控件的外观绘制,定制出符合自己需求的外观,这种方法的优点是灵活,资源占用小,能自适应控件尺寸,方便进行换肤。缺点是程序员需要深入理解控件重绘的过程,以及掌握 GDI,GDI+的绘制函数,才能实现相关效果。

    OK,说了这么多,让我们先来了解下 MFC 都是在什么地方绘制控件的外观的。

    首先我们需要知道几个重要的跟绘制相关的 WINDOWS 消息和对应的消息处理函数 WMPAINT ==> OnPaint WMNCPAINT ==> OnNcPaint WMDRAWITEM ==> OnDrawItem WMERASEBKGND ==> OnEraseBackground WM_CTRCOLOR ==> OnCtrlColor

    MFC 大部分控件都继承自 CWnd 类,当一个 Windows 窗口需要重新绘制的时候,系统就会向该窗口投递 WMPAINT 消息,窗口通过 WMPAINT 的消息响应函数 OnPaint 实现客户区域绘制过程。

    对于窗口的非客户区域,比如窗口的标题栏的绘制,就不是通过 WMPAINT 消息,而是响应 WMNCPAINT 消息,在 OnNcPaint 里面进行绘制的。

    那么 WMDRAWITEM 又是什么消息呢?当一个控件具有 OWNER DRAW 的属性并且需要重绘的时候,那么其父窗口就会响应 WMDRAWITEM 消息,调用 OnDrawItem 函数,在这个函数里面调用子控件的 DrawItem 函数,完成子控件的重绘。

    接下来看:WMERASEBKGND,这个消息是用来擦除背景的,就好比你在一个画板上画画的时候,你先得把已经画好得内容擦掉,换一个干净的背景。在响应 OnPaint 的时候,都会先触发 WMERASEBKGND,我们可以在其响应函数 OnEraseBackground 里面准备好我们需要的背景。

    WM_CTRCOLOR 是子控件将要绘制的时候,向父窗口发送的消息,父窗口可以在这个消息里面设置子控件 DC,前景色、背景色、画刷等等,也可以把这个消息反射给子控件自己处理。

    让我们用一张图总结一下:重绘过程图理解了上面的窗口和界面的绘制过程和消息以后,我们就找到了重绘控件的方法。 对于支持 OWNERDRAW 属性的控件,我们一般会通过重载控件的 DrawItem 函数来进行控件外观的重新绘制。 为什么不在 OnPaint 里面重绘控件呢?其实也可以,但是如果通过 OnPaint 来重绘的话,你就得从 0 开始,绘制要复杂很多。

    接下来我们着重讲的就是支持 OWNERDRAW 属性的控件重绘。

    2. 常见控件的重绘制示例

    • CButton 按钮控件应该是应用程序界面中必备的控件之一,也是最简单的控件,它支持 OWNERDRAW 属性,那接来下我们就来重绘一个按钮,看代码:
    class CCustomDrawBtn : public CButton
    {
        DECLARE_DYNAMIC(CCustomDrawBtn)
    
    public:
        CCustomDrawBtn();
        virtual ~CCustomDrawBtn();
        virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
        virtual void PreSubclassWindow();
        afx_msg void OnMouseMove(UINT nFlags, CPoint point);
        afx_msg void OnTimer(UINT nIDEvent);
        ...
        省略非关键代码
    };
    

    第一步我们自绘的按钮类继承 CButton,因为我们只是重绘外观,对按钮控件 行为还是使用默认行为,重点是重载函数是:

    virtual void PreSubclassWindow();
    virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
    

    PreSubclassWindow 是窗口的子类化,可以在这里修改窗口的属性,我们看下具体实现。

    void CCustomDrawBtn::PreSubclassWindow()
    {
    
        ModifyStyle(0, BS_OWNERDRAW); // 修改按钮风格,支持 OWNERDRAW
        CButton::PreSubclassWindow();
    }
    

    目的就是修改按钮的默认风格,支持 OWNERDRAW,因为系统默认按钮控件是没有这个属性的。 DrawItem 这个是核心的重绘制函数,我们前面讲了子控件就是通过这个函数来绘制自身的外观,因此我们就在这个函数里面,把按钮绘制成我们想要的样子。

    void CCustomDrawBtn::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
    {
            CRect rect,rectFocus;
            GetClientRect(&rect);
            //焦点框
            rectFocus.CopyRect(&rect);
            rectFocus.DeflateRect(4,4,4,4);
    
            COLORREF textColor = m_textColor;
            COLORREF borderColor = m_borderColor;
            //禁用状态绘制
            if(lpDrSt->itemState & ODS_DISABLED)
            {
                bkColor = SKinColors::lightgray;
                textColor = SKinColors::dark_gray;
                borderColor = SKinColors::dark_gray;
            }
            CPen pen(PS_SOLID,1,borderColor);//边框颜色
            CPen* pOldPen = pDC->SelectObject(&pen);
            CFont font;
            font.CreateFont(m_fontSize,0,0,0,700,FALSE,FALSE,FALSE,DEFAULT_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,DEFAULT_PITCH,"宋体");
            CFont* pOldFont = pDC->SelectObject(&font);
            // 填充颜色
            CBrush brush;
    
            brush.CreateSolidBrush(bkColor);
            pDC->SelectObject(&brush);
            pDC->RoundRect(0,0,rect.right,rect.bottom,roundRadis,roundRadis);
            pDC->SelectObject(pOldPen);
            //绘制按钮文本
            //定义一个 CRect 用于绘制文本 
            CRect textRect;
            //拷贝矩形区域 
            textRect.CopyRect(&m_rcDrawClient);
            //获得字符串尺寸
            CString btnCaption = m_btnText;
            CSize sz = pDC->GetTextExtent(btnCaption);
            //调整文本位置 居中 
            textRect.top += (textRect.Height()- sz.cy)/2;
            //设置文本背景透明 
            pDC->SetBkMode(TRANSPARENT);
            //设置文本颜色
            pDC->SetTextColor(textColor); 
            //绘制文本内容
            pDC->DrawText(btnCaption,&textRect,DT_RIGHT|DT_CENTER|DT_BOTTOM);
    
            if (lpDrSt->itemState & ODS_FOCUS)
            {
                ...焦点状态下的外观绘制
            }
            ...省略其他代码
    }
    

    效果图:

    按钮效果

    上面就是绘制的核心代码了,涉及了很多 GDI 函数和 Windows 系统函数,希望不熟悉的自己查一下,文章的最后我会给出 GitHub 地址,大家可以去看下完整的 demo 代码。

    • CEdit 编辑控件也是非常常用的界面控件,用于接收用户的输入,通常我们可以通过重绘改变其背景色,字体大小,边框等,难点是它不支持 OWNER DRAW 属性,所以这里介绍一种方式是通过 CStatic 控件和 CEdit 控件的组合来实现效果,比如圆角边框,看下例子:
    class CCEditEx : public CStatic
    {
        DECLARE_DYNAMIC(CCEditEx)
    
    public:
        CCEditEx();
        virtual ~CCEditEx();
        ...
    
    protected:
        DECLARE_MESSAGE_MAP()
        virtual void PreSubclassWindow();
        virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
        afx_msg BOOL OnEraseBkgnd(CDC* pDC);
        afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
        void DrawBorder(CDC* dc, CRect &rc);
    private:
        CEdit m_edit;
    };
    

    可以看到我们的重绘类 CEditEx 是继承 CStatic,然后其包含了一个成员:m_edit 绘制代码

    void CCEditEx::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
    {
    
        CRect rc;
        rc = lpDrawItemStruct->rcItem;
    
        CDC dc;
        dc.Attach(lpDrawItemStruct->hDC);
        dc.SelectObject(&m_bkBrush);
        //绘制边框
        DrawBorder(&dc,rc);
    
        rc.DeflateRect(m_padding, m_padding);
    
        if (m_edit.GetSafeHwnd() && !m_bDisable) 
        {
            m_edit.ShowWindow(SW_SHOW);
            m_edit.SetBkColor(m_bkColor);
            m_edit.SetWindowText(m_strText);
        }
        else if(m_edit.GetSafeHwnd() && m_bDisable)
        {
            m_edit.EnableWindow(FALSE);
            m_edit.SetBkColor(m_bkColor);
            m_edit.SetWindowText(m_strText);
        }
        else 
        {
            //首次创建子控件
            GetClientRect(&rc);
            rc.DeflateRect(10,10);
            m_edit.Create(WS_CHILD | WS_VISIBLE | ES_LEFT | ES_AUTOHSCROLL, rc, this, 1);
            m_edit.SetFont(GetFont());
            if(m_bPwdInput){
                m_edit.SetPasswordChar('*');
            }
            m_edit.SetWindowText(m_strText);
            m_edit.SetBkColor(m_bkColor);
            m_edit.ShowWindow(SW_SHOW);
            if(m_bDisable)
            {
                m_edit.EnableWindow(FALSE);
            }
    
        }
        ReleaseDC(&dc);
    }
    

    效果图:

    输入框效果

    这里其实可以看到 medit.Create(WSCHILD | WSVISIBLE | ESLEFT | ES_AUTOHSCROLL, rc, this, 1); edit 作为其子控件创建出来,CStatic 主要绘制边框,完整代码请看最后的 GitHub 地址。

    • CComboBox 组合框控件,这个控件的绘制要复杂一些,分为两个部分,一是文字输入下拉框部分,一个外面的边框和三角箭头按钮。
    class CCustomDrawCombox : public CComboBox
    {
        DECLARE_DYNAMIC(CCustomDrawCombox)
        afx_msg void OnPaint();
    
    public:
        CCustomDrawCombox();
        virtual ~CCustomDrawCombox();
    private:
        virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
        virtual void MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct);
        virtual int CompareItem(LPCOMPAREITEMSTRUCT lpCompareItemStruct);
    private:
        virtual void PreSubclassWindow();
        ...
    };
    

    核心有两个一个是 DrawItem 里面绘制下拉框样式,一个是在 OnPaint 里面绘制 外层部分。

    void CCustomDrawCombox::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
    {
        CString strDrawText;
        if(!lpDrawItemStruct->itemID!=-1)
        {
            GetLBText(lpDrawItemStruct->itemID,strDrawText);
        }
        else
        {
            strDrawText = "请选择";
        }
        TEXTMETRIC TextMetr;
        CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
        if(pDC)
        {
            pDC->GetTextMetrics(&TextMetr);
            CRect rcClient;
            GetClientRect(&rcClient);
    
            if((lpDrawItemStruct->itemState & ODS_SELECTED)&&
                (lpDrawItemStruct->itemAction&(ODA_DRAWENTIRE|ODA_SELECT)))
            {
                pDC->FillSolidRect(&lpDrawItemStruct->rcItem,RGB(101,160,251));
                pDC->SetBkColor(::GetSysColor(COLOR_HIGHLIGHT));
                pDC->SetTextColor(RGB(255,255,255));
                pDC->TextOut(lpDrawItemStruct->rcItem.left,lpDrawItemStruct->rcItem.top,strDrawText);
            }
            else if (lpDrawItemStruct->itemAction&(ODA_SELECT|ODA_DRAWENTIRE))
            {
                pDC->FillSolidRect(&lpDrawItemStruct->rcItem,RGB(255,255,255));
                pDC->SetBkColor(::GetSysColor(COLOR_WINDOW));
                pDC->SetTextColor(::GetSysColor(COLOR_WINDOWTEXT));
                pDC->TextOut(lpDrawItemStruct->rcItem.left,lpDrawItemStruct->rcItem.top,strDrawText);
            }
            ReleaseDC(pDC);
        }
    
    }
    

    下面是画外层的效果

    void CCustomDrawCombox::OnPaint()
    {
        CPaintDC dc(this); // device context for painting
        //绘制客户区
        CDC dMemDC;
        dMemDC.CreateCompatibleDC(pDC);
        dMemDC.SetMapMode(pDC->GetMapMode());
    
        //画动作
        CBitmap mNewBmp;
        CRect rc;
        GetClientRect(&rc);
    
        mNewBmp.CreateCompatibleBitmap(pDC, rc.right - rc.left, rc.bottom - rc.top);
        CBitmap* pOldBmp = dMemDC.SelectObject(&mNewBmp);
        CPen pen(PS_SOLID,1,RGB(200,200,200));
        CPen *pOldPen = dMemDC.SelectObject(&pen);
        CBrush bkBrush;
        bkBrush.CreateSolidBrush(RGB(255,255,255));
        dMemDC.SelectObject(&bkBrush);
        CPoint pt(10,10);
        dMemDC.Rectangle(rc);//画整个客户区域
    
        CRect rcEnd(rc);//按钮区域
        rcEnd.left = rc.right - 20;
        //画右边的三角形按钮
        CBrush bkBrushRect;
        bkBrushRect.CreateSolidBrush(RGB(21,123,237));
        dMemDC.SelectObject(&bkBrushRect);
        dMemDC.Rectangle(rcEnd);
        //画三角形
        CRgn rgn;
        CPoint ptAngle[3];
        int angleSideWidth = 8;//三角形边长
        //第一个点的坐标
        ptAngle[0].x = rcEnd.left+rcEnd.Width()/2-angleSideWidth/2;
        ptAngle[0].y = rcEnd.top+rcEnd.Height()/2-2;
        //第二个点的坐标
        ptAngle[1].x = ptAngle[0].x + angleSideWidth;
        ptAngle[1].y = ptAngle[0].y;
        //第三个点的坐标
        ptAngle[2].x = rcEnd.left+rcEnd.Width()/2;
        ptAngle[2].y = ptAngle[0].y + 5;
        CBrush brushAngle;
        rgn.CreatePolygonRgn(ptAngle, 3, ALTERNATE); //创建区域  
        brushAngle.CreateSolidBrush( RGB(255,255,255) ); //创建画刷
        dMemDC.FillRgn( &rgn, &brushAngle ); //填充区域
        //dMemDC.DrawFrameControl( &rcEnd,DFC_SCROLL,DFCS_SCROLLDOWN|DFCS_FLAT|DFCS_MONO );
        pDC->BitBlt(rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, &dMemDC,
            rc.left ,rc.top, SRCCOPY);
        //恢复
        dMemDC.SelectObject(pOldBmp);
        dMemDC.SelectObject(pOldPen);
        pOldPen->DeleteObject();
        pOldBmp->DeleteObject();
        dMemDC.DeleteDC();
        bkBrush.DeleteObject();
        bkBrushRect.DeleteObject();
        brushAngle.DeleteObject();
    }
    

    效果图:自绘 combbox

    • CListCtrl 最后说一下列表控件,这个控件的绘制应该说是几个里面最复杂的一个,包含两个部分,一个是 header 的绘制,一个是每一个列表项的绘制,当然原理都是一样,我的 demo 里面有完整例子,大家可以自己去下载来看一下,这里就不再贴代码了。

    3. MFC 通用皮肤库的实现思路

    经过上面的讲解,相信大家对简单的 MFC 控件的自绘制有了一个初步的认识,通过上面的方法我们确实可以自绘控件,但是大家可能都接触过有一类皮肤库,它们可以在修改少量代码的情况下,就可以把你应用程序里面所有的控件都换成自定义的皮肤,这是怎么做的呢?

    这类皮肤库通常是通过 WIN32 的消息函数勾子,底层截获了应用程序的相关 WIN32 绘制消息,并进行过滤和处理,需要对整个 WIN32 程序的运行过程,WINDOWNS 消息机制有深入理解后,方能实现,感兴趣的朋友可以自行查资料做深入研究。

    最后本 Chat 相关的 GitHubi 地址: https://gitee.com/xmsharp/MFCSkin.git 由于本人水平有限,只能抛砖引玉,希望大家有所收获。

     

    展开全文
  • Delphi双缓存Listview图标自绘代码,一般listview都是以vsreport类型,进行一行行数据展示,这个例子是以vsicon类型进行展示,所有数据全部在AdvancedCustomDrawItem 事件中进行自。涉及到的技术点主要有以下三个...
  • MFC CButton重绘

    2016-01-04 15:45:04
    VC/C++,MFC开发下的一个按钮重绘源码,背景贴图,文字重绘,或者图片和文字同时显示等,代码简单明了。
  • C# winforms ComboBox 重绘

    2018-05-26 01:21:28
    C# winforms ComboBox 重绘 相关的几个代码,来自codeproject
  • VC++MFC重绘控件.zip

    2020-05-12 14:45:30
    基于VC++编写的几个MFC基础控件重绘代码,包含CButton、CCombox、CListCtrl、CEdit、CStatic等基础控件,可扩展性强,适合初学者进行学习和参考
  • C#重绘ListBox控件

    2021-03-16 01:12:02
    摘要:C#源码,控件类库,ListBox 使用C#语言重绘ListBox控件,只是简单的重绘,有兴趣的可下载本源码学习研究吧。
  • 回流和重绘

    千次阅读 2022-02-08 16:23:39
    你真的了解回流和重绘吗 - SegmentFault 思否 回流与重绘 - 掘金 (juejin.cn) 面试官:怎么理解回流跟重绘?什么场景下会触发? | web前端面试 - 面试官系列 (vue3js.cn) 浏览器渲染过程 渲染页面:浏览器的工作...

    资料

    你真的了解回流和重绘吗 - SegmentFault 思否

    回流与重绘 - 掘金 (juejin.cn)

    面试官:怎么理解回流跟重绘?什么场景下会触发? | web前端面试 - 面试官系列 (vue3js.cn)

    浏览器渲染过程

    渲染页面:浏览器的工作原理 - Web 性能 | MDN (mozilla.org)

    image-20220208145056464

    1. 解析HTML,形成DOM树,当遇到非阻塞资源时,如图片则会继续解析,当遇到css文件时,也会继续解析。当时遇到了js文件(特别是没有defer和async)的阻塞渲染并停止HTML的解析。
    2. 构建CSSOM树。浏览器将CSS规则转换为可以理解和使用的样式映射。浏览器遍历CSS中的每个规则集,根据CSS选择器创建具有父、子和兄弟关系的节点树。
    3. 第三步是将DOM和CSSOM组合成一个Render树,计算样式树或渲染树从DOM树的根开始构建,遍历每个可见节点。像和它的子节点以及任何具有display: none样式的结点,例如script { display: none; }(在user agent stylesheets可以看到这个样式)这些标签将不会显示,也就是它们不会出现在Render树上。具有visibility: hidden的节点会出现在Render树上,因为它们会占用空间。由于我们没有给出任何指令来覆盖用户代理默认值,因此上面代码示例中的script节点将不会包含在Render树中。
    4. 第四步是在渲染树上运行布局以计算每个节点的几何体。(回流\重排)。在此阶段,考虑到视区大小,浏览器将确定屏幕上所有不同框的尺寸。以视区的大小为基础,布局通常从body开始,用每个元素的框模型属性排列所有body的子孙元素的尺寸,为不知道其尺寸的替换元素(例如图像)提供占位符空间。
    5. 最后一步是将各个节点绘制到屏幕上。

    回流一定会触发重绘,重绘不一定会触发回流。

    生成渲染树

    image-20220208150955390

    1. 从DOM树的根开始构建,遍历每个可见节点
    2. 对于每一个可见节点,找对应的CSSOM规则。
    3. 根据每一个可见节点和它们对应的CSSOM规则,结合形成Render Tree.

    不可见节点

    1. Head标签下的所以子节点(meta、link)。
    2. script。
    3. display : none 的元素
    4. 利用visibility和opacity隐藏的节点,还是会显示在渲染树上的。

    回流

    以视区的大小为基础,布局通常从body开始,用每个元素的框模型属性排列所有body的子孙元素的尺寸,为不知道其尺寸的替换元素(例如图像)提供占位符空间。

    总的来说就是计算它们在设备视口(viewport)内的确切位置和大小(也就是几何属性和位置),这个计算的阶段就是回流。

    重绘

    我们就可以将渲染树的每个节点都转换为屏幕上的实际像素,这个阶段就叫做重绘节点。也就是在屏幕上绘制图形,但是如背景颜色发生改变,那么会引起重绘,但是不会引发回流。

    触发回流重绘的操作

    • 添加或删除可见的DOM元素
    • 元素的位置发生变化
    • 元素的尺寸发生变化(包括外边距、内边框、边框大小、高度和宽度等)
    • 内容发生变化,比如文本变化或图片被另一个不同尺寸的图片所替代。
    • 页面一开始渲染的时候(这肯定避免不了)
    • 浏览器的窗口尺寸变化(因为回流是根据视口的大小来计算元素的位置和大小的)

    浏览器的优化机制

    image-20220208161045011

    减少回流和重绘的方法

    1. 把多个操作DOM操作合并在一起—使用csstext或者class

      image-20220208161212390

    2. 使得节点脱离文档流(也就是不可见的节点),操作,回到文档流。

      1. 使用display: none
    3. 必须触发布局事件

      也就是offset这些。若多次使用到同一个offset,那么可以先把它的值保存起来再使用。

    4. 对于复杂的动画可以使用绝对定位使其脱离文档流。对于复杂动画效果,由于会经常的引起回流重绘,因此,我们可以使用绝对定位,让它脱离文档流。否则会引起父元素以及后续元素频繁的回流。

    展开全文
  • C# TreeListView重绘

    2014-04-29 17:06:08
    继承Control重绘了一个treeListView,主要代码做了注视

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 98,302
精华内容 39,320
关键字:

代码重绘