QT 边框旋转

。。。先上一张效果图,感兴趣的就耐心往下看^_^

 

设计灵感参考了这篇文章。之前看了这篇文章,因为作者并未告诉实现代码,不得已,只能自己想办法搞一个。

之前以为直接用QConicalGradient就能实现效果了,实际操作之后,发现

如果控件的宽高相等(正方形),则只会在控件的拐角处,感觉特效的那个线段变长了,但如果,控件宽高差异很大时,离中心点越远的地方,效果越尴尬o(╯□╰)o

QConicalGradient是绕中心点旋转,最完美的效果应该是使用于圆形控件

要让边框动画看起来自然,需要解决的问题是能绘制出边框轮廓(提取轮廓好像QPainterPathStroker可以用),然后取其中一小段,绘制出来,本文就是这样的实现思路

直角的好实现,圆角的地方就稍微画个图顺一下就OK

废话不多说,代码就是我的思路,上代码

我也先贴个头

#ifndef BORDERWIDGET_H
#define BORDERWIDGET_H

#include <QWidget>

class QPropertyAnimation;
class QColor;
class BorderWidget :public QWidget
{
    Q_OBJECT
    Q_PROPERTY(qreal border_len READ BorderLength WRITE SetBorderLength)
public:
    explicit BorderWidget(QWidget *parent= nullptr);
    //特效占比
    void SetEffectPercent(qreal percent){border_effect_percent_= percent;}
    //边框颜色
    void SetBorderColor(QColor cor){border_color_ = cor;}
    //特效颜色
    void SetEffectColor(QColor cor){border_effect_color_= cor;}
    //边框宽度
    void SetBorderWidth(int width){border_width_=width;}
    //圆角半径
    void SetBorderRadius (int radius){border_round_radius_ =radius;}
    //特效使能
    void SetEffectWork(bool effectwork);
    //控件
    void SetWidget(QWidget*);

signals:
public slots:
protected:
    virtual void paintEvent(QPaintEvent *event);
    qreal BorderLength() {return border_len_;}
    void SetBorderLength(double v);
private:
    QWidget*                wdg_;                   //需要加特效的控件
    qreal                   border_len_;
    QPropertyAnimation*     animation_;
    qreal                   border_effect_percent_; //特效占比(0~1) 默认值0.08
    QColor                  border_color_;          //边框颜色 默认绿色
    QColor                  border_effect_color_;   //边框特效颜色 默认红色
    int                     border_width_;          //边框宽度 默认4
    int                     border_round_radius_/*[4]*/;
                                                    //边框圆角半径 0时为直角 默认4
    bool                    effect_work_;           //默认true
};


#endif // BORDERWIDGET_H

不过,我还能贴个尾^_^

#include <QPainter>
#include <QDebug>
#include <QPropertyAnimation>

#ifndef MY_DBLEPSILON
#define MY_DBLEPSILON 1e-13
#endif

#ifndef MY_FLTEPSILON
#define MY_FLTEPSILON 1e-6
#endif

#ifndef PI
#define PI 3.14159265
#endif
//等于0.
bool DoubleIsZERO(double v)
{
    if(v<0.)v*=-1.;
    return v<=MY_DBLEPSILON;
}

bool DoubleLess(double d1, double d2)
{return d1-d2<0;}

bool DoubleLessEqual(double d1, double d2)
{
    d1-=d2;
    if(d1<0.) return true;
    return DoubleIsZERO(d1);
}

QRect DrawRectOnGeometry(QRect geometry)
{
    int off = 1;
    geometry.adjust(off, off, -off,-off);
    return geometry;
}

QRect EffectRectOnGeometry(QRect geometry, int border_width)
{
    border_width =border_width/2;
    geometry.adjust(border_width, border_width, -border_width,-border_width);
    return geometry;
}

//用正弦获取角度
qreal DegreeOnSinX(qreal sinx)
{
    if(DoubleLessEqual(1.,sinx))return 90.;//浮点运算出的结果有时距离/圆角半径的结果>1.0
    return asin(sinx)*180./PI;
}

//从矩形中获取圆角矩形的路径
QPainterPath RoundPathOnRect(QRectF wdgrect, int border_round_radius)
{
    qreal l= wdgrect.left(), t=wdgrect.top(), r=wdgrect.right(), b=wdgrect.bottom();
    qreal arc_w=2*border_round_radius;
    QPainterPath paintpath;
    paintpath.moveTo(l+border_round_radius, t);
    paintpath.lineTo(r - border_round_radius, t);
    paintpath.arcTo(r-arc_w, t, arc_w, arc_w,90,-90.);
    paintpath.lineTo(r, b-border_round_radius);
    paintpath.arcTo(r-arc_w, b-arc_w, arc_w,arc_w, 0.,-90.);
    paintpath.lineTo(l+border_round_radius, b);
    paintpath.arcTo(l, b-arc_w,arc_w, arc_w,270.,-90.);
    paintpath.lineTo(l, t+border_round_radius);
    paintpath.arcTo(l, t, arc_w, arc_w,180.,-90.);
    return paintpath;
}

//边界(圆角)
QPainterPath BorderEffectRoundRectPath(QRectF srcrect,
                                       qreal start_percent,
                                       qreal stop_percent,
                                       int border_radius)
{
    QPainterPath srcpath = RoundPathOnRect(srcrect, border_radius);
    QPointF pt1 = srcpath.pointAtPercent(start_percent),pt2 =srcpath.pointAtPercent(stop_percent);
    QPainterPath painterpath;
    qreal l = srcrect.left(), t = srcrect.top(), r = srcrect.right(), b = srcrect.bottom();
    qreal x1 = pt1.x(), y1 = pt1.y(), x2 = pt2.x(), y2 = pt2.y(), border_radius_=border_radius;
    if(!DoubleIsZERO(x1-x2) &&
       !DoubleIsZERO(y1-y2))
    {
        qreal start_deg= 0., end_deg =0.;
        qreal pos_judge = 0., arc_width = 2.*border_radius_;
        if(x1 < x2 &&
           y1 < y2)
        {//右上角
            pos_judge = r - border_radius_;
            painterpath.moveTo(pt1);
            if(DoubleLessEqual(x1, pos_judge))
            {
                //起点在弧形左边
                painterpath.lineTo(pos_judge,y1);
                //终点在弧形上或弧形外都画弧
                if(DoubleLess(pos_judge, x2))//终点在弧形上
                {
                    end_deg = DegreeOnSinX((x2-pos_judge)/border_radius_);
                    painterpath.arcTo(r-arc_width, t, arc_width, arc_width, 90., -end_deg);
                }
            }
            else//if(DoubleLess(pos_judge,x1)
            {
                //起点在弧形上
                //终点在弧形上或外都画弧
                start_deg = DegreeOnSinX((x1 -pos_judge)/border_radius_);
                end_deg = DegreeOnSinX((x2-pos_judge)/border_radius_)-start_deg;
                start_deg = 90.-start_deg;
                painterpath.arcTo(r-arc_width, t, arc_width, arc_width, start_deg, -end_deg);
            }
            if(DoubleLess(t + border_radius_, y2))//y2 > t + border_radius_
            {
                painterpath.lineTo(x2, y2);//终点在弧形外需要多画一条线
            }
        }
        else if(x1>x2&&
                y1<y2)//右下角
        {
            pos_judge = b - border_radius_;
            painterpath.moveTo(pt1);
            if(DoubleLessEqual(y1, pos_judge))
            {
                painterpath.lineTo(x1, pos_judge);
                if(DoubleLess(pos_judge, y2))
                {
                    end_deg = DegreeOnSinX((y2-pos_judge)/border_radius_);
                    painterpath.arcTo(r-arc_width, b- arc_width, arc_width, arc_width, 0., -end_deg);
                }
            }
            else
            {
                start_deg = DegreeOnSinX((y1 - pos_judge)/border_radius_);
                end_deg = DegreeOnSinX((y2 - pos_judge)/border_radius_) - start_deg;
                start_deg = 0. - start_deg;
                painterpath.arcTo(r -arc_width, b -arc_width, arc_width, arc_width, start_deg, -end_deg);
            }
            if(DoubleLess(x2, r -border_radius_))painterpath.lineTo(x2,y2);
        }
        else if(x1>x2 &&
                y1>y2)//左下角
        {
            pos_judge = l + border_radius_;
            painterpath.moveTo(pt1);
            if(DoubleLessEqual(pos_judge, y1))
            {
                painterpath.lineTo(pos_judge, y1);
                if(DoubleLessEqual(x2, pos_judge))
                {
                    end_deg = DegreeOnSinX((pos_judge-x2)/border_radius_);
                    painterpath.arcTo(l, b - arc_width, arc_width, arc_width, 270., -end_deg);
                }
            }
            else
            {
                start_deg = DegreeOnSinX((pos_judge - x1)/border_radius_);
                end_deg = DegreeOnSinX((pos_judge - x2)/border_radius_) - start_deg;
                start_deg = 270.-start_deg;
                painterpath.arcTo(l, b - arc_width, arc_width, arc_width,start_deg, -end_deg);
            }
            if(DoubleLess(y2, b - border_radius_))painterpath.lineTo(pt2);
        }
        else if(x1<x2&&
                y1>y2)//左上角
        {
            pos_judge = t + border_radius_;
            painterpath.moveTo(pt1);
            if(DoubleLessEqual(pos_judge, y1))
            {
                painterpath.lineTo(x1, pos_judge);
                if(DoubleLess(y2, pos_judge))
                {
                    end_deg = DegreeOnSinX((pos_judge - y2)/border_radius_);
                    painterpath.arcTo(l, t, arc_width, arc_width, 180., -end_deg);
                }
            }
            else
            {
                start_deg = DegreeOnSinX((pos_judge-y1)/border_radius_);
                end_deg = DegreeOnSinX((pos_judge - y2)/border_radius_)-start_deg;
                start_deg = 180.-start_deg;
                painterpath.arcTo(l, t, arc_width, arc_width, start_deg, -end_deg);
            }
            if(DoubleLess( l+border_radius_,x2))painterpath.lineTo(pt2);
        }
    }
    else
    {
        painterpath.moveTo(pt1);
        painterpath.lineTo(pt2);
    }
    return painterpath;
}

//边界(直角)
QPainterPath BorderEffectRectPath(QRectF srcrect,
                                  qreal start_percent,
                                  qreal stop_percent)
{
    QPainterPath srcpath;
    srcpath.addRect(srcrect);
    QPointF pt1 = srcpath.pointAtPercent(start_percent), pt2 = srcpath.pointAtPercent(stop_percent);
    QPainterPath painterpath;
    qreal x1 = pt1.x(), y1= pt1.y(), x2 = pt2.x(), y2 = pt2.y();
    painterpath.moveTo(pt1);
    if(!DoubleIsZERO(x1-x2)&&
       !DoubleIsZERO(y1-y2))
    {
        if(x1<x2&&
           y1<y2)//右上角
        {
            painterpath.lineTo(x2, y1);
        }
        else if(x1>x2&&
                y1<y2)//右下角
        {
            painterpath.lineTo(x1,y2);
        }
        else if(x1>x2&&
                y1>y2)//左下角
        {
            painterpath.lineTo(x2,y1);
        }
        else if(x1<x2&&
                y1>y2)
        {
            painterpath.lineTo(x1,y2);
        }
    }
    painterpath.lineTo(pt2);
    return painterpath;
}
BorderWidget::BorderWidget(QWidget*parent):QWidget(parent),
    wdg_(nullptr),
    border_len_(0.),
    animation_(nullptr),
    border_effect_percent_(0.08),
    border_color_(QColor(0,255,0)),
    border_effect_color_(QColor(255,0,0)),
    border_width_(4),
    border_round_radius_(4),
    effect_work_(true)
{
    animation_ = new QPropertyAnimation(this);
}

void BorderWidget::SetEffectWork(bool effectwork)
{
    effect_work_ = effectwork;
    if(wdg_&&
       animation_)
    {
        if(effect_work_)
        {
            if(animation_->state()==QAbstractAnimation::Stopped)animation_->start();
        }
        else if(animation_->state()!=QAbstractAnimation::Stopped)animation_->stop();
    }
    update();
}

void BorderWidget::SetWidget(QWidget *x)
{
    wdg_=x;
    setVisible(wdg_);
    QRect wdgrect = wdg_->geometry();
    if(wdg_)
    {
        wdgrect.adjust(-border_width_,-border_width_,border_width_,border_width_);
        setGeometry(wdgrect);
        wdgrect.moveTo(0,0);
        QRect drawrect = DrawRectOnGeometry(wdgrect);
        QPainterPath paintpath;
        if(border_round_radius_>0)
        {
            paintpath = RoundPathOnRect(drawrect, border_round_radius_);
        }
        else
        {
            paintpath.addRect(drawrect);
        }
        stackUnder(wdg_);
    }
    if(animation_)
    {
        if(animation_->state()!=QAbstractAnimation::Stopped)animation_->stop();
        if(wdg_)
        {
            animation_->setTargetObject(this);
            animation_->setPropertyName("border_len");
            animation_->setEasingCurve(QEasingCurve::Linear);
            animation_->setStartValue(0.);
            QRect effectrect = EffectRectOnGeometry(wdgrect, border_width_);
            QPainterPath effectpath;
            if(border_round_radius_>0)
            {
                effectpath = RoundPathOnRect(effectrect, border_round_radius_);
            }
            else
            {
                effectpath.addRect(effectrect);
            }
            animation_->setEndValue(effectpath.length());
            animation_->setDuration(4000);
            animation_->setLoopCount(-1);
            animation_->start();
        }
    }
}

void BorderWidget::paintEvent(QPaintEvent *event)
{
    (void)event;//
    if(wdg_&&animation_)
    {
        QRect geo = geometry();
        geo.moveTo(0,0);
        QPen pen(Qt::SolidLine);
        pen.setCapStyle(Qt::RoundCap);//这种风格圆角看起来更丝滑
        pen.setWidth(1);
        pen.setColor(border_color_);
        QBrush qbr(border_color_);
        QPainter pt(this);
        pt.setPen(pen);
        pt.setBrush(qbr);
        QRect drawrect = DrawRectOnGeometry(geo);
        QPainterPath p;
        if(border_round_radius_>0)
        {
            p = RoundPathOnRect(drawrect, border_round_radius_);
        }
        else
        {
            p.addRect(drawrect);
        }
        pt.drawPath(p);

        if(!effect_work_)return;
        qreal lnlen = animation_->endValue().toDouble();
        qreal ln1 = border_len_/lnlen, ln2 = ln1 + border_effect_percent_;
        if(ln2 >= 1.)ln2 -= 1.;
        if(ln1 >= 1.)
        {
            ln1 -=1.;
            border_len_ = ln1;
        }
        QPainterPath painterpath;
        QRect effectrect = EffectRectOnGeometry(geo, border_width_);
        if(border_round_radius_>0)painterpath = BorderEffectRoundRectPath(effectrect, ln1, ln2, border_round_radius_);
        else painterpath = BorderEffectRectPath(effectrect, ln1, ln2);
        pen.setColor(border_effect_color_);
        pen.setWidth(border_width_ + 2);//暂不知为啥直接用border_width_不能完全挡住边框背景
//#define USE_GRADIENT
#ifdef USE_GRADIENT
        //渐变效果
        QLinearGradient lngt(drawrect.topLeft(), drawrect.bottomRight());
        lngt.setColorAt(0.,QColor(255,0,255));
        lngt.setColorAt(0.25, QColor(255,0,0));
        lngt.setColorAt(0.5, QColor(255,255,0));
        lngt.setColorAt(0.75,QColor(0,255,255));
        lngt.setColorAt(1.,QColor(0,0,255));
        pen.setBrush(QBrush(lngt));
#endif
        pt.setPen(pen);
        pt.setBrush(Qt::NoBrush);
        pt.drawPath(painterpath);
    }
}

void BorderWidget::SetBorderLength(double v)
{
    border_len_ = v;
    update();
}

 

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值